From 0b74426ade0a9e1f10f0dd7364082b7dbe311aa2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 18:07:50 +0000 Subject: [PATCH 01/20] Build(deps): Bump hypothesis in /dependencies/default Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.92.1 to 6.92.2. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.92.1...hypothesis-python-6.92.2) --- updated-dependencies: - dependency-name: hypothesis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dependencies/default/constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/default/constraints.txt b/dependencies/default/constraints.txt index 56be8787..99a15a3f 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,7 +1,7 @@ attrs==23.1.0 coverage==7.3.4 exceptiongroup==1.2.0 -hypothesis==6.92.1 +hypothesis==6.92.2 iniconfig==2.0.0 packaging==23.2 pluggy==1.3.0 From 54dce0ebe1f102b635b5d5a6201bc8ba1b5f80ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 18:07:31 +0000 Subject: [PATCH 02/20] Build(deps): Bump attrs from 23.1.0 to 23.2.0 in /dependencies/default Bumps [attrs](https://github.com/sponsors/hynek) from 23.1.0 to 23.2.0. - [Commits](https://github.com/sponsors/hynek/commits) --- updated-dependencies: - dependency-name: attrs dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dependencies/default/constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/default/constraints.txt b/dependencies/default/constraints.txt index 99a15a3f..5abbd2bf 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,4 +1,4 @@ -attrs==23.1.0 +attrs==23.2.0 coverage==7.3.4 exceptiongroup==1.2.0 hypothesis==6.92.2 From 8fd888e550d3ad00d1042c1aa25e6b4429a5035e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 18:07:27 +0000 Subject: [PATCH 03/20] Build(deps): Bump pytest from 7.4.3 to 7.4.4 in /dependencies/default Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.4.3 to 7.4.4. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.4.3...7.4.4) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dependencies/default/constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/default/constraints.txt b/dependencies/default/constraints.txt index 5abbd2bf..7424772d 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -5,7 +5,7 @@ hypothesis==6.92.2 iniconfig==2.0.0 packaging==23.2 pluggy==1.3.0 -pytest==7.4.3 +pytest==7.4.4 sortedcontainers==2.4.0 tomli==2.0.1 typing_extensions==4.9.0 From 3e67b0dc65e566b6f326d5576913dfe1b8e05339 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:26:18 +0000 Subject: [PATCH 04/20] Build(deps): Bump coverage from 7.3.4 to 7.4.0 in /dependencies/default Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.3.4 to 7.4.0. - [Release notes](https://github.com/nedbat/coveragepy/releases) - [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst) - [Commits](https://github.com/nedbat/coveragepy/compare/7.3.4...7.4.0) --- updated-dependencies: - dependency-name: coverage dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dependencies/default/constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/default/constraints.txt b/dependencies/default/constraints.txt index 7424772d..207cb918 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,5 +1,5 @@ attrs==23.2.0 -coverage==7.3.4 +coverage==7.4.0 exceptiongroup==1.2.0 hypothesis==6.92.2 iniconfig==2.0.0 From a88de75f4ba6c3cb7b4f97bc11acda96fa4dd7a2 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Sat, 6 Jan 2024 11:26:30 +0100 Subject: [PATCH 05/20] [fix] Fixes a bug that caused an INTERNALERROR when an __init__.py raised an error. Signed-off-by: Michael Seifert --- pytest_asyncio/plugin.py | 29 ++++++++++++++++++----------- tests/test_import.py | 20 +++++++++++++++++++- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index eb013f46..4da72455 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -620,6 +620,24 @@ def _patched_collect(): collector.obj.__pytest_asyncio_scoped_event_loop = scoped_event_loop return collector.__original_collect() + collector.__original_collect = collector.collect + collector.collect = _patched_collect + elif type(collector) is Package: + + def _patched_collect(): + # When collector is a package, collector.obj is the package's __init__.py. + # pytest doesn't seem to collect fixtures in __init__.py. + # Using parsefactories to collect fixtures in __init__.py their baseid will + # end with "__init__.py", thus limiting the scope of the fixture to the + # init module. Therefore, we tell the pluginmanager explicitly to collect + # the fixtures in the init module, but strip "__init__.py" from the baseid + # Possibly related to https://github.com/pytest-dev/pytest/issues/4085 + collector.obj.__pytest_asyncio_scoped_event_loop = scoped_event_loop + fixturemanager = collector.config.pluginmanager.get_plugin("funcmanage") + package_node_id = _removesuffix(collector.nodeid, "__init__.py") + fixturemanager.parsefactories(collector.obj, nodeid=package_node_id) + return collector.__original_collect() + collector.__original_collect = collector.collect collector.collect = _patched_collect else: @@ -628,17 +646,6 @@ def _patched_collect(): if pyobject is None: return pyobject.__pytest_asyncio_scoped_event_loop = scoped_event_loop - # When collector is a package, collector.obj is the package's __init__.py. - # pytest doesn't seem to collect fixtures in __init__.py. - # Using parsefactories to collect fixtures in __init__.py their baseid will end - # with "__init__.py", thus limiting the scope of the fixture to the init module. - # Therefore, we tell the pluginmanager explicitly to collect the fixtures - # in the init module, but strip "__init__.py" from the baseid - # Possibly related to https://github.com/pytest-dev/pytest/issues/4085 - if isinstance(collector, Package): - fixturemanager = collector.config.pluginmanager.get_plugin("funcmanage") - package_node_id = _removesuffix(collector.nodeid, "__init__.py") - fixturemanager.parsefactories(collector.obj, nodeid=package_node_id) def _removesuffix(s: str, suffix: str) -> str: diff --git a/tests/test_import.py b/tests/test_import.py index 77352150..05435675 100644 --- a/tests/test_import.py +++ b/tests/test_import.py @@ -3,7 +3,7 @@ from pytest import Pytester -def test_import_warning(pytester: Pytester): +def test_import_warning_does_not_cause_internal_error(pytester: Pytester): pytester.makepyfile( dedent( """\ @@ -16,3 +16,21 @@ async def test_errors_out(): ) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(errors=1) + + +def test_import_warning_in_package_does_not_cause_internal_error(pytester: Pytester): + pytester.makepyfile( + __init__=dedent( + """\ + raise ImportWarning() + """ + ), + test_a=dedent( + """\ + async def test_errors_out(): + pass + """ + ), + ) + result = pytester.runpytest("--asyncio-mode=auto") + result.assert_outcomes(errors=1) From e426a597c7504058cd4e9a5d525a17f7bc91376e Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Sun, 7 Jan 2024 15:02:51 +0100 Subject: [PATCH 06/20] [refactor] Attach package-scoped fixtures to a virtual/temporary Python module in the package, rather than attaching the fixture to the package's __init__.py. Signed-off-by: Michael Seifert --- pytest_asyncio/plugin.py | 75 +++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 4da72455..bb5e7898 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -8,6 +8,8 @@ import sys import warnings from asyncio import AbstractEventLoopPolicy +from pathlib import Path +from tempfile import NamedTemporaryFile from textwrap import dedent from typing import ( Any, @@ -625,18 +627,67 @@ def _patched_collect(): elif type(collector) is Package: def _patched_collect(): - # When collector is a package, collector.obj is the package's __init__.py. - # pytest doesn't seem to collect fixtures in __init__.py. - # Using parsefactories to collect fixtures in __init__.py their baseid will - # end with "__init__.py", thus limiting the scope of the fixture to the - # init module. Therefore, we tell the pluginmanager explicitly to collect - # the fixtures in the init module, but strip "__init__.py" from the baseid - # Possibly related to https://github.com/pytest-dev/pytest/issues/4085 - collector.obj.__pytest_asyncio_scoped_event_loop = scoped_event_loop - fixturemanager = collector.config.pluginmanager.get_plugin("funcmanage") - package_node_id = _removesuffix(collector.nodeid, "__init__.py") - fixturemanager.parsefactories(collector.obj, nodeid=package_node_id) - return collector.__original_collect() + # When collector is a Package, collector.obj is the package's __init__.py. + # Accessing the __init__.py to attach the fixture function may trigger + # additional module imports or change the order of imports, which leads to + # a number of problems. + # see https://github.com/pytest-dev/pytest-asyncio/issues/729 + # Moreover, Package.obj has been removed in pytest 8. + # Therefore, pytest-asyncio creates a temporary Python module inside the + # collected package. The sole purpose of that module is to house a fixture + # function for the pacakge-scoped event loop fixture. Once the fixture + # has been evaluated by pytest, the temporary module can be removed. + with NamedTemporaryFile( + dir=collector.path.parent, + prefix="pytest_asyncio_virtual_module_", + suffix=".py", + ) as virtual_module_file: + virtual_module = Module.from_parent( + collector, path=Path(virtual_module_file.name) + ) + virtual_module_file.write( + dedent( + f"""\ + import asyncio + import pytest + from pytest_asyncio.plugin import _temporary_event_loop_policy + @pytest.fixture( + scope="{collector_scope}", + name="{collector.nodeid}::", + ) + def scoped_event_loop( + *args, + event_loop_policy, + ): + new_loop_policy = event_loop_policy + with _temporary_event_loop_policy(new_loop_policy): + loop = asyncio.new_event_loop() + loop.__pytest_asyncio = True + asyncio.set_event_loop(loop) + yield loop + loop.close() + """ + ).encode() + ) + virtual_module_file.flush() + # Pytest's fixture matching algorithm compares a fixture's baseid with + # an Item's nodeid to determine whether a fixture is available for a + # specific Item. Since Package.nodeid ends with __init__.py, the + # fixture's baseid will also end with __init__.py, which prevents + # the fixture from being matched to test items in the current package. + # Since the fixture matching is purely based on string comparison, we + # strip the __init__.py suffix from the Package's node ID and + # tell the fixturemanager to collect the fixture with the modified + # nodeid. This makes the fixture visible to all items in the package. + # see also https://github.com/pytest-dev/pytest/issues/11662#issuecomment-1879310072 # noqa + # Possibly related to https://github.com/pytest-dev/pytest/issues/4085 + fixturemanager = collector.config.pluginmanager.get_plugin("funcmanage") + package_node_id = _removesuffix(collector.nodeid, "__init__.py") + fixturemanager.parsefactories( + virtual_module.obj, nodeid=package_node_id + ) + yield virtual_module + yield from collector.__original_collect() collector.__original_collect = collector.collect collector.collect = _patched_collect From 84703dffac6525515bfb29211c4bd14887f5f767 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Sun, 7 Jan 2024 16:46:34 +0100 Subject: [PATCH 07/20] [fix] Fixes a bug that caused pytest-asyncio to import additional, unrelated packages during test collection. This was caused by a missing call to collector.funcnamefilter when setting up the packaged-scoped event loop fixture function. The new code respects funcnamefilter and monkeypatches Package.collect to install a package-scoped loop whenever an __init__.py is encountered. Signed-off-by: Michael Seifert --- docs/source/reference/changelog.rst | 8 ++ pytest_asyncio/plugin.py | 138 +++++++++++++++++----------- tests/markers/test_package_scope.py | 1 + tests/test_import.py | 22 +++++ 4 files changed, 114 insertions(+), 55 deletions(-) diff --git a/docs/source/reference/changelog.rst b/docs/source/reference/changelog.rst index b6f57af2..dfcc7c05 100644 --- a/docs/source/reference/changelog.rst +++ b/docs/source/reference/changelog.rst @@ -2,6 +2,14 @@ Changelog ========= +0.23.4 (UNRELEASED) +=================== +- pytest-asyncio no longer imports additional, unrelated packages during test collection `#729 `_ + +Known issues +------------ +As of v0.23, pytest-asyncio attaches an asyncio event loop to each item of the test suite (i.e. session, packages, modules, classes, functions) and allows tests to be run in those loops when marked accordingly. Pytest-asyncio currently assumes that async fixture scope is correlated with the new event loop scope. This prevents fixtures from being evaluated independently from the event loop scope and breaks some existing test suites (see `#706`_). For example, a test suite may require all fixtures and tests to run in the same event loop, but have async fixtures that are set up and torn down for each module. If you're affected by this issue, please continue using the v0.21 release, until it is resolved. + 0.23.3 (2024-01-01) =================== - Fixes a bug that caused event loops to be closed prematurely when using async generator fixtures with class scope or wider in a function-scoped test `#706 `_ diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index bb5e7898..cfc28e8d 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -30,6 +30,7 @@ ) import pytest +from _pytest.pathlib import visit from pytest import ( Class, Collector, @@ -625,68 +626,95 @@ def _patched_collect(): collector.__original_collect = collector.collect collector.collect = _patched_collect elif type(collector) is Package: + if not collector.funcnamefilter(collector.name): + return def _patched_collect(): - # When collector is a Package, collector.obj is the package's __init__.py. - # Accessing the __init__.py to attach the fixture function may trigger - # additional module imports or change the order of imports, which leads to - # a number of problems. - # see https://github.com/pytest-dev/pytest-asyncio/issues/729 - # Moreover, Package.obj has been removed in pytest 8. - # Therefore, pytest-asyncio creates a temporary Python module inside the - # collected package. The sole purpose of that module is to house a fixture - # function for the pacakge-scoped event loop fixture. Once the fixture - # has been evaluated by pytest, the temporary module can be removed. - with NamedTemporaryFile( - dir=collector.path.parent, - prefix="pytest_asyncio_virtual_module_", - suffix=".py", - ) as virtual_module_file: - virtual_module = Module.from_parent( - collector, path=Path(virtual_module_file.name) - ) - virtual_module_file.write( - dedent( - f"""\ - import asyncio - import pytest - from pytest_asyncio.plugin import _temporary_event_loop_policy - @pytest.fixture( - scope="{collector_scope}", - name="{collector.nodeid}::", - ) - def scoped_event_loop( - *args, - event_loop_policy, - ): - new_loop_policy = event_loop_policy - with _temporary_event_loop_policy(new_loop_policy): - loop = asyncio.new_event_loop() - loop.__pytest_asyncio = True - asyncio.set_event_loop(loop) - yield loop - loop.close() - """ - ).encode() - ) - virtual_module_file.flush() + # pytest.Package collects all files and sub-packages. Pytest 8 changes + # this logic to only collect a single directory. Sub-packages are then + # collected by a separate Package collector. Therefore, this logic can be + # dropped, once we move to pytest 8. + collector_dir = Path(collector.path.parent) + for direntry in visit(str(collector_dir), recurse=collector._recurse): + if not direntry.name == "__init__.py": + # No need to register a package-scoped fixture, if we aren't + # collecting a (sub-)package + continue + pkgdir = Path(direntry.path).parent + pkg_nodeid = str(pkgdir.relative_to(collector_dir)) + if pkg_nodeid == ".": + pkg_nodeid = "" # Pytest's fixture matching algorithm compares a fixture's baseid with # an Item's nodeid to determine whether a fixture is available for a - # specific Item. Since Package.nodeid ends with __init__.py, the - # fixture's baseid will also end with __init__.py, which prevents - # the fixture from being matched to test items in the current package. - # Since the fixture matching is purely based on string comparison, we - # strip the __init__.py suffix from the Package's node ID and - # tell the fixturemanager to collect the fixture with the modified - # nodeid. This makes the fixture visible to all items in the package. + # specific Item. Package.nodeid ends with __init__.py, so the + # fixture's baseid will also end with __init__.py and prevents + # the fixture from being matched to test items in the package. + # Furthermore, Package also collects any sub-packages, which means + # the ID of the scoped event loop for the package must change for + # each sub-package. + # As the fixture matching is purely based on string comparison, we + # can assemble a path based on the root package path + # (i.e. Package.path.parent) and the sub-package path + # (i.e. Path(direntry.path).parent)). This makes the fixture visible + # to all items in the package. # see also https://github.com/pytest-dev/pytest/issues/11662#issuecomment-1879310072 # noqa # Possibly related to https://github.com/pytest-dev/pytest/issues/4085 - fixturemanager = collector.config.pluginmanager.get_plugin("funcmanage") - package_node_id = _removesuffix(collector.nodeid, "__init__.py") - fixturemanager.parsefactories( - virtual_module.obj, nodeid=package_node_id + fixture_id = ( + str(Path(pkg_nodeid).joinpath("__init__.py")) + "::" ) - yield virtual_module + # When collector is a Package, collector.obj is the package's + # __init__.py. Accessing the __init__.py to attach the fixture function + # may trigger additional module imports or change the order of imports, + # which leads to a number of problems. + # see https://github.com/pytest-dev/pytest-asyncio/issues/729 + # Moreover, Package.obj has been removed in pytest 8. + # Therefore, pytest-asyncio creates a temporary Python module inside the + # collected package. The sole purpose of that module is to house a + # fixture function for the pacakge-scoped event loop fixture. Once the + # fixture has been evaluated by pytest, the temporary module + # can be removed. + with NamedTemporaryFile( + dir=pkgdir, + prefix="pytest_asyncio_virtual_module_", + suffix=".py", + ) as virtual_module_file: + virtual_module = Module.from_parent( + collector, path=Path(virtual_module_file.name) + ) + virtual_module_file.write( + dedent( + f"""\ + import asyncio + import pytest + from pytest_asyncio.plugin \ + import _temporary_event_loop_policy + @pytest.fixture( + scope="{collector_scope}", + name="{fixture_id}", + ) + def scoped_event_loop( + *args, + event_loop_policy, + ): + new_loop_policy = event_loop_policy + with _temporary_event_loop_policy(new_loop_policy): + loop = asyncio.new_event_loop() + loop.__pytest_asyncio = True + asyncio.set_event_loop(loop) + yield loop + loop.close() + """ + ).encode() + ) + virtual_module_file.flush() + fixturemanager = collector.config.pluginmanager.get_plugin( + "funcmanage" + ) + # Collect the fixtures in the virtual module with the node ID of + # the current sub-package to ensure correct fixture matching. + # see also https://github.com/pytest-dev/pytest/issues/11662#issuecomment-1879310072 # noqa + fixturemanager.parsefactories(virtual_module.obj, nodeid=pkg_nodeid) + yield virtual_module yield from collector.__original_collect() collector.__original_collect = collector.collect diff --git a/tests/markers/test_package_scope.py b/tests/markers/test_package_scope.py index 1dc8a5c9..e0f44322 100644 --- a/tests/markers/test_package_scope.py +++ b/tests/markers/test_package_scope.py @@ -46,6 +46,7 @@ async def test_this_runs_in_same_loop(self): ), ) subpkg = pytester.mkpydir(subpackage_name) + subpkg.joinpath("__init__.py").touch() subpkg.joinpath("test_subpkg.py").write_text( dedent( f"""\ diff --git a/tests/test_import.py b/tests/test_import.py index 05435675..9912ae0c 100644 --- a/tests/test_import.py +++ b/tests/test_import.py @@ -34,3 +34,25 @@ async def test_errors_out(): ) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(errors=1) + + +def test_does_not_import_unrelated_packages(pytester: Pytester): + pkg_dir = pytester.mkpydir("mypkg") + pkg_dir.joinpath("__init__.py").write_text( + dedent( + """\ + raise ImportError() + """ + ), + ) + test_dir = pytester.mkdir("tests") + test_dir.joinpath("test_a.py").write_text( + dedent( + """\ + async def test_passes(): + pass + """ + ), + ) + result = pytester.runpytest("--asyncio-mode=auto") + result.assert_outcomes(passed=1) From 1f924b7b19fe774df2706218b6ba5d338ef63ce0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:26:45 +0000 Subject: [PATCH 08/20] Build(deps): Bump hypothesis in /dependencies/default Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.92.2 to 6.92.5. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.92.2...hypothesis-python-6.92.5) --- updated-dependencies: - dependency-name: hypothesis dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dependencies/default/constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/default/constraints.txt b/dependencies/default/constraints.txt index 207cb918..73bb677a 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,7 +1,7 @@ attrs==23.2.0 coverage==7.4.0 exceptiongroup==1.2.0 -hypothesis==6.92.2 +hypothesis==6.92.5 iniconfig==2.0.0 packaging==23.2 pluggy==1.3.0 From e4870031f0e2b2bb081e6779d647f01494c61dd0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:12:29 +0000 Subject: [PATCH 09/20] Build(deps): Bump alabaster from 0.7.13 to 0.7.15 in /dependencies/docs Bumps [alabaster](https://github.com/sphinx-doc/alabaster) from 0.7.13 to 0.7.15. - [Release notes](https://github.com/sphinx-doc/alabaster/releases) - [Changelog](https://github.com/sphinx-doc/alabaster/blob/master/docs/changelog.rst) - [Commits](https://github.com/sphinx-doc/alabaster/compare/0.7.13...0.7.15) --- updated-dependencies: - dependency-name: alabaster dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- dependencies/docs/constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/docs/constraints.txt b/dependencies/docs/constraints.txt index c8938488..a8810c2d 100644 --- a/dependencies/docs/constraints.txt +++ b/dependencies/docs/constraints.txt @@ -1,4 +1,4 @@ -alabaster==0.7.13 +alabaster==0.7.15 Babel==2.14.0 certifi==2023.11.17 charset-normalizer==3.3.2 From aecfd01789656cd21f2e13892567a2f1dd320fb3 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 9 Jan 2024 18:18:42 +0100 Subject: [PATCH 10/20] [build] Constrain the maximum supported pytest version to pytest <8. Pytest 8 made some changes to its test collection and pytest-asyncio currently isn't compatible with those changes. see https://github.com/pytest-dev/pytest-asyncio/issues/737 Signed-off-by: Michael Seifert --- dependencies/default/requirements.txt | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies/default/requirements.txt b/dependencies/default/requirements.txt index 0828607f..107f59f1 100644 --- a/dependencies/default/requirements.txt +++ b/dependencies/default/requirements.txt @@ -1,3 +1,3 @@ # Always adjust install_requires in setup.cfg and pytest-min-requirements.txt # when changing runtime dependencies -pytest >= 7.0.0 +pytest >= 7.0.0,<8 diff --git a/setup.cfg b/setup.cfg index fdbaf625..45d70b37 100644 --- a/setup.cfg +++ b/setup.cfg @@ -40,7 +40,7 @@ include_package_data = True # Always adjust requirements.txt and pytest-min-requirements.txt when changing runtime dependencies install_requires = - pytest >= 7.0.0 + pytest >= 7.0.0,<8 [options.extras_require] testing = From ef619b9fea227a70387f1c97828cc21617c1ea2d Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 10 Jan 2024 18:38:53 +0100 Subject: [PATCH 11/20] [build] Run tests on Windows. The use of temporary files for injecting fixtures into packages shows platform-specific behavior. As a result, it makes sense to run the tests on Windows as well, at least as long as the virtual module workaround is in place. Signed-off-by: Michael Seifert --- .github/workflows/main.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6aeaaa83..972620a1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -56,11 +56,12 @@ jobs: path: dist test: - name: Python ${{ matrix.python-version }} - runs-on: ubuntu-latest + name: ${{ matrix.os }} - Python ${{ matrix.python-version }} + runs-on: ${{ matrix.os }} strategy: matrix: + os: [ubuntu-latest, windows-latest] python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] steps: @@ -75,7 +76,6 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - set -xe python -VV python -m site python -m pip install --upgrade pip @@ -85,6 +85,7 @@ jobs: - name: Store coverage data uses: actions/upload-artifact@v3 + if: "!endsWith(matrix.os, 'windows')" with: name: coverage-per-interpreter path: .coverage.* From 9572f3e0e42d3359cbea5b70a02be6169059f0e8 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 10 Jan 2024 19:12:05 +0100 Subject: [PATCH 12/20] [tests] Fixed deprecated use of yield_fixture in test. Signed-off-by: Michael Seifert --- tests/test_subprocess.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_subprocess.py b/tests/test_subprocess.py index 14d3498a..f60b2824 100644 --- a/tests/test_subprocess.py +++ b/tests/test_subprocess.py @@ -8,7 +8,7 @@ # The default asyncio event loop implementation on Windows does not # support subprocesses. Subprocesses are available for Windows if a # ProactorEventLoop is used. - @pytest.yield_fixture() + @pytest.fixture() def event_loop(): loop = asyncio.ProactorEventLoop() yield loop From df487d5e2d7976c89f79d3459e7cb13c3899082e Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 10 Jan 2024 19:13:29 +0100 Subject: [PATCH 13/20] [ci] Shorten names of display OS in CI jobs. Signed-off-by: Michael Seifert --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 972620a1..6f240e0a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,11 +57,11 @@ jobs: test: name: ${{ matrix.os }} - Python ${{ matrix.python-version }} - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.os }}-latest strategy: matrix: - os: [ubuntu-latest, windows-latest] + os: [ubuntu, windows] python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] steps: From 8ba9bd03c44584929c7251cf1383df23f5e14f44 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 10 Jan 2024 19:15:09 +0100 Subject: [PATCH 14/20] [fix] Make virtual modules compatible with Windows. Signed-off-by: Michael Seifert --- pytest_asyncio/plugin.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index cfc28e8d..a1bdbd91 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -4,6 +4,7 @@ import enum import functools import inspect +import os import socket import sys import warnings @@ -659,9 +660,7 @@ def _patched_collect(): # to all items in the package. # see also https://github.com/pytest-dev/pytest/issues/11662#issuecomment-1879310072 # noqa # Possibly related to https://github.com/pytest-dev/pytest/issues/4085 - fixture_id = ( - str(Path(pkg_nodeid).joinpath("__init__.py")) + "::" - ) + fixture_id = f"{pkg_nodeid}/__init__.py::".lstrip("/") # When collector is a Package, collector.obj is the package's # __init__.py. Accessing the __init__.py to attach the fixture function # may trigger additional module imports or change the order of imports, @@ -677,6 +676,7 @@ def _patched_collect(): dir=pkgdir, prefix="pytest_asyncio_virtual_module_", suffix=".py", + delete=False, # Required for Windows compatibility ) as virtual_module_file: virtual_module = Module.from_parent( collector, path=Path(virtual_module_file.name) @@ -684,6 +684,10 @@ def _patched_collect(): virtual_module_file.write( dedent( f"""\ + # This is a temporary file created by pytest-asyncio + # If you see this file, a pytest run has crashed and + # wasn't able to clean up the file in time. + # You can safely remove this file. import asyncio import pytest from pytest_asyncio.plugin \ @@ -715,6 +719,7 @@ def scoped_event_loop( # see also https://github.com/pytest-dev/pytest/issues/11662#issuecomment-1879310072 # noqa fixturemanager.parsefactories(virtual_module.obj, nodeid=pkg_nodeid) yield virtual_module + os.unlink(virtual_module_file.name) yield from collector.__original_collect() collector.__original_collect = collector.collect From 256ef7d2f5baab34d8668c6ffc476b7740b782fc Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Sun, 14 Jan 2024 19:46:18 +0100 Subject: [PATCH 15/20] [fix] pytest-asyncio no longer uses virtual modules to install dynamic package-scoped fixtures. The temporary files used for this mechanism appearing as disappear after they have been collected. This seems to create issues in some projects, such as setuptools. see https://github.com/pytest-dev/pytest-asyncio/issues/729#issuecomment-1887800329 Signed-off-by: Michael Seifert --- pytest_asyncio/plugin.py | 114 +++++++-------------------------------- 1 file changed, 19 insertions(+), 95 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index a1bdbd91..5bf8d3b5 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -4,13 +4,10 @@ import enum import functools import inspect -import os import socket import sys import warnings from asyncio import AbstractEventLoopPolicy -from pathlib import Path -from tempfile import NamedTemporaryFile from textwrap import dedent from typing import ( Any, @@ -31,7 +28,6 @@ ) import pytest -from _pytest.pathlib import visit from pytest import ( Class, Collector, @@ -627,100 +623,28 @@ def _patched_collect(): collector.__original_collect = collector.collect collector.collect = _patched_collect elif type(collector) is Package: - if not collector.funcnamefilter(collector.name): - return def _patched_collect(): - # pytest.Package collects all files and sub-packages. Pytest 8 changes - # this logic to only collect a single directory. Sub-packages are then - # collected by a separate Package collector. Therefore, this logic can be - # dropped, once we move to pytest 8. - collector_dir = Path(collector.path.parent) - for direntry in visit(str(collector_dir), recurse=collector._recurse): - if not direntry.name == "__init__.py": - # No need to register a package-scoped fixture, if we aren't - # collecting a (sub-)package - continue - pkgdir = Path(direntry.path).parent - pkg_nodeid = str(pkgdir.relative_to(collector_dir)) - if pkg_nodeid == ".": - pkg_nodeid = "" - # Pytest's fixture matching algorithm compares a fixture's baseid with - # an Item's nodeid to determine whether a fixture is available for a - # specific Item. Package.nodeid ends with __init__.py, so the - # fixture's baseid will also end with __init__.py and prevents - # the fixture from being matched to test items in the package. - # Furthermore, Package also collects any sub-packages, which means - # the ID of the scoped event loop for the package must change for - # each sub-package. - # As the fixture matching is purely based on string comparison, we - # can assemble a path based on the root package path - # (i.e. Package.path.parent) and the sub-package path - # (i.e. Path(direntry.path).parent)). This makes the fixture visible - # to all items in the package. - # see also https://github.com/pytest-dev/pytest/issues/11662#issuecomment-1879310072 # noqa - # Possibly related to https://github.com/pytest-dev/pytest/issues/4085 - fixture_id = f"{pkg_nodeid}/__init__.py::".lstrip("/") - # When collector is a Package, collector.obj is the package's - # __init__.py. Accessing the __init__.py to attach the fixture function - # may trigger additional module imports or change the order of imports, - # which leads to a number of problems. - # see https://github.com/pytest-dev/pytest-asyncio/issues/729 - # Moreover, Package.obj has been removed in pytest 8. - # Therefore, pytest-asyncio creates a temporary Python module inside the - # collected package. The sole purpose of that module is to house a - # fixture function for the pacakge-scoped event loop fixture. Once the - # fixture has been evaluated by pytest, the temporary module - # can be removed. - with NamedTemporaryFile( - dir=pkgdir, - prefix="pytest_asyncio_virtual_module_", - suffix=".py", - delete=False, # Required for Windows compatibility - ) as virtual_module_file: - virtual_module = Module.from_parent( - collector, path=Path(virtual_module_file.name) - ) - virtual_module_file.write( - dedent( - f"""\ - # This is a temporary file created by pytest-asyncio - # If you see this file, a pytest run has crashed and - # wasn't able to clean up the file in time. - # You can safely remove this file. - import asyncio - import pytest - from pytest_asyncio.plugin \ - import _temporary_event_loop_policy - @pytest.fixture( - scope="{collector_scope}", - name="{fixture_id}", - ) - def scoped_event_loop( - *args, - event_loop_policy, - ): - new_loop_policy = event_loop_policy - with _temporary_event_loop_policy(new_loop_policy): - loop = asyncio.new_event_loop() - loop.__pytest_asyncio = True - asyncio.set_event_loop(loop) - yield loop - loop.close() - """ - ).encode() - ) - virtual_module_file.flush() - fixturemanager = collector.config.pluginmanager.get_plugin( - "funcmanage" + # When collector is a Package, collector.obj is the package's + # __init__.py. Accessing the __init__.py to attach the fixture function + # may trigger additional module imports or change the order of imports, + # which leads to a number of problems. + # see https://github.com/pytest-dev/pytest-asyncio/issues/729 + # Moreover, Package.obj has been removed in pytest 8. + # Therefore, pytest-asyncio attaches the packages-scoped event loop + # fixture to the first collected module in that package. + package_scoped_loop_added = False + for subcollector in collector.__original_collect(): + if ( + not package_scoped_loop_added + and isinstance(subcollector, Module) + and getattr(subcollector, "obj", None) + ): + subcollector.obj.__pytest_asyncio_package_scoped_event_loop = ( + scoped_event_loop ) - # Collect the fixtures in the virtual module with the node ID of - # the current sub-package to ensure correct fixture matching. - # see also https://github.com/pytest-dev/pytest/issues/11662#issuecomment-1879310072 # noqa - fixturemanager.parsefactories(virtual_module.obj, nodeid=pkg_nodeid) - yield virtual_module - os.unlink(virtual_module_file.name) - yield from collector.__original_collect() + package_scoped_loop_added = True + yield subcollector collector.__original_collect = collector.collect collector.collect = _patched_collect From 463ce984677f0ac965f225c3b7ac14ebf43adad6 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Sun, 14 Jan 2024 19:53:35 +0100 Subject: [PATCH 16/20] [fix] Avoid trying to install scoped event loops for unknown test collector types. Signed-off-by: Michael Seifert --- docs/source/reference/changelog.rst | 1 + pytest_asyncio/plugin.py | 40 ++++++++++++++--------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/docs/source/reference/changelog.rst b/docs/source/reference/changelog.rst index dfcc7c05..2fde5a76 100644 --- a/docs/source/reference/changelog.rst +++ b/docs/source/reference/changelog.rst @@ -5,6 +5,7 @@ Changelog 0.23.4 (UNRELEASED) =================== - pytest-asyncio no longer imports additional, unrelated packages during test collection `#729 `_ +- Addresses further issues that caused an internal pytest error during test collection Known issues ------------ diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 5bf8d3b5..4729b267 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -608,21 +608,7 @@ def scoped_event_loop( # know it exists. We work around this by attaching the fixture function to the # collected Python object, where it will be picked up by pytest.Class.collect() # or pytest.Module.collect(), respectively - if type(collector) is Module: - # Accessing Module.obj triggers a module import executing module-level - # statements. A module-level pytest.skip statement raises the "Skipped" - # OutcomeException or a Collector.CollectError, if the "allow_module_level" - # kwargs is missing. These cases are handled correctly when they happen inside - # Collector.collect(), but this hook runs before the actual collect call. - # Therefore, we monkey patch Module.collect to add the scoped fixture to the - # module before it runs the actual collection. - def _patched_collect(): - collector.obj.__pytest_asyncio_scoped_event_loop = scoped_event_loop - return collector.__original_collect() - - collector.__original_collect = collector.collect - collector.collect = _patched_collect - elif type(collector) is Package: + if type(collector) is Package: def _patched_collect(): # When collector is a Package, collector.obj is the package's @@ -648,12 +634,24 @@ def _patched_collect(): collector.__original_collect = collector.collect collector.collect = _patched_collect - else: - pyobject = collector.obj - # If the collected module is a DoctestTextfile, collector.obj is None - if pyobject is None: - return - pyobject.__pytest_asyncio_scoped_event_loop = scoped_event_loop + elif isinstance(collector, Module): + # Accessing Module.obj triggers a module import executing module-level + # statements. A module-level pytest.skip statement raises the "Skipped" + # OutcomeException or a Collector.CollectError, if the "allow_module_level" + # kwargs is missing. These cases are handled correctly when they happen inside + # Collector.collect(), but this hook runs before the actual collect call. + # Therefore, we monkey patch Module.collect to add the scoped fixture to the + # module before it runs the actual collection. + def _patched_collect(): + # If the collected module is a DoctestTextfile, collector.obj is None + if collector.obj is not None: + collector.obj.__pytest_asyncio_scoped_event_loop = scoped_event_loop + return collector.__original_collect() + + collector.__original_collect = collector.collect + collector.collect = _patched_collect + elif isinstance(collector, Class): + collector.obj.__pytest_asyncio_scoped_event_loop = scoped_event_loop def _removesuffix(s: str, suffix: str) -> str: From dd129e9c4ce92cc8af50b82551e23ba2d857f8da Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Sun, 28 Jan 2024 19:44:04 +0100 Subject: [PATCH 17/20] [fix] Fixes a bug that prevented tests from being collected from package when any module inside the package used a pytest.skip statement. Signed-off-by: Michael Seifert --- pytest_asyncio/plugin.py | 44 ++++++++++++++++------------------------ tests/test_skips.py | 30 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 4729b267..c0147ffb 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -558,6 +558,10 @@ def pytest_pycollect_makeitem_convert_async_functions_to_subclass( Session: "session", } +# A stack used to push package-scoped loops during collection of a package +# and pop those loops during collection of a Module +__package_loop_stack: List[Union[FixtureFunctionMarker, FixtureFunction]] = [] + @pytest.hookimpl def pytest_collectstart(collector: pytest.Collector): @@ -609,31 +613,11 @@ def scoped_event_loop( # collected Python object, where it will be picked up by pytest.Class.collect() # or pytest.Module.collect(), respectively if type(collector) is Package: - - def _patched_collect(): - # When collector is a Package, collector.obj is the package's - # __init__.py. Accessing the __init__.py to attach the fixture function - # may trigger additional module imports or change the order of imports, - # which leads to a number of problems. - # see https://github.com/pytest-dev/pytest-asyncio/issues/729 - # Moreover, Package.obj has been removed in pytest 8. - # Therefore, pytest-asyncio attaches the packages-scoped event loop - # fixture to the first collected module in that package. - package_scoped_loop_added = False - for subcollector in collector.__original_collect(): - if ( - not package_scoped_loop_added - and isinstance(subcollector, Module) - and getattr(subcollector, "obj", None) - ): - subcollector.obj.__pytest_asyncio_package_scoped_event_loop = ( - scoped_event_loop - ) - package_scoped_loop_added = True - yield subcollector - - collector.__original_collect = collector.collect - collector.collect = _patched_collect + # Packages do not have a corresponding Python object. Therefore, the fixture + # for the package-scoped event loop is added to a stack. When a module inside + # the package is collected, the module will attach the fixture to its + # Python object. + __package_loop_stack.append(scoped_event_loop) elif isinstance(collector, Module): # Accessing Module.obj triggers a module import executing module-level # statements. A module-level pytest.skip statement raises the "Skipped" @@ -644,8 +628,14 @@ def _patched_collect(): # module before it runs the actual collection. def _patched_collect(): # If the collected module is a DoctestTextfile, collector.obj is None - if collector.obj is not None: - collector.obj.__pytest_asyncio_scoped_event_loop = scoped_event_loop + module = collector.obj + if module is not None: + module.__pytest_asyncio_scoped_event_loop = scoped_event_loop + try: + package_loop = __package_loop_stack.pop() + module.__pytest_asyncio_package_scoped_event_loop = package_loop + except IndexError: + pass return collector.__original_collect() collector.__original_collect = collector.collect diff --git a/tests/test_skips.py b/tests/test_skips.py index abd9dd70..5d7aa303 100644 --- a/tests/test_skips.py +++ b/tests/test_skips.py @@ -105,3 +105,33 @@ async def test_is_skipped(): ) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(skipped=1) + + +def test_skip_in_module_does_not_skip_package(pytester: Pytester): + pytester.makepyfile( + __init__="", + test_skip=dedent( + """\ + import pytest + + pytest.skip("Skip all tests", allow_module_level=True) + + def test_a(): + pass + + def test_b(): + pass + """ + ), + test_something=dedent( + """\ + import pytest + + @pytest.mark.asyncio + async def test_something(): + pass + """ + ), + ) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=1, skipped=1) From c51c3e7cebfb5ffdeed6922e89bbf5259c45c8e3 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Sun, 28 Jan 2024 19:45:55 +0100 Subject: [PATCH 18/20] [docs] Mention compatibility issue with pytest 8. Signed-off-by: Michael Seifert --- docs/source/reference/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/reference/changelog.rst b/docs/source/reference/changelog.rst index 2fde5a76..ae692775 100644 --- a/docs/source/reference/changelog.rst +++ b/docs/source/reference/changelog.rst @@ -6,6 +6,7 @@ Changelog =================== - pytest-asyncio no longer imports additional, unrelated packages during test collection `#729 `_ - Addresses further issues that caused an internal pytest error during test collection +- Declares incompatibility with pytest 8 `#737 `_ Known issues ------------ From a309bc509b6a2010adadedba76c2a3ea8ca46676 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Sun, 28 Jan 2024 19:46:43 +0100 Subject: [PATCH 19/20] [docs] Prepare release of v0.23.4. Signed-off-by: Michael Seifert --- docs/source/reference/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/reference/changelog.rst b/docs/source/reference/changelog.rst index ae692775..c0bdf5da 100644 --- a/docs/source/reference/changelog.rst +++ b/docs/source/reference/changelog.rst @@ -2,7 +2,7 @@ Changelog ========= -0.23.4 (UNRELEASED) +0.23.4 (2024-01-28) =================== - pytest-asyncio no longer imports additional, unrelated packages during test collection `#729 `_ - Addresses further issues that caused an internal pytest error during test collection From b879130e3212884fde45f2ecfe48b41ce1eacce3 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Sun, 28 Jan 2024 20:01:34 +0100 Subject: [PATCH 20/20] [chore] Updated shed pre-commit hook to v2024.1.1. Signed-off-by: Michael Seifert --- .pre-commit-config.yaml | 2 +- pytest_asyncio/__init__.py | 1 + pytest_asyncio/plugin.py | 13 ++++++------- tests/async_fixtures/test_async_fixtures_scope.py | 1 + tests/hypothesis/test_base.py | 1 + tests/loop_fixture_scope/test_loop_fixture_scope.py | 1 + tests/markers/test_class_scope.py | 1 + tests/test_simple.py | 1 + tests/test_subprocess.py | 1 + 9 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4e5d2f8e..ed7d0cb0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ repos: hooks: - id: yesqa - repo: https://github.com/Zac-HD/shed - rev: 0.10.7 + rev: 2024.1.1 hooks: - id: shed args: diff --git a/pytest_asyncio/__init__.py b/pytest_asyncio/__init__.py index 95046981..08dca478 100644 --- a/pytest_asyncio/__init__.py +++ b/pytest_asyncio/__init__.py @@ -1,4 +1,5 @@ """The main point for importing pytest-asyncio items.""" + from ._version import version as __version__ # noqa from .plugin import fixture, is_async_test diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index c0147ffb..b0744f0d 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -1,4 +1,5 @@ """pytest-asyncio implementation.""" + import asyncio import contextlib import enum @@ -115,8 +116,7 @@ def fixture( None, ] = ..., name: Optional[str] = ..., -) -> FixtureFunction: - ... +) -> FixtureFunction: ... @overload @@ -132,8 +132,7 @@ def fixture( None, ] = ..., name: Optional[str] = None, -) -> FixtureFunctionMarker: - ... +) -> FixtureFunctionMarker: ... def fixture( @@ -720,9 +719,9 @@ def pytest_generate_tests(metafunc: Metafunc) -> None: # The fixture needs to be appended to avoid messing up the fixture evaluation # order metafunc.fixturenames.append(event_loop_fixture_id) - metafunc._arg2fixturedefs[ - event_loop_fixture_id - ] = fixturemanager._arg2fixturedefs[event_loop_fixture_id] + metafunc._arg2fixturedefs[event_loop_fixture_id] = ( + fixturemanager._arg2fixturedefs[event_loop_fixture_id] + ) @pytest.hookimpl(hookwrapper=True) diff --git a/tests/async_fixtures/test_async_fixtures_scope.py b/tests/async_fixtures/test_async_fixtures_scope.py index 079a981a..a25934a8 100644 --- a/tests/async_fixtures/test_async_fixtures_scope.py +++ b/tests/async_fixtures/test_async_fixtures_scope.py @@ -2,6 +2,7 @@ We support module-scoped async fixtures, but only if the event loop is module-scoped too. """ + import asyncio import pytest diff --git a/tests/hypothesis/test_base.py b/tests/hypothesis/test_base.py index c2a7ea6a..fa12f2b3 100644 --- a/tests/hypothesis/test_base.py +++ b/tests/hypothesis/test_base.py @@ -1,6 +1,7 @@ """Tests for the Hypothesis integration, which wraps async functions in a sync shim for Hypothesis. """ + from textwrap import dedent import pytest diff --git a/tests/loop_fixture_scope/test_loop_fixture_scope.py b/tests/loop_fixture_scope/test_loop_fixture_scope.py index 679ab48f..eb4be8c9 100644 --- a/tests/loop_fixture_scope/test_loop_fixture_scope.py +++ b/tests/loop_fixture_scope/test_loop_fixture_scope.py @@ -1,4 +1,5 @@ """Unit tests for overriding the event loop with a larger scoped one.""" + import asyncio import pytest diff --git a/tests/markers/test_class_scope.py b/tests/markers/test_class_scope.py index fa2fe81e..9ec3ed9c 100644 --- a/tests/markers/test_class_scope.py +++ b/tests/markers/test_class_scope.py @@ -1,4 +1,5 @@ """Test if pytestmark works when defined on a class.""" + import asyncio from textwrap import dedent diff --git a/tests/test_simple.py b/tests/test_simple.py index 05c92694..f5f52a8d 100644 --- a/tests/test_simple.py +++ b/tests/test_simple.py @@ -1,4 +1,5 @@ """Quick'n'dirty unit tests for provided fixtures and markers.""" + import asyncio from textwrap import dedent diff --git a/tests/test_subprocess.py b/tests/test_subprocess.py index f60b2824..3d91e7b1 100644 --- a/tests/test_subprocess.py +++ b/tests/test_subprocess.py @@ -1,4 +1,5 @@ """Tests for using subprocesses in tests.""" + import asyncio.subprocess import sys