Fixed #25761 -- Added __cause__.__traceback__ to reraised exceptions.
When Django reraises an exception, it sets the __cause__ attribute even in Python 2, mimicking Python's 3 behavior for "raise Foo from Bar". However, Python 3 also ensures that all exceptions have a __traceback__ attribute and thus the "traceback2" Python 2 module (backport of Python 3's "traceback" module) relies on the fact that whenever you have a __cause__ attribute, the recorded exception also has a __traceback__ attribute. This is breaking testtools which is using traceback2 (see https://github.com/testing-cabal/testtools/issues/162). This commit fixes this inconsistency by ensuring that Django sets the __traceback__ attribute on any exception stored in a __cause__ attribute of a reraised exception.
This commit is contained in:
parent
c6ea4ed5d2
commit
9f4e031bd3
|
@ -272,6 +272,8 @@ class MigrationLoader(object):
|
||||||
),
|
),
|
||||||
missing)
|
missing)
|
||||||
exc_value.__cause__ = exc
|
exc_value.__cause__ = exc
|
||||||
|
if not hasattr(exc, '__traceback__'):
|
||||||
|
exc.__traceback__ = sys.exc_info()[2]
|
||||||
six.reraise(NodeNotFoundError, exc_value, sys.exc_info()[2])
|
six.reraise(NodeNotFoundError, exc_value, sys.exc_info()[2])
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,8 @@ class DatabaseErrorWrapper(object):
|
||||||
if issubclass(exc_type, db_exc_type):
|
if issubclass(exc_type, db_exc_type):
|
||||||
dj_exc_value = dj_exc_type(*exc_value.args)
|
dj_exc_value = dj_exc_type(*exc_value.args)
|
||||||
dj_exc_value.__cause__ = exc_value
|
dj_exc_value.__cause__ = exc_value
|
||||||
|
if not hasattr(exc_value, '__traceback__'):
|
||||||
|
exc_value.__traceback__ = traceback
|
||||||
# Only set the 'errors_occurred' flag for errors that may make
|
# Only set the 'errors_occurred' flag for errors that may make
|
||||||
# the connection unusable.
|
# the connection unusable.
|
||||||
if dj_exc_type not in (DataError, IntegrityError):
|
if dj_exc_type not in (DataError, IntegrityError):
|
||||||
|
|
|
@ -146,6 +146,8 @@ class LocalTimezone(ReferenceLocalTimezone):
|
||||||
exc_value = exc_type(
|
exc_value = exc_type(
|
||||||
"Unsupported value: %r. You should install pytz." % dt)
|
"Unsupported value: %r. You should install pytz." % dt)
|
||||||
exc_value.__cause__ = exc
|
exc_value.__cause__ = exc
|
||||||
|
if not hasattr(exc, '__traceback__'):
|
||||||
|
exc.__traceback__ = sys.exc_info()[2]
|
||||||
six.reraise(exc_type, exc_value, sys.exc_info()[2])
|
six.reraise(exc_type, exc_value, sys.exc_info()[2])
|
||||||
|
|
||||||
utc = pytz.utc if pytz else UTC()
|
utc = pytz.utc if pytz else UTC()
|
||||||
|
|
|
@ -196,7 +196,13 @@ As per :pep:`3134`, a ``__cause__`` attribute is set with the original
|
||||||
(underlying) database exception, allowing access to any additional
|
(underlying) database exception, allowing access to any additional
|
||||||
information provided. (Note that this attribute is available under
|
information provided. (Note that this attribute is available under
|
||||||
both Python 2 and Python 3, although :pep:`3134` normally only applies
|
both Python 2 and Python 3, although :pep:`3134` normally only applies
|
||||||
to Python 3.)
|
to Python 3. To avoid unexpected differences with Python 3, Django will also
|
||||||
|
ensure that the exception made available via ``__cause__`` has a usable
|
||||||
|
``__traceback__`` attribute.)
|
||||||
|
|
||||||
|
.. versionchanged:: 1.10
|
||||||
|
|
||||||
|
The ``__traceback__`` attribute described above was added.
|
||||||
|
|
||||||
.. exception:: models.ProtectedError
|
.. exception:: models.ProtectedError
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue