summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/util
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2020-03-02 23:45:35 +0000
committerGerrit Code Review <gerrit@bbpush.zzzcomputing.com>2020-03-02 23:45:35 +0000
commitb5050beb73b2e50b122c36e7dcdc06abffd472f2 (patch)
tree6679019ff418d6c346d5bd4cdc4aab4a73d9303e /lib/sqlalchemy/util
parent2d052d43518a0f4d9751db7e699cfebd3724c1e5 (diff)
parent57dc36a01b2b334a996f73f6a78b3bfbe4d9f2ec (diff)
downloadsqlalchemy-b5050beb73b2e50b122c36e7dcdc06abffd472f2.tar.gz
Merge "Ensure all nested exception throws have a cause"
Diffstat (limited to 'lib/sqlalchemy/util')
-rw-r--r--lib/sqlalchemy/util/__init__.py1
-rw-r--r--lib/sqlalchemy/util/compat.py63
-rw-r--r--lib/sqlalchemy/util/langhelpers.py6
3 files changed, 55 insertions, 15 deletions
diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py
index b4610a1b0..819d18018 100644
--- a/lib/sqlalchemy/util/__init__.py
+++ b/lib/sqlalchemy/util/__init__.py
@@ -67,6 +67,7 @@ from .compat import py33 # noqa
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
diff --git a/lib/sqlalchemy/util/compat.py b/lib/sqlalchemy/util/compat.py
index 104e8e03d..31654b97c 100644
--- a/lib/sqlalchemy/util/compat.py
+++ b/lib/sqlalchemy/util/compat.py
@@ -147,13 +147,42 @@ if py3k:
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
@@ -257,13 +286,13 @@ else:
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
@@ -370,6 +399,8 @@ else:
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
@@ -377,6 +408,12 @@ def raise_from_cause(exception, exc_info=None):
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.
diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py
index 41a9698c7..09aa94bf2 100644
--- a/lib/sqlalchemy/util/langhelpers.py
+++ b/lib/sqlalchemy/util/langhelpers.py
@@ -65,7 +65,9 @@ class safe_reraise(object):
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
@@ -76,7 +78,7 @@ class safe_reraise(object):
"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):