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/ext | |
| 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/ext')
| -rw-r--r-- | lib/sqlalchemy/ext/associationproxy.py | 24 | ||||
| -rw-r--r-- | lib/sqlalchemy/ext/baked.py | 9 | ||||
| -rw-r--r-- | lib/sqlalchemy/ext/compiler.py | 23 | ||||
| -rw-r--r-- | lib/sqlalchemy/ext/declarative/clsregistry.py | 15 | ||||
| -rw-r--r-- | lib/sqlalchemy/ext/indexable.py | 15 |
5 files changed, 55 insertions, 31 deletions
diff --git a/lib/sqlalchemy/ext/associationproxy.py b/lib/sqlalchemy/ext/associationproxy.py index 41346fc4e..f00b642db 100644 --- a/lib/sqlalchemy/ext/associationproxy.py +++ b/lib/sqlalchemy/ext/associationproxy.py @@ -244,6 +244,10 @@ class AssociationProxy(interfaces.InspectionAttrInfo): try: inst = class_.__dict__[self.key + "_inst"] except KeyError: + inst = None + + # avoid exception context + if inst is None: owner = self._calc_owner(class_) if owner is not None: inst = AssociationProxyInstance.for_proxy(self, owner, obj) @@ -358,9 +362,12 @@ class AssociationProxyInstance(object): # this was never asserted before but this should be made clear. if not isinstance(prop, orm.RelationshipProperty): - raise NotImplementedError( - "association proxy to a non-relationship " - "intermediary is not supported" + util.raise_( + NotImplementedError( + "association proxy to a non-relationship " + "intermediary is not supported" + ), + replace_context=None, ) target_class = prop.mapper.class_ @@ -1323,10 +1330,13 @@ class _AssociationDict(_AssociationCollection): try: for k, v in seq_or_map: self[k] = v - except ValueError: - raise ValueError( - "dictionary update sequence " - "requires 2-element tuples" + except ValueError as err: + util.raise_( + ValueError( + "dictionary update sequence " + "requires 2-element tuples" + ), + replace_context=err, ) for key, value in kw: diff --git a/lib/sqlalchemy/ext/baked.py b/lib/sqlalchemy/ext/baked.py index cafe69093..cf67387e4 100644 --- a/lib/sqlalchemy/ext/baked.py +++ b/lib/sqlalchemy/ext/baked.py @@ -504,9 +504,12 @@ class Result(object): """ try: ret = self.one_or_none() - except orm_exc.MultipleResultsFound: - raise orm_exc.MultipleResultsFound( - "Multiple rows were found for one()" + except orm_exc.MultipleResultsFound as err: + util.raise_( + orm_exc.MultipleResultsFound( + "Multiple rows were found for one()" + ), + replace_context=err, ) else: if ret is None: diff --git a/lib/sqlalchemy/ext/compiler.py b/lib/sqlalchemy/ext/compiler.py index c27907cdc..b8b6f8dc0 100644 --- a/lib/sqlalchemy/ext/compiler.py +++ b/lib/sqlalchemy/ext/compiler.py @@ -398,6 +398,7 @@ Example usage:: """ from .. import exc +from .. import util from ..sql import sqltypes from ..sql import visitors @@ -422,10 +423,13 @@ def compiles(class_, *specs): def _wrap_existing_dispatch(element, compiler, **kw): try: return existing_dispatch(element, compiler, **kw) - except exc.UnsupportedCompilationError: - raise exc.CompileError( - "%s construct has no default " - "compilation handler." % type(element) + except exc.UnsupportedCompilationError as uce: + util.raise_( + exc.CompileError( + "%s construct has no default " + "compilation handler." % type(element) + ), + from_=uce, ) existing.specs["default"] = _wrap_existing_dispatch @@ -470,10 +474,13 @@ class _dispatcher(object): if not fn: try: fn = self.specs["default"] - except KeyError: - raise exc.CompileError( - "%s construct has no default " - "compilation handler." % type(element) + except KeyError as ke: + util.raise_( + exc.CompileError( + "%s construct has no default " + "compilation handler." % type(element) + ), + replace_context=ke, ) # if compilation includes add_to_result_map, collect add_to_result_map diff --git a/lib/sqlalchemy/ext/declarative/clsregistry.py b/lib/sqlalchemy/ext/declarative/clsregistry.py index 7ff30b807..93e643cf5 100644 --- a/lib/sqlalchemy/ext/declarative/clsregistry.py +++ b/lib/sqlalchemy/ext/declarative/clsregistry.py @@ -298,12 +298,15 @@ class _class_resolver(object): else: return x except NameError as n: - raise exc.InvalidRequestError( - "When initializing mapper %s, expression %r failed to " - "locate a name (%r). If this is a class name, consider " - "adding this relationship() to the %r class after " - "both dependent classes have been defined." - % (self.prop.parent, self.arg, n.args[0], self.cls) + util.raise_( + exc.InvalidRequestError( + "When initializing mapper %s, expression %r failed to " + "locate a name (%r). If this is a class name, consider " + "adding this relationship() to the %r class after " + "both dependent classes have been defined." + % (self.prop.parent, self.arg, n.args[0], self.cls) + ), + from_=n, ) diff --git a/lib/sqlalchemy/ext/indexable.py b/lib/sqlalchemy/ext/indexable.py index f2e0501bb..6eb7e1185 100644 --- a/lib/sqlalchemy/ext/indexable.py +++ b/lib/sqlalchemy/ext/indexable.py @@ -223,7 +223,8 @@ The above query will render:: """ # noqa from __future__ import absolute_import -from sqlalchemy import inspect +from .. import inspect +from .. import util from ..ext.hybrid import hybrid_property from ..orm.attributes import flag_modified @@ -301,9 +302,9 @@ class index_property(hybrid_property): # noqa self.datatype = dict self.onebased = onebased - def _fget_default(self): + def _fget_default(self, err=None): if self.default == self._NO_DEFAULT_ARGUMENT: - raise AttributeError(self.attr_name) + util.raise_(AttributeError(self.attr_name), replace_context=err) else: return self.default @@ -314,8 +315,8 @@ class index_property(hybrid_property): # noqa return self._fget_default() try: value = column_value[self.index] - except (KeyError, IndexError): - return self._fget_default() + except (KeyError, IndexError) as err: + return self._fget_default(err) else: return value @@ -337,8 +338,8 @@ class index_property(hybrid_property): # noqa raise AttributeError(self.attr_name) try: del column_value[self.index] - except KeyError: - raise AttributeError(self.attr_name) + except KeyError as err: + util.raise_(AttributeError(self.attr_name), replace_context=err) else: setattr(instance, attr_name, column_value) flag_modified(instance, attr_name) |
