Skip to content

Commit 0ad8e7d

Browse files
committed
Add a global fixture to clean up asyncio tasks between tests
1 parent ec5fc17 commit 0ad8e7d

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

tests/conftest.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import gc
2+
import sys
13
import json
4+
import typing
25
import asyncio
36
import inspect
47
import logging
@@ -43,6 +46,46 @@ def pytest_collection_modifyitems(session, config, items):
4346
item.add_marker(pytest.mark.filterwarnings("error::RuntimeWarning"))
4447

4548

49+
@pytest.hookimpl(trylast=True)
50+
def pytest_fixture_post_finalizer(fixturedef, request) -> None:
51+
"""Called after fixture teardown"""
52+
if fixturedef.argname != "event_loop":
53+
return
54+
55+
policy = asyncio.get_event_loop_policy()
56+
try:
57+
loop = policy.get_event_loop()
58+
except RuntimeError:
59+
loop = None
60+
if loop is not None:
61+
# Cleanup code based on the implementation of asyncio.run()
62+
try:
63+
if not loop.is_closed():
64+
asyncio.runners._cancel_all_tasks(loop) # type: ignore[attr-defined]
65+
loop.run_until_complete(loop.shutdown_asyncgens())
66+
if sys.version_info >= (3, 9):
67+
loop.run_until_complete(loop.shutdown_default_executor())
68+
finally:
69+
loop.close()
70+
new_loop = policy.new_event_loop() # Replace existing event loop
71+
# Ensure subsequent calls to get_event_loop() succeed
72+
policy.set_event_loop(new_loop)
73+
74+
75+
@pytest.fixture
76+
def event_loop(
77+
request: pytest.FixtureRequest,
78+
) -> typing.Iterator[asyncio.AbstractEventLoop]:
79+
"""Create an instance of the default event loop for each test case."""
80+
yield asyncio.get_event_loop_policy().new_event_loop()
81+
# Call the garbage collector to trigger ResourceWarning's as soon
82+
# as possible (these are triggered in various __del__ methods).
83+
# Without this, resources opened in one test can fail other tests
84+
# when the warning is generated.
85+
gc.collect()
86+
# Event loop cleanup handled by pytest_fixture_post_finalizer
87+
88+
4689
class ForwardingSerialTransport:
4790
"""
4891
Serial transport that hooks directly into a protocol

0 commit comments

Comments
 (0)