Skip to content

Commit a5949bf

Browse files
authored
Merge pull request #341 from Lucas-C/master
Setting the __traceback__ attribute in future.utils.raise_from - close #340
2 parents e949bae + b69658e commit a5949bf

File tree

3 files changed

+65
-2
lines changed

3 files changed

+65
-2
lines changed

TESTING.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,10 @@ The test suite can be run with:
44

55
$ tox
66

7-
which tests the module under a number of different python versions, where available.
7+
which tests the module under a number of different python versions, where available, or with:
8+
9+
$ py.test
10+
11+
To execute a single test:
12+
13+
$ 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
@@ -443,12 +443,14 @@ def raise_from(exc, cause):
443443
e.__suppress_context__ = False
444444
if isinstance(cause, type) and issubclass(cause, Exception):
445445
e.__cause__ = cause()
446+
e.__cause__.__traceback__ = sys.exc_info()[2]
446447
e.__suppress_context__ = True
447448
elif cause is None:
448449
e.__cause__ = None
449450
e.__suppress_context__ = True
450451
elif isinstance(cause, BaseException):
451452
e.__cause__ = cause
453+
e.__cause__.__traceback__ = sys.exc_info()[2]
452454
e.__suppress_context__ = True
453455
else:
454456
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)