Repository navigation
test(memory): stop the loop-restart test's stale task being logged later - #349
Merged
Merged
Conversation
The #181 test leaves a cleanup task pending on a closed loop on purpose. When the garbage collector freed it, asyncio logged "Task was destroyed but it is pending!" in whichever later test was running. The test now installs an exception handler on the closed loop that drops that one report, after asserting the task is still pending. A handler on the asyncio logger now records the message for the whole run; at session end it collects garbage and fails the run, naming the test the task was freed in, so a new orphaned task cannot go unnoticed. Closes #295
pytest-asyncio 0.26, the floor the lowest tox env runs, closes each test's loop without cancelling pending tasks; 1.x runs the test in an asyncio.Runner, which cancels them. The cleanup task a MemoryBackend starts on its first operation therefore stayed pending under lowest, and the new pending-task guard failed the run with 135 reports. The default-backend fixture is now async and awaits aclose() while the loop is open, and a new autouse fixture records every MemoryBackend built during a test, including the library's fallback, and closes it at teardown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #295
Problem
test_cleanup_restarts_on_a_new_loop_after_the_old_one_closedleaves aMemoryBackendcleanup task pending on a closed loop on purpose (the #181 scenario). When the garbage collector freed that task, asyncio loggedTask was destroyed but it is pending!during an unrelated later test. asyncio logs this instead of warning, sofilterwarnings = ["error"]never caught it.Changes
loop.set_exception_handler()on the closed loop. The handler drops only that one report and passes anything else todefault_exception_handler. It relies on no private attribute.tests/conftest.pyadds a handler to theasynciologger that records the message and the test running when it fired.pytest_sessionfinishcallsgc.collect()so that a task still waiting to be collected reports before the check. Any record then sets the exit status to failed, and a terminal-summary section prints the message and says to rerun withPYTHONASYNCIODEBUG=1to find where the task was created.MemoryBackenda test builds is closed on the test's loop. The guard exposed 135 more leaked cleanup tasks in thelowesttox env. pytest-asyncio 0.26 (our floor) closes each test's loop without cancelling pending tasks. 1.x runs the test in anasyncio.Runner, which cancels them, so the leak didn't show there.setup_default_backendis now async and awaitsaclose()while the loop is still open. Its syncstop_cleanup()used to run after the loop had already closed.close_memory_backendsfixture records everyMemoryBackendbuilt during a test, asclose_network_clientsdoes for network clients. That includes the library's fallback backend. The fixture closes each one at teardown.Tests only. No library change and no changelog fragment, since nothing user-facing changes.
Verification
uv run pytest -p no:logging -s 2>&1 | grep "Task was destroyed": one match before, none after (also none withPYTHONASYNCIODEBUG=1).tox -e lowestand the latest-dependency suite both pass with the guard active.