Skip to content

Shield TemporaryDirectory.cleanup() from cancellation - #1316

Merged
agronholm merged 11 commits into
agronholm:masterfrom
Yasser-Ameur:fix/tempdir-cleanup-shield
Oct 17, 2026
Merged

agronholm merged 11 commits into
agronholm:masterfrom
Yasser-Ameur:fix/tempdir-cleanup-shield

Conversation

@Yasser-Ameur

@Yasser-Ameur Yasser-Ameur commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

NOTE Erasing or replacing the contents of this template will result in your pull
request being summarily closed without consideration!

Changes

Fixes #.

TemporaryDirectory.cleanup() runs its cleanup through to_thread.run_sync, which
checkpoints on entry. When the host task is already cancelled, and it usually is,
because cleanup() gets awaited from a finally: block during unwind, the checkpoint
aborts before the worker thread runs and the directory is left on disk.

#1304 fixed exactly this for __aexit__ with a shielded CancelScope. cleanup(), a
few lines below, kept the unshielded call. This gives it the same shield. The other
to_thread.run_sync teardown in the module, AsyncFile.aclose(), was already shielded
by #1314, so this was the last one.

test_cleanup_method_with_cancellation mirrors the test #1304 added for __aexit__.
It fails on the unmodified tree on all four backend variants and passes with the fix.

Checklist

If this is a user-facing code change, like a bugfix or a new feature, please ensure that
you've fulfilled the following conditions (where applicable):

  • You've added tests (in tests/) which would fail without your patch
  • You've updated the documentation (in docs/), in case of behavior changes or new
    features
  • You've added a new changelog entry (in docs/versionhistory.rst).

TemporaryDirectory.cleanup() ran the synchronous cleanup via
to_thread.run_sync, which performs a cancellation checkpoint on entry.
If the host task was already cancelled when cleanup() was called (as it
normally is, from a finally block), that checkpoint aborted the cleanup
before the worker thread ran, leaking the temporary directory.

agronholm#1304 fixed this same failure in __aexit__ by wrapping the call in a
shielded CancelScope, but left cleanup() with the identical bug a few
lines below. Apply the same fix to cleanup(), and add a regression test
mirroring the one agronholm#1304 added for __aexit__.
@Yasser-Ameur
Yasser-Ameur marked this pull request as ready for review September 9, 2026 00:06
Comment thread src/anyio/_core/_tempfile.py
…-shield

# Conflicts:
#	docs/versionhistory.rst
TemporaryDirectory.cleanup() shields the worker-thread cleanup call
from cancellation so the directory still gets removed, but returned
without ever raising the cancellation back to the caller. Add a
checkpoint_if_cancelled() after the shielded block, same as the
other shield+checkpoint pairing in this file, so a pending
cancellation is still delivered once the directory is gone. Extend
the regression test to assert both.
@Yasser-Ameur

Copy link
Copy Markdown
Contributor Author

Good catch, you're right. I've added await checkpoint_if_cancelled() right after the shielded block, so a pending cancellation is still delivered to the caller once the directory is safely removed, matching the shield-then-checkpoint pattern already used elsewhere in this file. I extended the regression test to assert both that the directory is gone and that the cancellation is still raised, and confirmed the new assertion fails without the checkpoint. __aexit__ has the same shield without a checkpoint after it; I left it alone since the question was about cleanup(), but I can add it in this PR if you'd like.

@agronholm agronholm added this to the 4.15.2 milestone Sep 20, 2026
Comment thread docs/versionhistory.rst Outdated
Comment thread tests/test_tempfile.py Outdated
Comment thread tests/test_tempfile.py Outdated

@agronholm agronholm left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

@agronholm
agronholm merged commit f6a15f6 into agronholm:master Oct 17, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants