--- /dev/null
+.. change::
+ :tags: bug, general, py3k
+ :tickets: 4849
+
+ 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.
if (record == NULL) {
record = PyObject_CallMethod(self->parent, "_key_fallback",
- "O", key);
+ "OO", key, Py_None);
if (record == NULL)
return NULL;
key_fallback = 1; // boolean to indicate record is a new reference
).execute(st)
except exc.DBAPIError as e:
if self._extract_error_code(e.orig) == 1146:
- raise exc.NoSuchTableError(full_name)
+ util.raise_(exc.NoSuchTableError(full_name), replace_context=e)
else:
raise
row = self._compat_first(rp, charset=charset)
except exc.DBAPIError as e:
code = self._extract_error_code(e.orig)
if code == 1146:
- raise exc.NoSuchTableError(full_name)
+ util.raise_(
+ exc.NoSuchTableError(full_name), replace_context=e
+ )
elif code == 1356:
- raise exc.UnreflectableTableError(
- "Table or view named %s could not be "
- "reflected: %s" % (full_name, e)
+ util.raise_(
+ exc.UnreflectableTableError(
+ "Table or view named %s could not be "
+ "reflected: %s" % (full_name, e)
+ ),
+ replace_context=e,
)
else:
raise
def set_isolation_level(self, connection, level):
try:
level = self._isolation_lookup[level.replace("_", " ")]
- except KeyError:
- raise exc.ArgumentError(
- "Invalid value '%s' for isolation_level. "
- "Valid isolation levels for %s are %s"
- % (level, self.name, ", ".join(self._isolation_lookup))
+ except KeyError as err:
+ util.raise_(
+ exc.ArgumentError(
+ "Invalid value '%s' for isolation_level. "
+ "Valid isolation levels for %s are %s"
+ % (level, self.name, ", ".join(self._isolation_lookup))
+ ),
+ replace_context=err,
)
connection.set_isolation_level(level)
self.extract_map[extract.field],
self.process(extract.expr, **kw),
)
- except KeyError:
- raise exc.CompileError(
- "%s is not a valid extract argument." % extract.field
+ except KeyError as err:
+ util.raise_(
+ exc.CompileError(
+ "%s is not a valid extract argument." % extract.field
+ ),
+ replace_context=err,
)
def limit_clause(self, select, **kw):
def set_isolation_level(self, connection, level):
try:
isolation_level = self._isolation_lookup[level.replace("_", " ")]
- except KeyError:
- raise exc.ArgumentError(
- "Invalid value '%s' for isolation_level. "
- "Valid isolation levels for %s are %s"
- % (level, self.name, ", ".join(self._isolation_lookup))
+ except KeyError as err:
+ util.raise_(
+ exc.ArgumentError(
+ "Invalid value '%s' for isolation_level. "
+ "Valid isolation levels for %s are %s"
+ % (level, self.name, ", ".join(self._isolation_lookup))
+ ),
+ replace_context=err,
)
cursor = connection.cursor()
cursor.execute("PRAGMA read_uncommitted = %d" % isolation_level)
return self._execute_text(object_, multiparams, params)
try:
meth = object_._execute_on_connection
- except AttributeError:
- raise exc.ObjectNotExecutableError(object_)
+ except AttributeError as err:
+ util.raise_(
+ exc.ObjectNotExecutableError(object_), replace_context=err
+ )
else:
return meth(self, multiparams, params)
invalidate_pool_on_disconnect = not is_exit_exception
if self._reentrant_error:
- util.raise_from_cause(
+ util.raise_(
exc.DBAPIError.instance(
statement,
parameters,
if context is not None
else None,
),
- exc_info,
+ with_traceback=exc_info[2],
+ from_=e,
)
self._reentrant_error = True
try:
self._autorollback()
if newraise:
- util.raise_from_cause(newraise, exc_info)
+ util.raise_(newraise, with_traceback=exc_info[2], from_=e)
elif should_wrap:
- util.raise_from_cause(sqlalchemy_exception, exc_info)
+ util.raise_(
+ sqlalchemy_exception, with_traceback=exc_info[2], from_=e
+ )
else:
- util.reraise(*exc_info)
+ util.raise_(exc_info[1], with_traceback=exc_info[2])
finally:
del self._reentrant_error
) = ctx.is_disconnect
if newraise:
- util.raise_from_cause(newraise, exc_info)
+ util.raise_(newraise, with_traceback=exc_info[2], from_=e)
elif should_wrap:
- util.raise_from_cause(sqlalchemy_exception, exc_info)
+ util.raise_(
+ sqlalchemy_exception, with_traceback=exc_info[2], from_=e
+ )
else:
- util.reraise(*exc_info)
+ util.raise_(exc_info[1], with_traceback=exc_info[2])
def _run_ddl_visitor(self, visitorcallable, element, **kwargs):
"""run a DDL visitor.
e, dialect, self
)
else:
- util.reraise(*sys.exc_info())
+ util.raise_(
+ sys.exc_info()[1], with_traceback=sys.exc_info()[2]
+ )
def raw_connection(self, _connection=None):
"""Return a "raw" DBAPI connection from the connection pool.
def _has_key(self, key):
return key in self._keymap
- def _key_fallback(self, key):
+ def _key_fallback(self, key, err):
if isinstance(key, int):
- raise IndexError(key)
+ util.raise_(IndexError(key), replace_context=err)
else:
- raise KeyError(key)
+ util.raise_(KeyError(key), replace_context=err)
class SimpleResultMetaData(ResultMetaData):
) in self._colnames_from_description(context, cursor_description):
yield idx, colname, sqltypes.NULLTYPE, coltype, None, untranslated
- def _key_fallback(self, key, raiseerr=True):
+ def _key_fallback(self, key, err, raiseerr=True):
if raiseerr:
- raise exc.NoSuchColumnError(
- "Could not locate column in row for column '%s'"
- % util.string_or_unprintable(key)
+ util.raise_(
+ exc.NoSuchColumnError(
+ "Could not locate column in row for column '%s'"
+ % util.string_or_unprintable(key)
+ ),
+ replace_context=err,
)
else:
return None
def _getter(self, key, raiseerr=True):
try:
rec = self._keymap[key]
- except KeyError:
- rec = self._key_fallback(key, raiseerr)
+ except KeyError as ke:
+ rec = self._key_fallback(key, ke, raiseerr)
if rec is None:
return None
for key in keys:
try:
rec = self._keymap[key]
- except KeyError:
- rec = self._key_fallback(key, raiseerr)
+ except KeyError as ke:
+ rec = self._key_fallback(key, ke, raiseerr)
if rec is None:
return None
)
return True
else:
- return self._key_fallback(key, False) is not None
+ return self._key_fallback(key, None, False) is not None
- def _key_fallback(self, key, raiseerr=True):
+ def _key_fallback(self, key, err, raiseerr=True):
map_ = self._keymap
result = None
)
if result is None:
if raiseerr:
- raise exc.NoSuchColumnError(
- "Could not locate column in row for column '%s'"
- % util.string_or_unprintable(key)
+ util.raise_(
+ exc.NoSuchColumnError(
+ "Could not locate column in row for column '%s'"
+ % util.string_or_unprintable(key)
+ ),
+ replace_context=err,
)
else:
return None
if key in self._keymap:
return True
else:
- return self._key_fallback(key, False) is not None
+ return self._key_fallback(key, None, False) is not None
class CursorFetchStrategy(object):
def fetchall(self):
return self._non_result([])
- def _non_result(self, default):
+ def _non_result(self, default, err=None):
if self.closed:
- raise exc.ResourceClosedError("This result object is closed.")
+ util.raise_(
+ exc.ResourceClosedError("This result object is closed."),
+ replace_context=err,
+ )
else:
return default
def fetchall(self):
return self._non_result([])
- def _non_result(self, default):
- raise exc.ResourceClosedError(
- "This result object does not return rows. "
- "It has been closed automatically."
+ def _non_result(self, default, err=None):
+ util.raise_(
+ exc.ResourceClosedError(
+ "This result object does not return rows. "
+ "It has been closed automatically."
+ ),
+ replace_context=err,
)
def _getter(self, key, raiseerr=True):
try:
getter = self._metadata._getter
- except AttributeError:
- return self.cursor_strategy._non_result(None)
+ except AttributeError as err:
+ return self.cursor_strategy._non_result(None, err)
else:
return getter(key, raiseerr)
def _tuple_getter(self, key, raiseerr=True):
try:
getter = self._metadata._tuple_getter
- except AttributeError:
- return self.cursor_strategy._non_result(None)
+ except AttributeError as err:
+ return self.cursor_strategy._non_result(None, err)
else:
return getter(key, raiseerr)
def _has_key(self, key):
try:
has_key = self._metadata._has_key
- except AttributeError:
- return self.cursor_strategy._non_result(None)
+ except AttributeError as err:
+ return self.cursor_strategy._non_result(None, err)
else:
return has_key(key)
def _subscript_impl(self, key, ismapping):
try:
rec = self._keymap[key]
- except KeyError:
- rec = self._parent._key_fallback(key)
+ except KeyError as ke:
+ rec = self._parent._key_fallback(key, ke)
except TypeError:
# the non-C version detects a slice using TypeError.
# this is pretty inefficient for the slice use case
try:
return self._get_by_key_impl_mapping(name)
except KeyError as e:
- raise AttributeError(e.args[0])
+ util.raise_(AttributeError(e.args[0]), replace_context=e)
class Row(BaseRow, collections_abc.Sequence):
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)
# 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_
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:
"""
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:
"""
from .. import exc
+from .. import util
from ..sql import sqltypes
from ..sql import visitors
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
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
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,
)
""" # 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
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
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
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)
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):
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"
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)
try:
return getattr(entity, key)
- except AttributeError:
- raise sa_exc.InvalidRequestError(
- "Entity '%s' has no property '%s'" % (description, key)
+ except AttributeError as err:
+ util.raise_(
+ sa_exc.InvalidRequestError(
+ "Entity '%s' has no property '%s'" % (description, key)
+ ),
+ replace_context=err,
)
try:
return self._strategies[key]
except KeyError:
- cls = self._strategy_lookup(self, *key)
- # this previously was setting self._strategies[cls], that's
- # a bad idea; should use strategy key at all times because every
- # strategy has multiple keys at this point
- self._strategies[key] = strategy = cls(self, key)
- return strategy
+ pass
+
+ # run outside to prevent transfer of exception context
+ cls = self._strategy_lookup(self, *key)
+ # this previously was setting self._strategies[cls], that's
+ # a bad idea; should use strategy key at all times because every
+ # strategy has multiple keys at this point
+ self._strategies[key] = strategy = cls(self, key)
+ return strategy
def setup(self, context, query_entity, path, adapter, **kwargs):
loader = self._get_context_loader(context, path)
if not query._yield_per:
break
- except Exception as err:
- cursor.close()
- util.raise_from_cause(err)
+ except Exception:
+ with util.safe_reraise():
+ cursor.close()
@util.dependencies("sqlalchemy.orm.query")
# it to mapped ColumnProperty
try:
self.polymorphic_on = self._props[self.polymorphic_on]
- except KeyError:
- raise sa_exc.ArgumentError(
- "Can't determine polymorphic_on "
- "value '%s' - no attribute is "
- "mapped to this name." % self.polymorphic_on
+ except KeyError as err:
+ util.raise_(
+ sa_exc.ArgumentError(
+ "Can't determine polymorphic_on "
+ "value '%s' - no attribute is "
+ "mapped to this name." % self.polymorphic_on
+ ),
+ replace_context=err,
)
if self.polymorphic_on in self._columntoproperty:
try:
return self._props[key]
- except KeyError:
- raise sa_exc.InvalidRequestError(
- "Mapper '%s' has no property '%s'" % (self, key)
+ except KeyError as err:
+ util.raise_(
+ sa_exc.InvalidRequestError(
+ "Mapper '%s' has no property '%s'" % (self, key)
+ ),
+ replace_context=err,
)
def get_property_by_column(self, column):
persistent, key=mapper._persistent_sortkey_fn
)
except TypeError as err:
- raise sa_exc.InvalidRequestError(
- "Could not sort objects by primary key; primary key "
- "values must be sortable in Python (was: %s)" % err
+ util.raise_(
+ sa_exc.InvalidRequestError(
+ "Could not sort objects by primary key; primary key "
+ "values must be sortable in Python (was: %s)" % err
+ ),
+ replace_context=err,
)
return (
sorted(pending, key=operator.attrgetter("insert_order"))
def _factory(cls, lookup, synchronize_session, *arg):
try:
klass = lookup[synchronize_session]
- except KeyError:
- raise sa_exc.ArgumentError(
- "Valid strategies for session synchronization "
- "are %s" % (", ".join(sorted(repr(x) for x in lookup)))
+ except KeyError as err:
+ util.raise_(
+ sa_exc.ArgumentError(
+ "Valid strategies for session synchronization "
+ "are %s" % (", ".join(sorted(repr(x) for x in lookup)))
+ ),
+ replace_context=err,
)
else:
return klass(*arg)
self._additional_evaluators(evaluator_compiler)
except evaluator.UnevaluatableError as err:
- raise sa_exc.InvalidRequestError(
- 'Could not evaluate current criteria in Python: "%s". '
- "Specify 'fetch' or False for the "
- "synchronize_session parameter." % err
+ util.raise_(
+ sa_exc.InvalidRequestError(
+ 'Could not evaluate current criteria in Python: "%s". '
+ "Specify 'fetch' or False for the "
+ "synchronize_session parameter." % err
+ ),
+ from_=err,
)
# TODO: detect when the where clause is a trivial primary key match
for prop in mapper._identity_key_props
)
- except KeyError:
- raise sa_exc.InvalidRequestError(
- "Incorrect names of values in identifier to formulate "
- "primary key for query.get(); primary key attribute names"
- " are %s"
- % ",".join(
- "'%s'" % prop.key
- for prop in mapper._identity_key_props
- )
+ except KeyError as err:
+ util.raise_(
+ sa_exc.InvalidRequestError(
+ "Incorrect names of values in identifier to formulate "
+ "primary key for query.get(); primary key attribute "
+ "names are %s"
+ % ",".join(
+ "'%s'" % prop.key
+ for prop in mapper._identity_key_props
+ )
+ ),
+ replace_context=err,
)
if (
"""
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:
a_subset=self.parent_local_selectable,
consider_as_foreign_keys=consider_as_foreign_keys,
)
- except sa_exc.NoForeignKeysError:
+ except sa_exc.NoForeignKeysError as nfe:
if self.secondary is not None:
- raise sa_exc.NoForeignKeysError(
- "Could not determine join "
- "condition between parent/child tables on "
- "relationship %s - there are no foreign keys "
- "linking these tables via secondary table '%s'. "
- "Ensure that referencing columns are associated "
- "with a ForeignKey or ForeignKeyConstraint, or "
- "specify 'primaryjoin' and 'secondaryjoin' "
- "expressions." % (self.prop, self.secondary)
+ util.raise_(
+ sa_exc.NoForeignKeysError(
+ "Could not determine join "
+ "condition between parent/child tables on "
+ "relationship %s - there are no foreign keys "
+ "linking these tables via secondary table '%s'. "
+ "Ensure that referencing columns are associated "
+ "with a ForeignKey or ForeignKeyConstraint, or "
+ "specify 'primaryjoin' and 'secondaryjoin' "
+ "expressions." % (self.prop, self.secondary)
+ ),
+ from_=nfe,
)
else:
- raise sa_exc.NoForeignKeysError(
- "Could not determine join "
- "condition between parent/child tables on "
- "relationship %s - there are no foreign keys "
- "linking these tables. "
- "Ensure that referencing columns are associated "
- "with a ForeignKey or ForeignKeyConstraint, or "
- "specify a 'primaryjoin' expression." % self.prop
+ util.raise_(
+ sa_exc.NoForeignKeysError(
+ "Could not determine join "
+ "condition between parent/child tables on "
+ "relationship %s - there are no foreign keys "
+ "linking these tables. "
+ "Ensure that referencing columns are associated "
+ "with a ForeignKey or ForeignKeyConstraint, or "
+ "specify a 'primaryjoin' expression." % self.prop
+ ),
+ from_=nfe,
)
- except sa_exc.AmbiguousForeignKeysError:
+ except sa_exc.AmbiguousForeignKeysError as afe:
if self.secondary is not None:
- raise sa_exc.AmbiguousForeignKeysError(
- "Could not determine join "
- "condition between parent/child tables on "
- "relationship %s - there are multiple foreign key "
- "paths linking the tables via secondary table '%s'. "
- "Specify the 'foreign_keys' "
- "argument, providing a list of those columns which "
- "should be counted as containing a foreign key "
- "reference from the secondary table to each of the "
- "parent and child tables." % (self.prop, self.secondary)
+ util.raise_(
+ sa_exc.AmbiguousForeignKeysError(
+ "Could not determine join "
+ "condition between parent/child tables on "
+ "relationship %s - there are multiple foreign key "
+ "paths linking the tables via secondary table '%s'. "
+ "Specify the 'foreign_keys' "
+ "argument, providing a list of those columns which "
+ "should be counted as containing a foreign key "
+ "reference from the secondary table to each of the "
+ "parent and child tables."
+ % (self.prop, self.secondary)
+ ),
+ from_=afe,
)
else:
- raise sa_exc.AmbiguousForeignKeysError(
- "Could not determine join "
- "condition between parent/child tables on "
- "relationship %s - there are multiple foreign key "
- "paths linking the tables. Specify the "
- "'foreign_keys' argument, providing a list of those "
- "columns which should be counted as containing a "
- "foreign key reference to the parent table." % self.prop
+ util.raise_(
+ sa_exc.AmbiguousForeignKeysError(
+ "Could not determine join "
+ "condition between parent/child tables on "
+ "relationship %s - there are multiple foreign key "
+ "paths linking the tables. Specify the "
+ "'foreign_keys' argument, providing a list of those "
+ "columns which should be counted as containing a "
+ "foreign key reference to the parent table."
+ % self.prop
+ ),
+ from_=afe,
)
@property
self._parent._rollback_exception = sys.exc_info()[1]
if rollback_err:
- util.reraise(*rollback_err)
+ util.raise_(rollback_err[1], with_traceback=rollback_err[2])
sess.dispatch.after_soft_rollback(sess, self)
def _add_bind(self, key, bind):
try:
insp = inspect(key)
- except sa_exc.NoInspectionAvailable:
+ except sa_exc.NoInspectionAvailable as err:
if not isinstance(key, type):
- raise sa_exc.ArgumentError(
- "Not an acceptable bind target: %s" % key
+ util.raise_(
+ sa_exc.ArgumentError(
+ "Not an acceptable bind target: %s" % key
+ ),
+ replace_context=err,
)
else:
self.__binds[key] = bind
if mapper is not None:
try:
mapper = inspect(mapper)
- except sa_exc.NoInspectionAvailable:
+ except sa_exc.NoInspectionAvailable as err:
if isinstance(mapper, type):
- raise exc.UnmappedClassError(mapper)
+ util.raise_(
+ exc.UnmappedClassError(mapper), replace_context=err,
+ )
else:
raise
"consider using a session.no_autoflush block if this "
"flush is occurring prematurely"
)
- util.raise_from_cause(e)
+ util.raise_(e, with_traceback=sys.exc_info[2])
def refresh(
self,
"""
try:
state = attributes.instance_state(instance)
- except exc.NO_STATE:
- raise exc.UnmappedInstanceError(instance)
+ except exc.NO_STATE as err:
+ util.raise_(
+ exc.UnmappedInstanceError(instance), replace_context=err,
+ )
self._expire_state(state, attribute_names)
"""
try:
state = attributes.instance_state(instance)
- except exc.NO_STATE:
- raise exc.UnmappedInstanceError(instance)
+ except exc.NO_STATE as err:
+ util.raise_(
+ exc.UnmappedInstanceError(instance), replace_context=err,
+ )
self._expire_state(state, attribute_names)
def _expire_state(self, state, attribute_names):
"""
try:
state = attributes.instance_state(instance)
- except exc.NO_STATE:
- raise exc.UnmappedInstanceError(instance)
+ except exc.NO_STATE as err:
+ util.raise_(
+ exc.UnmappedInstanceError(instance), replace_context=err,
+ )
if state.session_id is not self.hash_key:
raise sa_exc.InvalidRequestError(
"Instance %s is not present in this Session" % state_str(state)
try:
state = attributes.instance_state(instance)
- except exc.NO_STATE:
- raise exc.UnmappedInstanceError(instance)
+ except exc.NO_STATE as err:
+ util.raise_(
+ exc.UnmappedInstanceError(instance), replace_context=err,
+ )
self._save_or_update_state(state)
try:
state = attributes.instance_state(instance)
- except exc.NO_STATE:
- raise exc.UnmappedInstanceError(instance)
+ except exc.NO_STATE as err:
+ util.raise_(
+ exc.UnmappedInstanceError(instance), replace_context=err,
+ )
self._delete_impl(state, instance, head=True)
"""
try:
state = attributes.instance_state(instance)
- except exc.NO_STATE:
- raise exc.UnmappedInstanceError(instance)
+ except exc.NO_STATE as err:
+ util.raise_(
+ exc.UnmappedInstanceError(instance), replace_context=err,
+ )
return self._contains_state(state)
def __iter__(self):
for o in objects:
try:
state = attributes.instance_state(o)
- except exc.NO_STATE:
- raise exc.UnmappedInstanceError(o)
+
+ except exc.NO_STATE as err:
+ util.raise_(
+ exc.UnmappedInstanceError(o), replace_context=err,
+ )
objset.add(state)
else:
objset = None
try:
state = attributes.instance_state(instance)
- except exc.NO_STATE:
- raise exc.UnmappedInstanceError(instance)
+ except exc.NO_STATE as err:
+ util.raise_(
+ exc.UnmappedInstanceError(instance), replace_context=err,
+ )
else:
return _state_session(state)
# use getattr on the class to work around
# synonyms, hybrids, etc.
attr = getattr(ent.class_, attr)
- except AttributeError:
+ except AttributeError as err:
if raiseerr:
- raise sa_exc.ArgumentError(
- 'Can\'t find property named "%s" on '
- "%s in this Query." % (attr, ent)
+ util.raise_(
+ sa_exc.ArgumentError(
+ 'Can\'t find property named "%s" on '
+ "%s in this Query." % (attr, ent)
+ ),
+ replace_context=err,
)
else:
return None
from . import attributes
from . import exc
from . import util as orm_util
+from .. import util
def populate(
value = source.manager[prop.key].impl.get(
source, source_dict, attributes.PASSIVE_OFF
)
- except exc.UnmappedColumnError:
- _raise_col_to_prop(False, source_mapper, l, dest_mapper, r)
+ except exc.UnmappedColumnError as err:
+ _raise_col_to_prop(False, source_mapper, l, dest_mapper, r, err)
try:
# inline of dest_mapper._set_state_attr_by_column
prop = dest_mapper._columntoproperty[r]
dest.manager[prop.key].impl.set(dest, dest_dict, value, None)
- except exc.UnmappedColumnError:
- _raise_col_to_prop(True, source_mapper, l, dest_mapper, r)
+ except exc.UnmappedColumnError as err:
+ _raise_col_to_prop(True, source_mapper, l, dest_mapper, r, err)
# technically the "r.primary_key" check isn't
# needed here, but we check for this condition to limit
try:
prop = source_mapper._columntoproperty[l]
value = source_dict[prop.key]
- except exc.UnmappedColumnError:
- _raise_col_to_prop(False, source_mapper, l, source_mapper, r)
+ except exc.UnmappedColumnError as err:
+ _raise_col_to_prop(False, source_mapper, l, source_mapper, r, err)
try:
prop = source_mapper._columntoproperty[r]
)
try:
dest_mapper._set_state_attr_by_column(dest, dest.dict, r, None)
- except exc.UnmappedColumnError:
- _raise_col_to_prop(True, None, l, dest_mapper, r)
+ except exc.UnmappedColumnError as err:
+ _raise_col_to_prop(True, None, l, dest_mapper, r, err)
def update(source, source_mapper, dest, old_prefix, synchronize_pairs):
value = source_mapper._get_state_attr_by_column(
source, source.dict, l, passive=attributes.PASSIVE_OFF
)
- except exc.UnmappedColumnError:
- _raise_col_to_prop(False, source_mapper, l, None, r)
+ except exc.UnmappedColumnError as err:
+ _raise_col_to_prop(False, source_mapper, l, None, r, err)
dest[r.key] = value
dest[old_prefix + r.key] = oldvalue
value = source_mapper._get_state_attr_by_column(
source, source.dict, l, passive=attributes.PASSIVE_OFF
)
- except exc.UnmappedColumnError:
- _raise_col_to_prop(False, source_mapper, l, None, r)
+ except exc.UnmappedColumnError as err:
+ _raise_col_to_prop(False, source_mapper, l, None, r, err)
dict_[r.key] = value
for l, r in synchronize_pairs:
try:
prop = source_mapper._columntoproperty[l]
- except exc.UnmappedColumnError:
- _raise_col_to_prop(False, source_mapper, l, None, r)
+ except exc.UnmappedColumnError as err:
+ _raise_col_to_prop(False, source_mapper, l, None, r, err)
history = uowcommit.get_attribute_history(
source, prop.key, attributes.PASSIVE_NO_INITIALIZE
)
def _raise_col_to_prop(
- isdest, source_mapper, source_column, dest_mapper, dest_column
+ isdest, source_mapper, source_column, dest_mapper, dest_column, err
):
if isdest:
- raise exc.UnmappedColumnError(
- "Can't execute sync rule for "
- "destination column '%s'; mapper '%s' does not map "
- "this column. Try using an explicit `foreign_keys` "
- "collection which does not include this column (or use "
- "a viewonly=True relation)." % (dest_column, dest_mapper)
+ util.raise_(
+ exc.UnmappedColumnError(
+ "Can't execute sync rule for "
+ "destination column '%s'; mapper '%s' does not map "
+ "this column. Try using an explicit `foreign_keys` "
+ "collection which does not include this column (or use "
+ "a viewonly=True relation)." % (dest_column, dest_mapper)
+ ),
+ replace_context=err,
)
else:
- raise exc.UnmappedColumnError(
- "Can't execute sync rule for "
- "source column '%s'; mapper '%s' does not map this "
- "column. Try using an explicit `foreign_keys` "
- "collection which does not include destination column "
- "'%s' (or use a viewonly=True relation)."
- % (source_column, source_mapper, dest_column)
+ util.raise_(
+ exc.UnmappedColumnError(
+ "Can't execute sync rule for "
+ "source column '%s'; mapper '%s' does not map this "
+ "column. Try using an explicit `foreign_keys` "
+ "collection which does not include destination column "
+ "'%s' (or use a viewonly=True relation)."
+ % (source_column, source_mapper, dest_column)
+ ),
+ replace_context=err,
)
self.connection = connection
self.fresh = True
except Exception as e:
- pool.logger.debug("Error on connect(): %s", e)
- raise
+ with util.safe_reraise():
+ pool.logger.debug("Error on connect(): %s", e)
else:
if first_connect_check:
pool.dispatch.first_connect.for_modify(
else:
try:
m = rmatch(value)
- except TypeError:
- raise ValueError(
- "Couldn't parse %s string '%r' "
- "- value is not a string." % (type_.__name__, value)
+ except TypeError as err:
+ util.raise_(
+ ValueError(
+ "Couldn't parse %s string '%r' "
+ "- value is not a string." % (type_.__name__, value)
+ ),
+ from_=err,
)
if m is None:
raise ValueError(
def _key(self, key):
try:
dialect, value_key = key.split("_", 1)
- except ValueError:
- raise KeyError(key)
+ except ValueError as err:
+ util.raise_(KeyError(key), replace_context=err)
else:
return dialect, value_key
try:
opt = self.obj.dialect_options[dialect]
- except exc.NoSuchModuleError:
- raise KeyError(key)
+ except exc.NoSuchModuleError as err:
+ util.raise_(KeyError(key), replace_context=err)
else:
return opt[value_key]
def __setitem__(self, key, value):
try:
dialect, value_key = self._key(key)
- except KeyError:
- raise exc.ArgumentError(
- "Keys must be of the form <dialectname>_<argname>"
+ except KeyError as err:
+ util.raise_(
+ exc.ArgumentError(
+ "Keys must be of the form <dialectname>_<argname>"
+ ),
+ replace_context=err,
)
else:
self.obj.dialect_options[dialect][value_key] = value
def __getitem__(self, key):
try:
return self._index[key]
- except KeyError:
+ except KeyError as err:
if isinstance(key, util.int_types):
- raise IndexError(key)
+ util.raise_(IndexError(key), replace_context=err)
else:
raise
def __getattr__(self, key):
try:
return self._index[key]
- except KeyError:
- raise AttributeError(key)
+ except KeyError as err:
+ util.raise_(AttributeError(key), replace_context=err)
def __contains__(self, key):
if key not in self._index:
self._raise_for_expected(element, argname, resolved)
def _raise_for_expected(
- self, element, argname=None, resolved=None, advice=None, code=None
+ self,
+ element,
+ argname=None,
+ resolved=None,
+ advice=None,
+ code=None,
+ err=None,
):
if argname:
msg = "%s expected for argument %r; got %r." % (
if advice:
msg += " " + advice
- raise exc.ArgumentError(msg, code=code)
+ util.raise_(exc.ArgumentError(msg, code=code), replace_context=err)
class _Deannotate(object):
def _no_text_coercion(
- element, argname=None, exc_cls=exc.ArgumentError, extra=None
+ element, argname=None, exc_cls=exc.ArgumentError, extra=None, err=None
):
- raise exc_cls(
- "%(extra)sTextual SQL expression %(expr)r %(argname)sshould be "
- "explicitly declared as text(%(expr)r)"
- % {
- "expr": util.ellipses_string(element),
- "argname": "for argument %s" % (argname,) if argname else "",
- "extra": "%s " % extra if extra else "",
- }
+ util.raise_(
+ exc_cls(
+ "%(extra)sTextual SQL expression %(expr)r %(argname)sshould be "
+ "explicitly declared as text(%(expr)r)"
+ % {
+ "expr": util.ellipses_string(element),
+ "argname": "for argument %s" % (argname,) if argname else "",
+ "extra": "%s " % extra if extra else "",
+ }
+ ),
+ replace_context=err,
)
return elements.BindParameter(
name, element, type_, unique=True
)
- except exc.ArgumentError:
- self._raise_for_expected(element)
+ except exc.ArgumentError as err:
+ self._raise_for_expected(element, err=err)
class BinaryElementImpl(
):
try:
return expr._bind_param(operator, element, type_=bindparam_type)
- except exc.ArgumentError:
- self._raise_for_expected(element)
+ except exc.ArgumentError as err:
+ self._raise_for_expected(element, err=err)
def _post_coercion(self, resolved, expr, **kw):
if (
col = only_froms[element.element]
else:
col = with_cols[element.element]
- except KeyError:
+ except KeyError as err:
coercions._no_text_coercion(
element.element,
extra=(
"GROUP BY / DISTINCT etc."
),
exc_cls=exc.CompileError,
+ err=err,
)
else:
kwargs["render_label_as_label"] = col
else:
try:
opstring = OPERATORS[operator_]
- except KeyError:
- raise exc.UnsupportedCompilationError(self, operator_)
+ except KeyError as err:
+ util.raise_(
+ exc.UnsupportedCompilationError(self, operator_),
+ replace_context=err,
+ )
else:
return self._generate_generic_binary(
binary, opstring, from_linter=from_linter, **kw
if column.primary_key:
first_pk = True
except exc.CompileError as ce:
- util.raise_from_cause(
+ util.raise_(
exc.CompileError(
util.u("(in table '%s', column '%s'): %s")
% (table.description, column.name, ce.args[0])
- )
+ ),
+ from_=ce,
)
const = self.create_table_constraints(
)
collection = [(t, ()) for t in unsorted_tables]
else:
- util.raise_from_cause(
+ util.raise_(
exc.CircularDependencyError(
err2.args[0],
err2.cycles,
sorted([t.fullname for t in err2.cycles])
)
),
- )
+ ),
+ from_=err2,
)
seq_coll = [
def comparator(self):
try:
comparator_factory = self.type.comparator_factory
- except AttributeError:
- raise TypeError(
- "Object %r associated with '.type' attribute "
- "is not a TypeEngine class or object" % self.type
+ except AttributeError as err:
+ util.raise_(
+ TypeError(
+ "Object %r associated with '.type' attribute "
+ "is not a TypeEngine class or object" % self.type
+ ),
+ replace_context=err,
)
else:
return comparator_factory(self)
def __getattr__(self, key):
try:
return getattr(self.comparator, key)
- except AttributeError:
- raise AttributeError(
- "Neither %r object nor %r object has an attribute %r"
- % (type(self).__name__, type(self.comparator).__name__, key)
+ except AttributeError as err:
+ util.raise_(
+ AttributeError(
+ "Neither %r object nor %r object has an attribute %r"
+ % (
+ type(self).__name__,
+ type(self.comparator).__name__,
+ key,
+ )
+ ),
+ replace_context=err,
)
def operate(self, op, *other, **kwargs):
# a unique/anonymous key in any case, so use the _orig_key
# so that a text() construct can support unique parameters
existing = new_params[bind._orig_key]
- except KeyError:
- raise exc.ArgumentError(
- "This text() construct doesn't define a "
- "bound parameter named %r" % bind._orig_key
+ except KeyError as err:
+ util.raise_(
+ exc.ArgumentError(
+ "This text() construct doesn't define a "
+ "bound parameter named %r" % bind._orig_key
+ ),
+ replace_context=err,
)
else:
new_params[existing._orig_key] = bind
for key, value in names_to_values.items():
try:
existing = new_params[key]
- except KeyError:
- raise exc.ArgumentError(
- "This text() construct doesn't define a "
- "bound parameter named %r" % key
+ except KeyError as err:
+ util.raise_(
+ exc.ArgumentError(
+ "This text() construct doesn't define a "
+ "bound parameter named %r" % key
+ ),
+ replace_context=err,
)
else:
new_params[key] = existing._with_value(value)
else:
try:
lower = int(range_[0])
- except ValueError:
- raise exc.ArgumentError(
- "Integer or None expected for range value"
+ except ValueError as err:
+ util.raise_(
+ exc.ArgumentError(
+ "Integer or None expected for range value"
+ ),
+ replace_context=err,
)
else:
if lower == 0:
else:
try:
upper = int(range_[1])
- except ValueError:
- raise exc.ArgumentError(
- "Integer or None expected for range value"
+ except ValueError as err:
+ util.raise_(
+ exc.ArgumentError(
+ "Integer or None expected for range value"
+ ),
+ replace_context=err,
)
else:
if upper == 0:
if item is not None:
try:
spwd = item._set_parent_with_dispatch
- except AttributeError:
- util.raise_from_cause(
+ except AttributeError as err:
+ util.raise_(
exc.ArgumentError(
"'SchemaItem' object, such as a 'Column' or a "
"'Constraint' expected, got %r" % item
- )
+ ),
+ replace_context=err,
)
else:
spwd(self)
_proxies=[self],
*fk
)
- except TypeError:
- util.raise_from_cause(
+ except TypeError as err:
+ util.raise_(
TypeError(
"Could not create a copy of this %r object. "
"Ensure the class includes a _constructor() "
"attribute or method which accepts the "
"standard Column constructor arguments, or "
"references the Column class itself." % self.__class__
- )
+ ),
+ from_=err,
)
c.table = selectable
try:
ColumnCollectionConstraint._set_parent(self, table)
except KeyError as ke:
- raise exc.ArgumentError(
- "Can't create ForeignKeyConstraint "
- "on table '%s': no column "
- "named '%s' is present." % (table.description, ke.args[0])
+ util.raise_(
+ exc.ArgumentError(
+ "Can't create ForeignKeyConstraint "
+ "on table '%s': no column "
+ "named '%s' is present." % (table.description, ke.args[0])
+ ),
+ from_=ke,
)
for col, fk in zip(self.columns, self.elements):
return None
try:
value = clause._limit_offset_value
- except AttributeError:
- raise exc.CompileError(
- "This SELECT structure does not use a simple "
- "integer value for %s" % attrname
+ except AttributeError as err:
+ util.raise_(
+ exc.CompileError(
+ "This SELECT structure does not use a simple "
+ "integer value for %s" % attrname
+ ),
+ replace_context=err,
)
else:
return util.asint(value)
try:
cols_present = bool(columns)
- except TypeError:
- raise exc.ArgumentError(
- "columns argument to select() must "
- "be a Python list or other iterable"
+ except TypeError as err:
+ util.raise_(
+ exc.ArgumentError(
+ "columns argument to select() must "
+ "be a Python list or other iterable"
+ ),
+ from_=err,
)
if cols_present:
def _db_value_for_elem(self, elem):
try:
return self._valid_lookup[elem]
- except KeyError:
+ except KeyError as err:
# for unknown string values, we return as is. While we can
# validate these if we wanted, that does not allow for lesser-used
# end-user use cases, such as using a LIKE comparison with an enum,
):
return elem
else:
- raise LookupError(
- '"%s" is not among the defined enum values' % elem
+ util.raise_(
+ LookupError(
+ '"%s" is not among the defined enum values' % elem
+ ),
+ replace_context=err,
)
class Comparator(String.Comparator):
def _object_value_for_elem(self, elem):
try:
return self._object_lookup[elem]
- except KeyError:
- raise LookupError(
- '"%s" is not among the defined enum values' % elem
+ except KeyError as err:
+ util.raise_(
+ LookupError(
+ '"%s" is not among the defined enum values' % elem
+ ),
+ replace_context=err,
)
def __repr__(self):
try:
return dialect._type_memos[self]["literal"]
except KeyError:
- d = self._dialect_info(dialect)
- d["literal"] = lp = d["impl"].literal_processor(dialect)
- return lp
+ pass
+ # avoid KeyError context coming into literal_processor() function
+ # raises
+ d = self._dialect_info(dialect)
+ d["literal"] = lp = d["impl"].literal_processor(dialect)
+ return lp
def _cached_bind_processor(self, dialect):
"""Return a dialect-specific bind processor for this type."""
try:
return dialect._type_memos[self]["bind"]
except KeyError:
- d = self._dialect_info(dialect)
- d["bind"] = bp = d["impl"].bind_processor(dialect)
- return bp
+ pass
+ # avoid KeyError context coming into bind_processor() function
+ # raises
+ d = self._dialect_info(dialect)
+ d["bind"] = bp = d["impl"].bind_processor(dialect)
+ return bp
def _cached_result_processor(self, dialect, coltype):
"""Return a dialect-specific result processor for this type."""
try:
return dialect._type_memos[self][coltype]
except KeyError:
- d = self._dialect_info(dialect)
- # key assumption: DBAPI type codes are
- # constants. Else this dictionary would
- # grow unbounded.
- d[coltype] = rp = d["impl"].result_processor(dialect, coltype)
- return rp
+ pass
+ # avoid KeyError context coming into result_processor() function
+ # raises
+ d = self._dialect_info(dialect)
+ # key assumption: DBAPI type codes are
+ # constants. Else this dictionary would
+ # grow unbounded.
+ d[coltype] = rp = d["impl"].result_processor(dialect, coltype)
+ return rp
def _cached_custom_processor(self, dialect, key, fn):
try:
return dialect._type_memos[self][key]
except KeyError:
- d = self._dialect_info(dialect)
- impl = d["impl"]
- d[key] = result = fn(impl)
- return result
+ pass
+ # avoid KeyError context coming into fn() function
+ # raises
+ d = self._dialect_info(dialect)
+ impl = d["impl"]
+ d[key] = result = fn(impl)
+ return result
def _dialect_info(self, dialect):
"""Return a dialect-specific registry which
"def _compiler_dispatch(self, visitor, **kw):\n"
" try:\n"
" meth = visitor.visit_%(name)s\n"
- " except AttributeError:\n"
- " util.raise_from_cause(\n"
- " exc.UnsupportedCompilationError(visitor, cls))\n"
+ " except AttributeError as err:\n"
+ " util.raise_(\n"
+ " exc.UnsupportedCompilationError(visitor, cls), \n"
+ " replace_context=err)\n"
" else:\n"
" return meth(self, **kw)\n"
) % {"name": visit_name}
from . import config # noqa
from . import mock # noqa
from .assertions import assert_raises # noqa
+from .assertions import assert_raises_context_ok # noqa
from .assertions import assert_raises_message # noqa
+from .assertions import assert_raises_message_context_ok # noqa
from .assertions import assert_raises_return # noqa
from .assertions import AssertsCompiledSQL # noqa
from .assertions import AssertsExecutionResults # noqa
import contextlib
import re
+import sys
import warnings
from . import assertsql
assert a == b, msg or "%r != %r" % (a, b)
+def _assert_proper_exception_context(exception):
+ """assert that any exception we're catching does not have a __context__
+ without a __cause__, and that __suppress_context__ is never set.
+
+ Python 3 will report nested as exceptions as "during the handling of
+ error X, error Y occurred". That's not what we want to do. we want
+ these exceptions in a cause chain.
+
+ """
+
+ if not util.py3k:
+ return
+
+ if (
+ exception.__context__ is not exception.__cause__
+ and not exception.__suppress_context__
+ ):
+ assert False, (
+ "Exception %r was correctly raised but did not set a cause, "
+ "within context %r as its cause."
+ % (exception, exception.__context__)
+ )
+
+
def assert_raises(except_cls, callable_, *args, **kw):
- try:
- callable_(*args, **kw)
- success = False
- except except_cls:
- success = True
+ _assert_raises(except_cls, callable_, args, kw, check_context=True)
- # assert outside the block so it works for AssertionError too !
- assert success, "Callable did not raise an exception"
+
+def assert_raises_context_ok(except_cls, callable_, *args, **kw):
+ _assert_raises(
+ except_cls, callable_, args, kw,
+ )
def assert_raises_return(except_cls, callable_, *args, **kw):
+ return _assert_raises(except_cls, callable_, args, kw, check_context=True)
+
+
+def assert_raises_message(except_cls, msg, callable_, *args, **kwargs):
+ _assert_raises(
+ except_cls, callable_, args, kwargs, msg=msg, check_context=True
+ )
+
+
+def assert_raises_message_context_ok(
+ except_cls, msg, callable_, *args, **kwargs
+):
+ _assert_raises(except_cls, callable_, args, kwargs, msg=msg)
+
+
+def _assert_raises(
+ except_cls, callable_, args, kwargs, msg=None, check_context=False
+):
ret_err = None
+ if check_context:
+ are_we_already_in_a_traceback = sys.exc_info()[0]
try:
- callable_(*args, **kw)
+ callable_(*args, **kwargs)
success = False
except except_cls as err:
- success = True
ret_err = err
+ success = True
+ if msg is not None:
+ assert re.search(
+ msg, util.text_type(err), re.UNICODE
+ ), "%r !~ %s" % (msg, err,)
+ if check_context and not are_we_already_in_a_traceback:
+ _assert_proper_exception_context(err)
+ print(util.text_type(err).encode("utf-8"))
# assert outside the block so it works for AssertionError too !
assert success, "Callable did not raise an exception"
- return ret_err
-
-def assert_raises_message(except_cls, msg, callable_, *args, **kwargs):
- try:
- callable_(*args, **kwargs)
- assert False, "Callable did not raise an exception"
- except except_cls as e:
- assert re.search(msg, util.text_type(e), re.UNICODE), "%r !~ %s" % (
- msg,
- e,
- )
- print(util.text_type(e).encode("utf-8"))
+ return ret_err
class AssertsCompiledSQL(object):
import contextlib
import operator
import re
+import sys
from . import config
from .. import util
)
break
else:
- util.raise_from_cause(ex)
+ util.raise_(ex, with_traceback=sys.exc_info()[2])
def _expect_success(self, config, name="block"):
if not self.fails:
from .compat import py36 # noqa
from .compat import py3k # noqa
from .compat import quote_plus # noqa
+from .compat import raise_ # noqa
from .compat import raise_from_cause # noqa
from .compat import reduce # noqa
from .compat import reraise # noqa
def cmp(a, b):
return (a > b) - (a < b)
- def reraise(tp, value, tb=None, cause=None):
- if cause is not None:
- assert cause is not value, "Same cause emitted"
- value.__cause__ = cause
- if value.__traceback__ is not tb:
- raise value.with_traceback(tb)
- raise value
+ def raise_(
+ exception, with_traceback=None, replace_context=None, from_=False
+ ):
+ r"""implement "raise" with cause support.
+
+ :param exception: exception to raise
+ :param with_traceback: will call exception.with_traceback()
+ :param replace_context: an as-yet-unsupported feature. This is
+ an exception object which we are "replacing", e.g., it's our
+ "cause" but we don't want it printed. Basically just what
+ ``__suppress_context__`` does but we don't want to suppress
+ the enclosing context, if any. So for now we make it the
+ cause.
+ :param from\_: the cause. this actually sets the cause and doesn't
+ hope to hide it someday.
+
+ """
+ if with_traceback is not None:
+ exception = exception.with_traceback(with_traceback)
+
+ if from_ is not False:
+ exception.__cause__ = from_
+ elif replace_context is not None:
+ # no good solution here, we would like to have the exception
+ # have only the context of replace_context.__context__ so that the
+ # intermediary exception does not change, but we can't figure
+ # that out.
+ exception.__cause__ = replace_context
+
+ try:
+ raise exception
+ finally:
+ # credit to
+ # https://cosmicpercolator.com/2016/01/13/exception-leaks-in-python-2-and-3/
+ # as the __traceback__ object creates a cycle
+ del exception, replace_context, from_, with_traceback
def u(s):
return s
else:
return text
- # not as nice as that of Py3K, but at least preserves
- # the code line where the issue occurred
exec(
- "def reraise(tp, value, tb=None, cause=None):\n"
- " if cause is not None:\n"
- " assert cause is not value, 'Same cause emitted'\n"
- " raise tp, value, tb\n"
+ "def raise_(exception, with_traceback=None, replace_context=None, "
+ "from_=False):\n"
+ " if with_traceback:\n"
+ " raise type(exception), exception, with_traceback\n"
+ " else:\n"
+ " raise exception\n"
)
TYPE_CHECKING = False
def raise_from_cause(exception, exc_info=None):
+ r"""legacy. use raise\_()"""
+
if exc_info is None:
exc_info = sys.exc_info()
exc_type, exc_value, exc_tb = exc_info
reraise(type(exception), exception, tb=exc_tb, cause=cause)
+def reraise(tp, value, tb=None, cause=None):
+ r"""legacy. use raise\_()"""
+
+ raise_(value, with_traceback=tb, from_=cause)
+
+
def with_metaclass(meta, *bases):
"""Create a base class with a metaclass.
exc_type, exc_value, exc_tb = self._exc_info
self._exc_info = None # remove potential circular references
if not self.warn_only:
- compat.reraise(exc_type, exc_value, exc_tb)
+ compat.raise_(
+ exc_value, with_traceback=exc_tb,
+ )
else:
if not compat.py3k and self._exc_info and self._exc_info[1]:
# emulate Py3K's behavior of telling us when an exception
"is:\n %s %s\n" % (self._exc_info[0], self._exc_info[1])
)
self._exc_info = None # remove potential circular references
- compat.reraise(type_, value, traceback)
+ compat.raise_(value, with_traceback=traceback)
def string_or_unprintable(element):
go()
+ def test_raise_from(self):
+ @assert_cycles()
+ def go():
+ try:
+ try:
+ raise KeyError("foo")
+ except KeyError as ke:
+
+ util.raise_(Exception("oops"), from_=ke)
+ except Exception as err: # noqa
+ pass
+
+ go()
+
def test_query_alias(self):
User, Address = self.classes("User", "Address")
configure_mappers()
"some other column", Integer
)
- @profiling.function_call_count()
+ @profiling.function_call_count(variance=0.10)
def go():
c1 in row
import copy
import datetime
import inspect
-import sys
from sqlalchemy import exc
from sqlalchemy import sql
except MyException as err:
is_(err.__cause__, None)
- def test_reraise_disallow_same_cause(self):
+ def test_raise_from_cause_legacy(self):
class MyException(Exception):
pass
+ class MyOtherException(Exception):
+ pass
+
+ me = MyException("exc on")
+
def go():
try:
- raise MyException("exc one")
- except Exception as err:
- type_, value, tb = sys.exc_info()
- util.reraise(type_, err, tb, value)
+ raise me
+ except Exception:
+ util.raise_from_cause(MyOtherException("exc two"))
- assert_raises_message(AssertionError, "Same cause emitted", go)
+ try:
+ go()
+ assert False
+ except MyOtherException as moe:
+ if testing.requires.python3.enabled:
+ is_(moe.__cause__, me)
- def test_raise_from_cause(self):
+ def test_raise_from(self):
class MyException(Exception):
pass
def go():
try:
raise me
- except Exception:
- util.raise_from_cause(MyOtherException("exc two"))
+ except Exception as err:
+ util.raise_(MyOtherException("exc two"), from_=err)
try:
go()
return super(MockCursor, self).execute(stmt, params, **kw)
eng = engines.proxying_engine(cursor_cls=MockCursor)
- assert_raises_message(
- tsa.exc.SAWarning,
- "Exception attempting to detect unicode returns",
- eng.connect,
- )
- assert eng.dialect.returns_unicode_strings in (True, False)
+ with testing.expect_warnings(
+ "Exception attempting to detect unicode returns"
+ ):
+ eng.connect()
+
+ # because plain varchar passed, we don't know the correct answer
+ eq_(eng.dialect.returns_unicode_strings, "conditional")
eng.dispose()
def test_works_after_dispose(self):
from sqlalchemy import select
from sqlalchemy import testing
from sqlalchemy.testing import assert_raises
+from sqlalchemy.testing import assert_raises_context_ok
from sqlalchemy.testing import assert_raises_message
from sqlalchemy.testing import eq_
from sqlalchemy.testing import fixtures
eq_(p.checkedout(), 0)
eq_(p._overflow, 0)
dbapi.shutdown(True)
- assert_raises(Exception, p.connect)
+ assert_raises_context_ok(Exception, p.connect)
eq_(p._overflow, 0)
eq_(p.checkedout(), 0) # and not 1
from sqlalchemy.engine import url
from sqlalchemy.testing import assert_raises
from sqlalchemy.testing import assert_raises_message
+from sqlalchemy.testing import assert_raises_message_context_ok
from sqlalchemy.testing import engines
from sqlalchemy.testing import eq_
from sqlalchemy.testing import expect_warnings
self.dbapi.shutdown("execute", stop=True)
- assert_raises_message(
+ assert_raises_message_context_ok(
MockDisconnect, "database is stopped", pool.connect
)
def test_cursor_shutdown_in_initialize(self):
db = self._fixture(True, True)
- assert_raises_message(
+ assert_raises_message_context_ok(
exc.SAWarning, "Exception attempting to detect", db.connect
)
eq_(
from sqlalchemy.testing import expect_warnings
from sqlalchemy.testing import fixtures
from sqlalchemy.testing import in_
+from sqlalchemy.testing import is_
from sqlalchemy.testing import is_false
from sqlalchemy.testing import is_true
from sqlalchemy.testing import mock
testing.db.dialect.ischema_names = {}
try:
m2 = MetaData(testing.db)
- assert_raises(sa.exc.SAWarning, Table, "test", m2, autoload=True)
- @testing.emits_warning("Did not recognize type")
- def warns():
- m3 = MetaData(testing.db)
- t3 = Table("test", m3, autoload=True)
- assert t3.c.foo.type.__class__ == sa.types.NullType
+ with testing.expect_warnings("Did not recognize type"):
+ t3 = Table("test", m2, autoload_with=testing.db)
+ is_(t3.c.foo.type.__class__, sa.types.NullType)
finally:
testing.db.dialect.ischema_names = ischema_names
def test_unknown_dialect_warning(self):
with self._fixture():
- assert_raises_message(
- exc.SAWarning,
+ with testing.expect_warnings(
"Can't validate argument 'unknown_y'; can't locate "
"any SQLAlchemy dialect named 'unknown'",
- Index,
- "a",
- "b",
- "c",
- unknown_y=True,
- )
+ ):
+ Index("a", "b", "c", unknown_y=True)
def test_participating_bad_kw(self):
with self._fixture():