Skip to content

Commit bbe8458

Browse files
committed
Fix unclosed event loop
1 parent af62924 commit bbe8458

3 files changed

Lines changed: 39 additions & 14 deletions

File tree

changelog.d/724.fixed.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed a ``ResourceWarning: unclosed event loop`` warning that could occur when a synchronous test called ``asyncio.run()`` or otherwise unset the current event loop after pytest-asyncio had run an async test or fixture.

pytest_asyncio/plugin.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -806,23 +806,13 @@ def _temporary_event_loop(loop: AbstractEventLoop) -> Iterator[None]:
806806
@contextlib.contextmanager
807807
def _temporary_event_loop_policy(
808808
policy: AbstractEventLoopPolicy,
809-
*,
810-
has_custom_factory: bool,
811809
) -> Iterator[None]:
812810
old_loop_policy = _get_event_loop_policy()
813-
if has_custom_factory:
814-
old_loop = None
815-
else:
816-
try:
817-
old_loop = _get_event_loop_no_warn()
818-
except RuntimeError:
819-
old_loop = None
820811
_set_event_loop_policy(policy)
821812
try:
822813
yield
823814
finally:
824815
_set_event_loop_policy(old_loop_policy)
825-
_set_event_loop(old_loop)
826816

827817

828818
def _get_event_loop_policy() -> AbstractEventLoopPolicy:
@@ -1042,10 +1032,7 @@ def _scoped_runner(
10421032
) -> Iterator[Runner]:
10431033
new_loop_policy = event_loop_policy
10441034
debug_mode = _get_asyncio_debug(request.config)
1045-
with _temporary_event_loop_policy(
1046-
new_loop_policy,
1047-
has_custom_factory=_asyncio_loop_factory is not None,
1048-
):
1035+
with _temporary_event_loop_policy(new_loop_policy):
10491036
runner = Runner(
10501037
debug=debug_mode,
10511038
loop_factory=_asyncio_loop_factory,
@@ -1068,6 +1055,9 @@ def _scoped_runner(
10681055
_RUNNER_TEARDOWN_WARNING % traceback.format_exc(),
10691056
RuntimeWarning,
10701057
)
1058+
finally:
1059+
if _asyncio_loop_factory is not None:
1060+
_set_event_loop(None)
10711061

10721062
return _scoped_runner
10731063

tests/test_set_event_loop.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,40 @@ async def test_after(self):
9999
result.assert_outcomes(passed=3)
100100

101101

102+
def test_asyncio_run_after_async_fixture_does_not_leak_loop(
103+
pytester: Pytester,
104+
):
105+
pytester.makeini(dedent("""\
106+
[pytest]
107+
asyncio_default_fixture_loop_scope = function
108+
"""))
109+
pytester.makepyfile(dedent("""\
110+
import asyncio
111+
import gc
112+
import pytest
113+
import pytest_asyncio
114+
115+
pytest_plugins = "pytest_asyncio"
116+
117+
@pytest_asyncio.fixture
118+
async def async_fixture():
119+
yield
120+
121+
@pytest.mark.asyncio
122+
async def test_async_function_uses_async_fixture(async_fixture):
123+
pass
124+
125+
def test_collect_unclosed_loops():
126+
async def amain():
127+
pass
128+
129+
asyncio.run(amain())
130+
gc.collect()
131+
"""))
132+
result = pytester.runpytest_subprocess("-W", "error")
133+
result.assert_outcomes(passed=2)
134+
135+
102136
@pytest.mark.parametrize("test_loop_scope", ("module", "package", "session"))
103137
@pytest.mark.parametrize(
104138
"loop_breaking_action",

0 commit comments

Comments
 (0)