@@ -30,6 +30,17 @@ class _UnhandledCallbacks(Exception):
3030 callbacks : tuple [Callable [[], object ], ...]
3131
3232
33+ class _OnlyForUseInDjangoTestTransaction (Exception ):
34+ """
35+ Raised when `part_of_a_transaction` is used without a transaction created by Django tests.
36+
37+ This can also be raised in tests which handle their own transaction
38+ instead of allowing the testsuite to wrap the test in a transaction.
39+ (These kinds of tests are called "transaction testcases".)
40+ This prevents `part_of_a_transaction` from running after-commit callbacks.
41+ """
42+
43+
3344@contextlib .contextmanager
3445def part_of_a_transaction (using : str | None = None ) -> Generator [None ]:
3546 """
@@ -41,18 +52,25 @@ def part_of_a_transaction(using: str | None = None) -> Generator[None]:
4152 This works by entering a new "atomic" block, so that the inner-most "atomic"
4253 isn't the one created by the test-suite.
4354
44- In "transaction testcases" this will create a transaction, but if you're writing
45- a transaction testcase, you probably want to manage transactions more explicitly
46- than by calling this.
47-
4855 Note that this does not handle after-commit callback simulation. If you need that,
4956 use [`transaction`][django_subatomic.db.transaction] instead.
57+
58+ In production code and "transaction testcases" this will raise an error
59+ to ensure we don't misleadingly run after-commit callbacks.
5060 """
5161 connection = transaction .get_connection (using )
62+
5263 raise_unhandled_callbacks = getattr (
5364 settings , "SUBATOMIC_CATCH_UNHANDLED_AFTER_COMMIT_CALLBACKS_IN_TESTS" , True
5465 )
5566
67+ # We must be called from inside an atomic block created by the test suite
68+ # to avoid running after-commit callbacks on exit.
69+ # We don't check that the atomic block is from the test suite though,
70+ # because if it's created elsewhere we'll see an error from `durable=True` below.
71+ if len (connection .atomic_blocks ) == 0 :
72+ raise _OnlyForUseInDjangoTestTransaction
73+
5674 if raise_unhandled_callbacks :
5775 callbacks = connection .run_on_commit
5876 if callbacks :
0 commit comments