diff --git a/boltons/tbutils.py b/boltons/tbutils.py index 2294685..d7c5589 100644 --- a/boltons/tbutils.py +++ b/boltons/tbutils.py @@ -682,6 +682,22 @@ class ParsedException(object): return ('%s(%r, %r, frames=%r)' % (cn, self.exc_type, self.exc_msg, self.frames)) + def to_string(self): + lines = [u'Traceback (most recent call last):'] + + for frame in self.frames: + lines.append(u' File "%s", line %s, in %s' % (frame['filepath'], + frame['lineno'], + frame['funcname'])) + source_line = frame.get('source_line') + if source_line: + lines.append(u' %s' % (source_line,)) + if self.exc_msg: + lines.append(u'%s: %s' % (self.exc_type, self.exc_msg)) + else: + lines.append(u'%s' % (self.exc_type,)) + return u'\n'.join(lines) + @classmethod def from_string(cls, tb_str): """Parse a traceback and exception from the text *tb_str*. This text diff --git a/tests/test_tbutils.py b/tests/test_tbutils.py index 46499f7..84f6725 100644 --- a/tests/test_tbutils.py +++ b/tests/test_tbutils.py @@ -9,30 +9,12 @@ except: from boltons.tbutils import (TracebackInfo, ExceptionInfo, - ParsedException, print_exception, fix_print_exception, ContextualCallpoint, ContextualExceptionInfo) -def test_parsed_exc(): - FAKE_TB_STR = u""" -Traceback (most recent call last): - File "example.py", line 2, in - plarp -NameError: name 'plarp' is not defined -""" - parsed_tb = ParsedException.from_string(FAKE_TB_STR) - print(parsed_tb) - assert parsed_tb.exc_type == 'NameError' - assert parsed_tb.exc_msg == "name 'plarp' is not defined" - assert parsed_tb.frames == [{'source_line': u'plarp', - 'filepath': u'example.py', - 'lineno': u'2', - 'funcname': u''}] - - def test_exception_info(): # test ExceptionInfo and TracebackInfo and hooks, via StringIOs diff --git a/tests/test_tbutils_parsed_exc.py b/tests/test_tbutils_parsed_exc.py new file mode 100644 index 0000000..b28727e --- /dev/null +++ b/tests/test_tbutils_parsed_exc.py @@ -0,0 +1,53 @@ + +from boltons.tbutils import ParsedException + + +def test_parsed_exc_basic(): + _tb_str = u"""\ +Traceback (most recent call last): + File "example.py", line 2, in + plarp +NameError: name 'plarp' is not defined""" + + parsed_tb = ParsedException.from_string(_tb_str) + print(parsed_tb) + assert parsed_tb.exc_type == 'NameError' + assert parsed_tb.exc_msg == "name 'plarp' is not defined" + assert parsed_tb.frames == [{'source_line': u'plarp', + 'filepath': u'example.py', + 'lineno': u'2', + 'funcname': u''}] + + assert parsed_tb.to_string() == _tb_str + + +def test_parsed_exc_nosrcline(): + """just making sure that everything can be parsed even if there is + a line without source and also if the exception has no message""" + + _tb_str = u"""\ +Traceback (most recent call last): + File "/home/mahmoud/virtualenvs/chert/bin/chert", line 9, in + load_entry_point('chert==0.2.1.dev0', 'console_scripts', 'chert')() + File "/home/mahmoud/projects/chert/chert/core.py", line 1281, in main + ch.process() + File "/home/mahmoud/projects/chert/chert/core.py", line 741, in process + self.load() + File "", line 2, in load + File "/home/mahmoud/projects/lithoxyl/lithoxyl/logger.py", line 291, in logged_func + return func_to_log(*a, **kw) + File "/home/mahmoud/projects/chert/chert/core.py", line 775, in load + raise RuntimeError +RuntimeError""" + + parsed_tb = ParsedException.from_string(_tb_str) + + assert parsed_tb.exc_type == 'RuntimeError' + assert parsed_tb.exc_msg == '' + + assert len(parsed_tb.frames) == 6 + assert parsed_tb.frames[3] == {'source_line': u'', + 'filepath': u'', + 'lineno': u'2', + 'funcname': u'load'} + assert parsed_tb.to_string() == _tb_str