Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate closing of existing loops #510

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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*")