diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-02-29 14:40:45 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-03-02 17:24:19 -0500 |
commit | 57dc36a01b2b334a996f73f6a78b3bfbe4d9f2ec (patch) | |
tree | 77cbb0199ca91be3b0816e3a5bd4c217e36a7d1b /lib/sqlalchemy/orm/attributes.py | |
parent | 649de79950dcf952d7a44069faf36925c23c4e63 (diff) | |
download | sqlalchemy-57dc36a01b2b334a996f73f6a78b3bfbe4d9f2ec.tar.gz |
Ensure all nested exception throws have a cause
Applied an explicit "cause" to most if not all internally raised exceptions
that are raised from within an internal exception catch, to avoid
misleading stacktraces that suggest an error within the handling of an
exception. While it would be preferable to suppress the internally caught
exception in the way that the ``__suppress_context__`` attribute would,
there does not as yet seem to be a way to do this without suppressing an
enclosing user constructed context, so for now it exposes the internally
caught exception as the cause so that full information about the context
of the error is maintained.
Fixes: #4849
Change-Id: I55a86b29023675d9e5e49bc7edc5a2dc0bcd4751
Diffstat (limited to 'lib/sqlalchemy/orm/attributes.py')
-rw-r--r-- | lib/sqlalchemy/orm/attributes.py | 78 |
1 files changed, 46 insertions, 32 deletions
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 66a18da99..a959b0a40 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -231,16 +231,19 @@ class QueryableAttribute( def __getattr__(self, key): try: return getattr(self.comparator, key) - except AttributeError: - raise AttributeError( - "Neither %r object nor %r object associated with %s " - "has an attribute %r" - % ( - type(self).__name__, - type(self.comparator).__name__, - self, - key, - ) + except AttributeError as err: + util.raise_( + AttributeError( + "Neither %r object nor %r object associated with %s " + "has an attribute %r" + % ( + type(self).__name__, + type(self.comparator).__name__, + self, + key, + ) + ), + replace_context=err, ) def __str__(self): @@ -373,31 +376,39 @@ def create_proxied_attribute(descriptor): comparator.""" try: return getattr(descriptor, attribute) - except AttributeError: + except AttributeError as err: if attribute == "comparator": - raise AttributeError("comparator") + util.raise_( + AttributeError("comparator"), replace_context=err + ) try: # comparator itself might be unreachable comparator = self.comparator - except AttributeError: - raise AttributeError( - "Neither %r object nor unconfigured comparator " - "object associated with %s has an attribute %r" - % (type(descriptor).__name__, self, attribute) + except AttributeError as err2: + util.raise_( + AttributeError( + "Neither %r object nor unconfigured comparator " + "object associated with %s has an attribute %r" + % (type(descriptor).__name__, self, attribute) + ), + replace_context=err2, ) else: try: return getattr(comparator, attribute) - except AttributeError: - raise AttributeError( - "Neither %r object nor %r object " - "associated with %s has an attribute %r" - % ( - type(descriptor).__name__, - type(comparator).__name__, - self, - attribute, - ) + except AttributeError as err3: + util.raise_( + AttributeError( + "Neither %r object nor %r object " + "associated with %s has an attribute %r" + % ( + type(descriptor).__name__, + type(comparator).__name__, + self, + attribute, + ) + ), + replace_context=err3, ) Proxy.__name__ = type(descriptor).__name__ + "Proxy" @@ -713,12 +724,15 @@ class AttributeImpl(object): elif value is ATTR_WAS_SET: try: return dict_[key] - except KeyError: + except KeyError as err: # TODO: no test coverage here. - raise KeyError( - "Deferred loader for attribute " - "%r failed to populate " - "correctly" % key + util.raise_( + KeyError( + "Deferred loader for attribute " + "%r failed to populate " + "correctly" % key + ), + replace_context=err, ) elif value is not ATTR_EMPTY: return self.set_committed_value(state, dict_, value) |