Properly parse tracebacks with missing source. Resolves #30

This commit is contained in:
Anthony Sottile 2015-04-19 00:13:33 -07:00
parent c9b3d2452e
commit ae21ed2a78
5 changed files with 65 additions and 9 deletions

View File

@ -6,4 +6,4 @@ python:
- "pypy"
install: "pip install -r requirements-test.txt"
script: "py.test --doctest-modules boltons"
script: "py.test --doctest-modules boltons tests"

View File

@ -27,6 +27,12 @@ import sys
import linecache
if str is bytes: # py2
text = unicode
else: # py3
text = str
# TODO: chaining primitives? what are real use cases where these help?
# TODO: print_* for backwards compatability
@ -691,7 +697,7 @@ class ParsedException(object):
Args:
tb_str (str): The traceback text (:class:`unicode` or UTF-8 bytes)
"""
if not isinstance(tb_str, unicode):
if not isinstance(tb_str, text):
tb_str = tb_str.decode('utf-8')
tb_lines = tb_str.lstrip().splitlines()
@ -717,21 +723,27 @@ class ParsedException(object):
raise ValueError('unrecognized traceback string format')
frames = []
for pair_idx in range(start_line, len(tb_lines), 2):
frame_line = tb_lines[pair_idx].strip()
line_no = start_line
while True:
frame_line = tb_lines[line_no].strip()
frame_match = frame_re.match(frame_line)
if frame_match:
frame_dict = frame_match.groupdict()
next_line = tb_lines[line_no + 1].strip()
if frame_re.match(next_line):
frame_dict['source_line'] = ''
else:
frame_dict['source_line'] = next_line
line_no += 1
else:
break
frame_dict['source_line'] = tb_lines[pair_idx + 1].strip()
line_no += 1
frames.append(frame_dict)
exc_line_offset = start_line + len(frames) * 2
try:
exc_line = tb_lines[exc_line_offset]
exc_line = tb_lines[line_no]
exc_type, _, exc_msg = exc_line.partition(':')
except:
except Exception:
exc_type, exc_msg = '', ''
return cls(exc_type, exc_msg, frames)

0
tests/__init__.py Normal file
View File

44
tests/tbutils_test.py Normal file
View File

@ -0,0 +1,44 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from boltons.tbutils import ParsedException
def test_normal_tb():
tb = '''\
Traceback (most recent call last):
File "<string>", line 2, in _some_function
return some_other_function(1)
File "myfile.py", line 3, in some_other_function
return foo(bar, baz)
MyException: ExceptionValue
'''
parsed = ParsedException.from_string(tb)
assert parsed.exc_type == 'MyException'
assert parsed.exc_msg == ' ExceptionValue'
assert parsed.frames == [
{
'source_line': 'return some_other_function(1)',
'filepath': '<string>',
'lineno': '2',
'funcname': '_some_function'
},
{
'source_line': 'return foo(bar, baz)',
'filepath': 'myfile.py',
'lineno': '3',
'funcname': 'some_other_function',
}
]
def test_eval_tb():
tb = '''\
Traceback (most recent call last):
File "<string>", line 2, in _some_function
File "myfile.py", line 3, in some_other_function
return foo(bar, baz)
MyException: ExceptionValue
'''
parsed = ParsedException.from_string(tb)
assert parsed.exc_type == 'MyException'

View File

@ -2,4 +2,4 @@
envlist = py27,py34,pypy
[testenv]
deps = -rrequirements-test.txt
commands = py.test --doctest-modules boltons
commands = py.test --doctest-modules boltons tests