summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/util/compat.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/util/compat.py')
-rw-r--r--lib/sqlalchemy/util/compat.py63
1 files changed, 50 insertions, 13 deletions
diff --git a/lib/sqlalchemy/util/compat.py b/lib/sqlalchemy/util/compat.py
index 8967955cd..004b4687a 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
@@ -405,6 +434,8 @@ def nested(*managers):
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
@@ -412,6 +443,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.