diff options
| author | Raphaƫl Hertzog <hertzog@debian.org> | 2015-11-25 12:54:52 +0100 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2015-12-03 16:31:50 -0500 |
| commit | 9f4e031bd3cb13d5879fe9ad3d889ce861b0babe (patch) | |
| tree | 79be55cfa7c8e9fe004e29b2e12c2fb60283e322 | |
| parent | c6ea4ed5d23671deee6e736fe1daba2c650a4fbb (diff) | |
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.
| -rw-r--r-- | django/db/migrations/loader.py | 2 | ||||
| -rw-r--r-- | django/db/utils.py | 2 | ||||
| -rw-r--r-- | django/utils/timezone.py | 2 | ||||
| -rw-r--r-- | docs/ref/exceptions.txt | 8 |
4 files changed, 13 insertions, 1 deletions
diff --git a/django/db/migrations/loader.py b/django/db/migrations/loader.py index 310a4d01b4..7ef5e9d4d6 100644 --- a/django/db/migrations/loader.py +++ b/django/db/migrations/loader.py @@ -272,6 +272,8 @@ class MigrationLoader(object): ), missing) exc_value.__cause__ = exc + if not hasattr(exc, '__traceback__'): + exc.__traceback__ = sys.exc_info()[2] six.reraise(NodeNotFoundError, exc_value, sys.exc_info()[2]) raise exc diff --git a/django/db/utils.py b/django/db/utils.py index aaa992c040..92354b7bad 100644 --- a/django/db/utils.py +++ b/django/db/utils.py @@ -85,6 +85,8 @@ class DatabaseErrorWrapper(object): if issubclass(exc_type, db_exc_type): dj_exc_value = dj_exc_type(*exc_value.args) 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 # the connection unusable. if dj_exc_type not in (DataError, IntegrityError): diff --git a/django/utils/timezone.py b/django/utils/timezone.py index 70fd5e3e48..205cac44a9 100644 --- a/django/utils/timezone.py +++ b/django/utils/timezone.py @@ -146,6 +146,8 @@ class LocalTimezone(ReferenceLocalTimezone): exc_value = exc_type( "Unsupported value: %r. You should install pytz." % dt) 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]) utc = pytz.utc if pytz else UTC() diff --git a/docs/ref/exceptions.txt b/docs/ref/exceptions.txt index f847019c52..7e03c42286 100644 --- a/docs/ref/exceptions.txt +++ b/docs/ref/exceptions.txt @@ -196,7 +196,13 @@ As per :pep:`3134`, a ``__cause__`` attribute is set with the original (underlying) database exception, allowing access to any additional information provided. (Note that this attribute is available under 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 |
