Skip to content

Commit

Permalink
Deprecate closing of existing loops (#510)
Browse files Browse the repository at this point in the history
* [docs] Corrected comment on when a RuntimeError occurs upon calling get_event_loop.

Signed-off-by: Michael Seifert <[email protected]>

* [refactor] Turn ResourceWarning for unclosed event loops into a DeprecationWarning.

Changing the warning type emphasizes that the functionality will no longer be available in the future.

The commit also extends the tests for the deprecation warning and adjusts the changlog entry and deprecation message.

Signed-off-by: Michael Seifert <[email protected]>

---------

Signed-off-by: Michael Seifert <[email protected]>
  • Loading branch information
seifertm authored Mar 19, 2023
1 parent e1cc62d commit 947e31a
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 14 deletions.
5 changes: 4 additions & 1 deletion docs/source/reference/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ Changelog
UNRELEASED
=================
- Drop compatibility with pytest 6.1. Pytest-asyncio now depends on pytest 7.0 or newer.
- event_loop fixture teardown emits a ResourceWarning when the current event loop has not been closed.
- pytest-asyncio cleans up any stale event loops when setting up and tearing down the
event_loop fixture. This behavior has been deprecated and pytest-asyncio emits a
DeprecationWarning when tearing down the event_loop fixture and current event loop
has not been closed.

0.20.3 (22-12-08)
=================
Expand Down
22 changes: 11 additions & 11 deletions pytest_asyncio/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,9 @@ def pytest_fixture_setup(
if old_loop is not loop:
old_loop.close()
except RuntimeError:
# Swallow this, since it's probably bad event loop hygiene.
# Either the current event loop has been set to None
# or the loop policy doesn't specify to create new loops
# or we're not in the main thread
pass
policy.set_event_loop(loop)
return
Expand All @@ -420,11 +422,13 @@ def _add_finalizers(fixturedef: FixtureDef, *finalizers: Callable[[], object]) -

_UNCLOSED_EVENT_LOOP_WARNING = dedent(
"""\
unclosed event loop %r.
Possible causes are:
1. A custom "event_loop" fixture is used which doesn't close the loop
2. Your code or one of your dependencies created a new event loop during
the test run
pytest-asyncio detected an unclosed event loop when tearing down the event_loop
fixture: %r
pytest-asyncio will close the event loop for you, but future versions of the
library will no longer do so. In order to ensure compatibility with future
versions, please make sure that:
1. Any custom "event_loop" fixture properly closes the loop after yielding it
2. Your code does not modify the event loop in async fixtures or tests
"""
)

Expand All @@ -436,14 +440,10 @@ def _close_event_loop() -> None:
except RuntimeError:
loop = None
if loop is not None:
# Emit ResourceWarnings in the context of the fixture/test case
# rather than waiting for the interpreter to trigger the warning when
# garbage collecting the event loop.
if not loop.is_closed():
warnings.warn(
_UNCLOSED_EVENT_LOOP_WARNING % loop,
ResourceWarning,
source=loop,
DeprecationWarning,
)
loop.close()

Expand Down
25 changes: 23 additions & 2 deletions tests/test_event_loop_fixture_finalizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,14 @@ async def test_async_with_explicit_fixture_request(event_loop):
result.assert_outcomes(passed=1)


def test_event_loop_fixture_finalizer_raises_warning_when_loop_is_unclosed(
def test_event_loop_fixture_finalizer_raises_warning_when_fixture_leaves_loop_unclosed(
pytester: Pytester,
):
pytester.makepyfile(
dedent(
"""\
import asyncio
import pytest
import pytest_asyncio
pytest_plugins = 'pytest_asyncio'
Expand All @@ -114,3 +113,25 @@ async def test_ends_with_unclosed_loop():
result = pytester.runpytest("--asyncio-mode=strict", "-W", "default")
result.assert_outcomes(passed=1, warnings=1)
result.stdout.fnmatch_lines("*unclosed event loop*")


def test_event_loop_fixture_finalizer_raises_warning_when_test_leaves_loop_unclosed(
pytester: Pytester,
):
pytester.makepyfile(
dedent(
"""\
import asyncio
import pytest
pytest_plugins = 'pytest_asyncio'
@pytest.mark.asyncio
async def test_ends_with_unclosed_loop():
asyncio.set_event_loop(asyncio.new_event_loop())
"""
)
)
result = pytester.runpytest("--asyncio-mode=strict", "-W", "default")
result.assert_outcomes(passed=1, warnings=1)
result.stdout.fnmatch_lines("*unclosed event loop*")

0 comments on commit 947e31a

Please sign in to comment.