Fix CancelScope reuse not being detected on the asyncio backend - #1296
Fix CancelScope reuse not being detected on the asyncio backend#1296jaideeppyne wants to merge 1 commit into
Conversation
The guard in CancelScope.__enter__ tested _active, which __exit__ resets, so it only caught re-entering a scope that was still active. Entering a scope again after it had been exited was silently allowed, even though the error message promises that each scope may only be used for a single 'with' block, and even though the Trio backend raises RuntimeError. Because _cancel_called survives the first block, reusing a cancelled scope silently cancelled the body of the second 'with' block. Track entry with a separate _has_been_entered flag that is never reset, mirroring how TaskGroup already guards its own re-entry.
c202306 to
a58e99c
Compare
fallenmi
left a comment
There was a problem hiding this comment.
Verified exact head a58e99cc38bfafb3ed5d44506f578dd70223f80b and the identical current merge tree at 51585c78dce4fa83598156b79ca6ecc224ecc6c6.
The separate _has_been_entered flag correctly enforces one-shot use without overloading _active, which still tracks active-scope exit bookkeeping. This closes both ordinary post-exit reuse and the cancelled-scope path that silently cancelled the second block, and brings the asyncio backend in line with Trio. The two backend-parametrized regressions cover both paths, and the changelog entry is present.
All 18 exact-head check runs, the check suite and workflow, and both commit statuses are successful. I found no blocking issue. This was a source/API review; I did not rerun the suite locally.
Disclosure: this review was prepared with Codex assistance; I verified the exact refs, merge tree, source changes, policy, interactions, and live CI before submission.
Changes
CancelScope.__enter__on the asyncio backend guards against reuse withif self._active:, but__exit__sets_active = False. So the guard only catches re-entering a scope that is still active, and entering a scope again after it has exited slips through silently, even though the guard's own message promises that each scope may only be used for a singlewithblock. The Trio backend raisesRuntimeErrorhere._cancel_calledalso survives the first block, so reusing a cancelled scope silently cancels the body of the second block:I track entry with a separate
_has_been_enteredflag that is never reset, which is howTaskGroupalready guards its own re-entry in the same module. Both backends now raise the sameRuntimeError.The two new tests fail on
asyncio,asyncio+uvloopandasyncio+eagerwithout the patch, and already passed on Trio.I found this with an asyncio/Trio differential harness. AI-assisted, and I ran and reviewed all of it myself.
Checklist
tests/) which would fail without your patchdocs/), in case of behavior changes or new featuresdocs/versionhistory.rst).