diff --git a/.travis.yml b/.travis.yml index 9f97722..0df3b47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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" diff --git a/boltons/tbutils.py b/boltons/tbutils.py index 2ff476d..9b498fa 100644 --- a/boltons/tbutils.py +++ b/boltons/tbutils.py @@ -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) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/tbutils_test.py b/tests/tbutils_test.py new file mode 100644 index 0000000..28ce3b7 --- /dev/null +++ b/tests/tbutils_test.py @@ -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 "", 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': '', + '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 "", 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' diff --git a/tox.ini b/tox.ini index 928a1dc..e99092f 100644 --- a/tox.ini +++ b/tox.ini @@ -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