Skip to content

Commit f979af9

Browse files
authored
Do not try to initialize async fixtures without explicit asyncio mark in strict mode (#307)
* test: Package-scoped event_loop fixture no longer leaks into other tests. The expected behaviour is that the `event_loop` fixture defined in `tests/sessionloop/conftest.py` is torn down when all tests in `tests/sessionloop` are complete. Running the tests with the pytest option --setup-show pointed out that the fixture is torn down at the end of the test session, instead. This is an unintended side effect of the sessionloop test which may affect other tests in the test suite. Reducing the fixture scope from "package" to "module" results in the expected behaviour. The module was renamed to reflect the fact that the tests do not use a session scope. Signed-off-by: Michael Seifert <[email protected]> * test: Removed test with obsolete "forbid_global_loop". forbid_global_loop was an option to pytest.mark.asyncio which was removed in v0.6.0. The two subprocess tests are otherwise identical. Therefore, one of the tests was removed along with the obsolete option. Signed-off-by: Michael Seifert <[email protected]> * test: Ignore subprocess tests when running on CPython 3.7. When run with Python 3.7 asyncio.subprocess.create_subprocess_exec seems to be affected by an issue that prevents correct cleanup. Tests using pytest-trio will report that signal handling is already performed by another library and fail. [1] This is possibly a bug in CPython 3.7, so we ignore this test for that Python version. CPython 3.7 uses asyncio.streams.StreamReader and asyncio.streams.StreamWriter to implement asyncio.streams.StreamReaderProtocol and asyncio.subprocess.SubprocessStreamProtocol. StreamReaderProtocol contained cyclic references between the reader and the protocol, which prevented garbage collection. While StreamReaderProtocol received a patch [2], SubprocessStreamProtocol, which is used by create_subprocess_exec, possibly has the same problem, but was not patched as part of CPython 3.7. That's why we ignore this test for CPython 3.7. [1] python-trio/pytest-trio#126 [2] python/cpython#9201 Signed-off-by: Michael Seifert <[email protected]> * build: Added pytest-trio to the test dependencies. This allows testing compatibility between pytest-trio and pytest-asyncio. Signed-off-by: Michael Seifert <[email protected]> * fix: Do not try to initialize async fixtures without explicit asyncio mark in strict mode. This fixes a bug that breaks compatibility with pytest_trio. Closes #298 Signed-off-by: Michael Seifert <[email protected]>
1 parent 133d8a8 commit f979af9

File tree

7 files changed

+54
-16
lines changed

7 files changed

+54
-16
lines changed

CHANGELOG.rst

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
Changelog
33
=========
44

5+
UNRELEASED
6+
=================
7+
- Adds `pytest-trio <https://pypi.org/project/pytest-trio/>`_ to the test dependencies
8+
- Fixes a bug that caused pytest-asyncio to try to set up async pytest_trio fixtures in strict mode. `#298 <https://github.com/pytest-dev/pytest-asyncio/issues/298>`_
9+
510
0.18.2 (22-03-03)
611
=================
712
- Fix asyncio auto mode not marking static methods. `#295 <https://github.com/pytest-dev/pytest-asyncio/issues/295>`_

pytest_asyncio/plugin.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,11 @@ def _preprocess_async_fixtures(config: Config, holder: Set[FixtureDef]) -> None:
210210
# Nothing to do with a regular fixture function
211211
continue
212212
if not _has_explicit_asyncio_mark(func):
213-
if asyncio_mode == Mode.AUTO:
213+
if asyncio_mode == Mode.STRICT:
214+
# Ignore async fixtures without explicit asyncio mark in strict mode
215+
# This applies to pytest_trio fixtures, for example
216+
continue
217+
elif asyncio_mode == Mode.AUTO:
214218
# Enforce asyncio mode if 'auto'
215219
_set_explicit_asyncio_mark(func)
216220
elif asyncio_mode == Mode.LEGACY:

setup.cfg

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ testing =
4444
hypothesis >= 5.7.1
4545
flaky >= 3.5.0
4646
mypy == 0.931
47+
pytest-trio >= 0.7.0
4748

4849
[options.entry_points]
4950
pytest11 =

tests/sessionloop/conftest.py renamed to tests/loop_fixture_scope/conftest.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
import pytest
44

55

6-
class CustomSelectorLoopSession(asyncio.SelectorEventLoop):
6+
class CustomSelectorLoop(asyncio.SelectorEventLoop):
77
"""A subclass with no overrides, just to test for presence."""
88

99

10-
loop = CustomSelectorLoopSession()
10+
loop = CustomSelectorLoop()
1111

1212

13-
@pytest.fixture(scope="package")
13+
@pytest.fixture(scope="module")
1414
def event_loop():
1515
"""Create an instance of the default event loop for each test case."""
1616
yield loop

tests/sessionloop/test_session_loops.py renamed to tests/loop_fixture_scope/test_loop_fixture_scope.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Unit tests for overriding the event loop with a session scoped one."""
1+
"""Unit tests for overriding the event loop with a larger scoped one."""
22
import asyncio
33

44
import pytest
@@ -8,7 +8,7 @@
88
async def test_for_custom_loop():
99
"""This test should be executed using the custom loop."""
1010
await asyncio.sleep(0.01)
11-
assert type(asyncio.get_event_loop()).__name__ == "CustomSelectorLoopSession"
11+
assert type(asyncio.get_event_loop()).__name__ == "CustomSelectorLoop"
1212

1313

1414
@pytest.mark.asyncio

tests/test_subprocess.py

+13-10
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,20 @@ def event_loop():
1515
loop.close()
1616

1717

18-
@pytest.mark.asyncio(forbid_global_loop=False)
19-
async def test_subprocess(event_loop):
20-
"""Starting a subprocess should be possible."""
21-
proc = await asyncio.subprocess.create_subprocess_exec(
22-
sys.executable, "--version", stdout=asyncio.subprocess.PIPE
23-
)
24-
await proc.communicate()
18+
@pytest.mark.skipif(
19+
sys.version_info < (3, 8),
20+
reason="""
21+
When run with Python 3.7 asyncio.subprocess.create_subprocess_exec seems to be
22+
affected by an issue that prevents correct cleanup. Tests using pytest-trio
23+
will report that signal handling is already performed by another library and
24+
fail. [1] This is possibly a bug in CPython 3.7, so we ignore this test for
25+
that Python version.
2526
26-
27-
@pytest.mark.asyncio(forbid_global_loop=True)
28-
async def test_subprocess_forbid(event_loop):
27+
[1] https://github.com/python-trio/pytest-trio/issues/126
28+
""",
29+
)
30+
@pytest.mark.asyncio
31+
async def test_subprocess(event_loop):
2932
"""Starting a subprocess should be possible."""
3033
proc = await asyncio.subprocess.create_subprocess_exec(
3134
sys.executable, "--version", stdout=asyncio.subprocess.PIPE

tests/trio/test_fixtures.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from textwrap import dedent
2+
3+
4+
def test_strict_mode_ignores_trio_fixtures(testdir):
5+
testdir.makepyfile(
6+
dedent(
7+
"""\
8+
import pytest
9+
import pytest_asyncio
10+
import pytest_trio
11+
12+
pytest_plugins = ["pytest_asyncio", "pytest_trio"]
13+
14+
@pytest_trio.trio_fixture
15+
async def any_fixture():
16+
return True
17+
18+
@pytest.mark.trio
19+
async def test_anything(any_fixture):
20+
pass
21+
"""
22+
)
23+
)
24+
result = testdir.runpytest("--asyncio-mode=strict")
25+
result.assert_outcomes(passed=1)

0 commit comments

Comments
 (0)