diff options
| author | mike bayer <mike_mp@zzzcomputing.com> | 2021-12-07 14:52:55 +0000 |
|---|---|---|
| committer | Gerrit Code Review <gerrit@ci3.zzzcomputing.com> | 2021-12-07 14:52:55 +0000 |
| commit | 1411f0bc4898737e3a575933e30c85a84b0bfb02 (patch) | |
| tree | 7c63a4e97c6066c410c08e8ba8c38e0df6d3e6d8 /lib/sqlalchemy | |
| parent | 0a5f0400b456ed4035a479f62d175e8b9361c46a (diff) | |
| parent | a845da8b0fc5bb172e278c399a1de9a2e49d62af (diff) | |
| download | sqlalchemy-1411f0bc4898737e3a575933e30c85a84b0bfb02.tar.gz | |
Merge "contextmanager skips rollback if trans says to skip it" into main
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/engine/base.py | 7 | ||||
| -rw-r--r-- | lib/sqlalchemy/engine/util.py | 23 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/session.py | 3 |
3 files changed, 31 insertions, 2 deletions
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index fbd8fe7df..b11ffd871 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -2022,6 +2022,13 @@ class Transaction(TransactionalContext): def _transaction_is_closed(self): return not self._deactivated_from_connection + def _rollback_can_be_called(self): + # for RootTransaction / NestedTransaction, it's safe to call + # rollback() even if the transaction is deactive and no warnings + # will be emitted. tested in + # test_transaction.py -> test_no_rollback_in_deactive(?:_savepoint)? + return True + class RootTransaction(Transaction): """Represent the "root" transaction on a :class:`_engine.Connection`. diff --git a/lib/sqlalchemy/engine/util.py b/lib/sqlalchemy/engine/util.py index 732ae7fa1..e88b9ebf3 100644 --- a/lib/sqlalchemy/engine/util.py +++ b/lib/sqlalchemy/engine/util.py @@ -98,6 +98,23 @@ class TransactionalContext: def _transaction_is_closed(self): raise NotImplementedError() + def _rollback_can_be_called(self): + """indicates the object is in a state that is known to be acceptable + for rollback() to be called. + + This does not necessarily mean rollback() will succeed or not raise + an error, just that there is currently no state detected that indicates + rollback() would fail or emit warnings. + + It also does not mean that there's a transaction in progress, as + it is usually safe to call rollback() even if no transaction is + present. + + .. versionadded:: 1.4.28 + + """ + raise NotImplementedError() + def _get_subject(self): raise NotImplementedError() @@ -143,7 +160,8 @@ class TransactionalContext: self.commit() except: with util.safe_reraise(): - self.rollback() + if self._rollback_can_be_called(): + self.rollback() finally: if not out_of_band_exit: subject._trans_context_manager = self._outer_trans_ctx @@ -154,7 +172,8 @@ class TransactionalContext: if not self._transaction_is_closed(): self.close() else: - self.rollback() + if self._rollback_can_be_called(): + self.rollback() finally: if not out_of_band_exit: subject._trans_context_manager = self._outer_trans_ctx diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 5419a912e..f75f2ac9f 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -940,6 +940,9 @@ class SessionTransaction(TransactionalContext): def _transaction_is_closed(self): return self._state is CLOSED + def _rollback_can_be_called(self): + return self._state not in (COMMITTED, CLOSED) + class Session(_SessionClassMethods): """Manages persistence operations for ORM-mapped objects. |
