Skip to content

Commit 66ca231

Browse files
committed
Setting the __traceback__ attribute in future.utils.raise_from - close #340
1 parent cce97e2 commit 66ca231

File tree

3 files changed

+66
-1
lines changed

3 files changed

+66
-1
lines changed

TESTING.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,11 @@ The test suite can be run either with:
77
which uses the unittest module's test discovery mechanism, or with:
88

99
$ py.test
10+
11+
To execute a single test:
12+
13+
$ python setup.py test -s tests.test_future.test_utils.TestCause.test_chained_exceptions_stacktrace
14+
15+
or with:
16+
17+
$ pytest -k test_chained_exceptions_stacktrace

src/future/utils/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,12 +441,14 @@ def raise_from(exc, cause):
441441
e.__suppress_context__ = False
442442
if isinstance(cause, type) and issubclass(cause, Exception):
443443
e.__cause__ = cause()
444+
e.__cause__.__traceback__ = sys.exc_info()[2]
444445
e.__suppress_context__ = True
445446
elif cause is None:
446447
e.__cause__ = None
447448
e.__suppress_context__ = True
448449
elif isinstance(cause, BaseException):
449450
e.__cause__ = cause
451+
e.__cause__.__traceback__ = sys.exc_info()[2]
450452
e.__suppress_context__ = True
451453
else:
452454
raise TypeError("exception causes must derive from BaseException")

tests/test_future/test_utils.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"""
55

66
from __future__ import absolute_import, unicode_literals, print_function
7-
import sys
7+
import sys, traceback
88
from future.builtins import *
99
from future.utils import (old_div, istext, isbytes, native, PY2, PY3,
1010
native_str, raise_, as_native_str, ensure_new_type,
@@ -314,6 +314,61 @@ def __init__(self):
314314
else:
315315
self.fail("No exception raised")
316316

317+
def test_single_exception_stacktrace(self):
318+
expected = '''Traceback (most recent call last):
319+
File "/opt/python-future/tests/test_future/test_utils.py", line 328, in test_single_exception_stacktrace
320+
raise CustomException('ERROR')
321+
'''
322+
if PY2:
323+
expected += 'CustomException: ERROR\n'
324+
else:
325+
expected += 'tests.test_future.test_utils.CustomException: ERROR\n'
326+
327+
try:
328+
raise CustomException('ERROR')
329+
except:
330+
self.assertEqual(expected, traceback.format_exc())
331+
else:
332+
self.fail('No exception raised')
333+
334+
if PY2:
335+
def test_chained_exceptions_stacktrace(self):
336+
expected = '''Traceback (most recent call last):
337+
File "/opt/python-future/tests/test_future/test_utils.py", line 354, in test_chained_exceptions_stacktrace
338+
raise_from(CustomException('ERROR'), val_err)
339+
File "/opt/python-future/src/future/utils/__init__.py", line 456, in raise_from
340+
raise e
341+
CustomException: ERROR
342+
343+
The above exception was the direct cause of the following exception:
344+
345+
File "/opt/python-future/tests/test_future/test_utils.py", line 352, in test_chained_exceptions_stacktrace
346+
raise ValueError('Wooops')
347+
ValueError: Wooops
348+
'''
349+
350+
try:
351+
try:
352+
raise ValueError('Wooops')
353+
except ValueError as val_err:
354+
raise_from(CustomException('ERROR'), val_err)
355+
except Exception as err:
356+
self.assertEqual(expected.splitlines(), traceback.format_exc().splitlines())
357+
else:
358+
self.fail('No exception raised')
359+
360+
361+
class CustomException(Exception):
362+
if PY2:
363+
def __str__(self):
364+
out = Exception.__str__(self)
365+
if hasattr(self, '__cause__') and self.__cause__ and hasattr(self.__cause__, '__traceback__') and self.__cause__.__traceback__:
366+
out += '\n\nThe above exception was the direct cause of the following exception:\n\n'
367+
out += ''.join(traceback.format_tb(self.__cause__.__traceback__) + ['{}: {}'.format(self.__cause__.__class__.__name__, self.__cause__)])
368+
return out
369+
else:
370+
pass
371+
317372

318373
if __name__ == '__main__':
319374
unittest.main()

0 commit comments

Comments
 (0)