From 2e78fbe0291adf673c6c5f4e46e270a0993bda1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 18:09:23 +0000 Subject: [PATCH 01/72] Build(deps): Bump hypothesis in /dependencies/default Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.131.18 to 6.131.28. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.131.18...hypothesis-python-6.131.28) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.131.28 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 d8cf2319..fca899bc 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,7 +1,7 @@ attrs==25.3.0 coverage==7.8.0 exceptiongroup==1.3.0 -hypothesis==6.131.18 +hypothesis==6.131.28 iniconfig==2.1.0 packaging==25.0 pluggy==1.6.0 From 919839f5172113a5e2ede927f6ec946b1899efe0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 18:25:49 +0000 Subject: [PATCH 02/72] Build(deps): Bump coverage from 7.8.0 to 7.8.2 in /dependencies/default Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.8.0 to 7.8.2. - [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.8.0...7.8.2) --- updated-dependencies: - dependency-name: coverage dependency-version: 7.8.2 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 fca899bc..aadc4864 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,5 +1,5 @@ attrs==25.3.0 -coverage==7.8.0 +coverage==7.8.2 exceptiongroup==1.3.0 hypothesis==6.131.28 iniconfig==2.1.0 From bd741b82e22030732e42a92eedebdb133cc3be36 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 20:06:53 +0000 Subject: [PATCH 03/72] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.10 → v0.11.11](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.10...v0.11.11) - [github.com/tox-dev/pyproject-fmt: v2.5.1 → v2.6.0](https://github.com/tox-dev/pyproject-fmt/compare/v2.5.1...v2.6.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bd705265..a22cf898 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: check-merge-conflict exclude: rst$ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.10 + rev: v0.11.11 hooks: - id: ruff args: [--fix] @@ -69,7 +69,7 @@ repos: hooks: - id: check-github-actions - repo: https://github.com/tox-dev/pyproject-fmt - rev: v2.5.1 + rev: v2.6.0 hooks: - id: pyproject-fmt # https://pyproject-fmt.readthedocs.io/en/latest/#calculating-max-supported-python-version From 77b1ac4c6dfc2b5ba0d8f51c0124844f47ad2900 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 18:47:42 +0000 Subject: [PATCH 04/72] Build(deps): Bump hypothesis in /dependencies/default Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.131.28 to 6.133.0. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.131.28...hypothesis-python-6.133.0) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.133.0 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 aadc4864..147c143d 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,7 +1,7 @@ attrs==25.3.0 coverage==7.8.2 exceptiongroup==1.3.0 -hypothesis==6.131.28 +hypothesis==6.133.0 iniconfig==2.1.0 packaging==25.0 pluggy==1.6.0 From 146128c9d1f4a4f427900964503b0c43ad76ff29 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 20:19:40 +0000 Subject: [PATCH 05/72] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.11 → v0.11.12](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.11...v0.11.12) - [github.com/pre-commit/mirrors-mypy: v1.15.0 → v1.16.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.15.0...v1.16.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a22cf898..f4f98a48 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: check-merge-conflict exclude: rst$ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.11 + rev: v0.11.12 hooks: - id: ruff args: [--fix] @@ -42,7 +42,7 @@ repos: - id: check-yaml - id: debug-statements - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.15.0 + rev: v1.16.0 hooks: - id: mypy exclude: ^(docs|tests)/.* From efa185b595453c570babb70bdbd3522a7995f55d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 18:53:28 +0000 Subject: [PATCH 06/72] Build(deps): Bump requests from 2.32.3 to 2.32.4 in /dependencies/docs Bumps [requests](https://github.com/psf/requests) from 2.32.3 to 2.32.4. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.32.3...v2.32.4) --- updated-dependencies: - dependency-name: requests dependency-version: 2.32.4 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 06376f30..90bfa333 100644 --- a/dependencies/docs/constraints.txt +++ b/dependencies/docs/constraints.txt @@ -9,7 +9,7 @@ Jinja2==3.1.6 MarkupSafe==3.0.2 packaging==25.0 Pygments==2.19.1 -requests==2.32.3 +requests==2.32.4 snowballstemmer==3.0.1 Sphinx==8.0.2 sphinx-rtd-theme==3.0.2 From 9e3adcba052f6a01f62f85440e9ec0d50c2b5ab3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 18:23:06 +0000 Subject: [PATCH 07/72] Build(deps): Bump hypothesis in /dependencies/default Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.133.0 to 6.135.4. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.133.0...hypothesis-python-6.135.4) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.135.4 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 147c143d..479b9cdb 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,7 +1,7 @@ attrs==25.3.0 coverage==7.8.2 exceptiongroup==1.3.0 -hypothesis==6.133.0 +hypothesis==6.135.4 iniconfig==2.1.0 packaging==25.0 pluggy==1.6.0 From 5cedab634da682b2eccb34750bc6a6370eb2a515 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 20:04:17 +0000 Subject: [PATCH 08/72] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.12 → v0.11.13](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.12...v0.11.13) - [github.com/Zac-HD/shed: 2024.10.1 → 2025.6.1](https://github.com/Zac-HD/shed/compare/2024.10.1...2025.6.1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f4f98a48..0cf39def 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: check-merge-conflict exclude: rst$ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.12 + rev: v0.11.13 hooks: - id: ruff args: [--fix] @@ -15,7 +15,7 @@ repos: hooks: - id: yesqa - repo: https://github.com/Zac-HD/shed - rev: 2024.10.1 + rev: 2025.6.1 hooks: - id: shed args: From bd39bbbde419d58a26efcc80f5b230146ab5ca36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 19:44:49 +0000 Subject: [PATCH 09/72] Build(deps): Bump hypothesis in /dependencies/default Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.135.4 to 6.135.10. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.135.4...hypothesis-python-6.135.10) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.135.10 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 479b9cdb..3edefe72 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,7 +1,7 @@ attrs==25.3.0 coverage==7.8.2 exceptiongroup==1.3.0 -hypothesis==6.135.4 +hypothesis==6.135.10 iniconfig==2.1.0 packaging==25.0 pluggy==1.6.0 From 065774f1bd4388e213b3a8e82caabdfd0e6b7888 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 19:46:03 +0000 Subject: [PATCH 10/72] Build(deps): Bump typing-extensions in /dependencies/default Bumps [typing-extensions](https://github.com/python/typing_extensions) from 4.13.2 to 4.14.0. - [Release notes](https://github.com/python/typing_extensions/releases) - [Changelog](https://github.com/python/typing_extensions/blob/main/CHANGELOG.md) - [Commits](https://github.com/python/typing_extensions/compare/4.13.2...4.14.0) --- updated-dependencies: - dependency-name: typing-extensions dependency-version: 4.14.0 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 3edefe72..c8800b6a 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -8,4 +8,4 @@ pluggy==1.6.0 pytest==8.3.5 sortedcontainers==2.4.0 tomli==2.2.1 -typing_extensions==4.13.2 +typing_extensions==4.14.0 From 509b040d41967b7eb98ad89548e8504b609d91a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 19:56:11 +0000 Subject: [PATCH 11/72] Build(deps): Bump coverage from 7.8.2 to 7.9.1 in /dependencies/default Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.8.2 to 7.9.1. - [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.8.2...7.9.1) --- updated-dependencies: - dependency-name: coverage dependency-version: 7.9.1 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 c8800b6a..13a8ec58 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,5 +1,5 @@ attrs==25.3.0 -coverage==7.8.2 +coverage==7.9.1 exceptiongroup==1.3.0 hypothesis==6.135.10 iniconfig==2.1.0 From 85be4cab270716e60c5bed2692d0e101ad4f097a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 20:00:13 +0000 Subject: [PATCH 12/72] Build(deps): Bump certifi in /dependencies/docs Bumps [certifi](https://github.com/certifi/python-certifi) from 2025.4.26 to 2025.6.15. - [Commits](https://github.com/certifi/python-certifi/compare/2025.04.26...2025.06.15) --- updated-dependencies: - dependency-name: certifi dependency-version: 2025.6.15 dependency-type: direct:production update-type: version-update:semver-minor ... 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 90bfa333..f0672c3b 100644 --- a/dependencies/docs/constraints.txt +++ b/dependencies/docs/constraints.txt @@ -1,6 +1,6 @@ alabaster==0.7.16 Babel==2.17.0 -certifi==2025.4.26 +certifi==2025.6.15 charset-normalizer==3.4.2 docutils==0.21.2 idna==3.10 From 8c6612fda96f78a1df2f0d271426b7b6e3c10737 Mon Sep 17 00:00:00 2001 From: Yao Zi Date: Tue, 10 Jun 2025 16:17:11 +0000 Subject: [PATCH 13/72] test: Adapt unmarked async tests in strict mode for pytest 8.4.0 Async tests fail instead of skipping and warning with Pytest 8.4.0 if no suitable async plugin is installed[1]. Adjust expectation of these tests to pass the testsuite with Pytest 8.4.0. Link: https://docs.pytest.org/en/stable/changelog.html#pytest-8-4-0-2025-06-02 # [1] Signed-off-by: Yao Zi --- tests/modes/test_strict_mode.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/tests/modes/test_strict_mode.py b/tests/modes/test_strict_mode.py index 52cbb251..44f54b7d 100644 --- a/tests/modes/test_strict_mode.py +++ b/tests/modes/test_strict_mode.py @@ -2,7 +2,7 @@ from textwrap import dedent -from pytest import Pytester +from pytest import Pytester, version_tuple as pytest_version def test_strict_mode_cmdline(pytester: Pytester): @@ -95,7 +95,10 @@ async def test_anything(): ) ) result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(skipped=1, warnings=1) + if pytest_version >= (8, 4, 0): + result.assert_outcomes(failed=1, skipped=0, warnings=0) + else: + result.assert_outcomes(skipped=1, warnings=1) result.stdout.fnmatch_lines(["*async def functions are not natively supported*"]) @@ -117,7 +120,11 @@ async def test_anything(any_fixture): ) ) result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(skipped=1, warnings=2) + + if pytest_version >= (8, 4, 0): + result.assert_outcomes(failed=1, skipped=0, warnings=2) + else: + result.assert_outcomes(skipped=1, warnings=2) result.stdout.fnmatch_lines( [ "*async def functions are not natively supported*", @@ -149,7 +156,10 @@ async def test_anything(any_fixture): ) ) result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(passed=1, failed=0, skipped=0, warnings=1) + if pytest_version >= (8, 4, 0): + result.assert_outcomes(passed=1, failed=0, skipped=0, warnings=2) + else: + result.assert_outcomes(passed=1, failed=0, skipped=0, warnings=1) result.stdout.fnmatch_lines( [ "*warnings summary*", @@ -193,7 +203,10 @@ async def test_anything(any_fixture): ) ) result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(passed=1, warnings=1) + if pytest_version >= (8, 4, 0): + result.assert_outcomes(passed=1, warnings=2) + else: + result.assert_outcomes(passed=1, warnings=1) result.stdout.fnmatch_lines( [ "*warnings summary*", From 8db29a7d82210bbb6a89c94dc8400f0b942e6e2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 04:30:44 +0000 Subject: [PATCH 14/72] Build(deps): Bump pytest from 8.3.5 to 8.4.0 in /dependencies/default Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.3.5 to 8.4.0. - [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/8.3.5...8.4.0) --- updated-dependencies: - dependency-name: pytest dependency-version: 8.4.0 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 13a8ec58..0c90dbb9 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -5,7 +5,7 @@ hypothesis==6.135.10 iniconfig==2.1.0 packaging==25.0 pluggy==1.6.0 -pytest==8.3.5 +pytest==8.4.1 sortedcontainers==2.4.0 tomli==2.2.1 typing_extensions==4.14.0 From 2a04a0f59369e0a858acdf36c1deb2f4dd18bbba Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 20:16:29 +0000 Subject: [PATCH 15/72] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.13 → v0.12.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.13...v0.12.0) - [github.com/pre-commit/mirrors-mypy: v1.16.0 → v1.16.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.16.0...v1.16.1) - [github.com/sirosen/check-jsonschema: 0.33.0 → 0.33.1](https://github.com/sirosen/check-jsonschema/compare/0.33.0...0.33.1) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0cf39def..959d43ec 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: check-merge-conflict exclude: rst$ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.13 + rev: v0.12.0 hooks: - id: ruff args: [--fix] @@ -42,7 +42,7 @@ repos: - id: check-yaml - id: debug-statements - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.16.0 + rev: v1.16.1 hooks: - id: mypy exclude: ^(docs|tests)/.* @@ -65,7 +65,7 @@ repos: - 'SC1004:' stages: [manual] - repo: https://github.com/sirosen/check-jsonschema - rev: 0.33.0 + rev: 0.33.1 hooks: - id: check-github-actions - repo: https://github.com/tox-dev/pyproject-fmt From b45914952d852bf6ea98c313ac68e403dcbe170f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 20:51:29 +0000 Subject: [PATCH 16/72] Build(deps): Bump hypothesis in /dependencies/default Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.135.10 to 6.135.14. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.135.10...hypothesis-python-6.135.14) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.135.14 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 0c90dbb9..88575c6e 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,7 +1,7 @@ attrs==25.3.0 coverage==7.9.1 exceptiongroup==1.3.0 -hypothesis==6.135.10 +hypothesis==6.135.14 iniconfig==2.1.0 packaging==25.0 pluggy==1.6.0 From b63983efc7aa943b6a7d3b40cab544994caba3af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 20:10:33 +0000 Subject: [PATCH 17/72] Build(deps): Bump urllib3 from 2.4.0 to 2.5.0 in /dependencies/docs Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.4.0 to 2.5.0. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.4.0...2.5.0) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... 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 f0672c3b..2a0c3d28 100644 --- a/dependencies/docs/constraints.txt +++ b/dependencies/docs/constraints.txt @@ -20,4 +20,4 @@ sphinxcontrib-jquery==4.1 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==2.0.0 sphinxcontrib-serializinghtml==2.0.0 -urllib3==2.4.0 +urllib3==2.5.0 From 3591c247d9aac4709c892ff1a28f0c8e27b97de4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 20:03:27 +0000 Subject: [PATCH 18/72] Build(deps): Bump pygments from 2.19.1 to 2.19.2 in /dependencies/docs Bumps [pygments](https://github.com/pygments/pygments) from 2.19.1 to 2.19.2. - [Release notes](https://github.com/pygments/pygments/releases) - [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES) - [Commits](https://github.com/pygments/pygments/compare/2.19.1...2.19.2) --- updated-dependencies: - dependency-name: pygments dependency-version: 2.19.2 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 2a0c3d28..292c0f10 100644 --- a/dependencies/docs/constraints.txt +++ b/dependencies/docs/constraints.txt @@ -8,7 +8,7 @@ imagesize==1.4.1 Jinja2==3.1.6 MarkupSafe==3.0.2 packaging==25.0 -Pygments==2.19.1 +Pygments==2.19.2 requests==2.32.4 snowballstemmer==3.0.1 Sphinx==8.0.2 From d05365b247a09cafd36a27e1f4cfecb37e82971c Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 18 Jun 2025 08:51:54 +0200 Subject: [PATCH 19/72] refactor: _wrap_async_fixture returns a Callable rather than modifying the FixtureDef --- pytest_asyncio/plugin.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index aecf6e96..6e35f862 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -268,7 +268,7 @@ def _synchronize_async_fixture(fixturedef: FixtureDef) -> None: if inspect.isasyncgenfunction(fixturedef.func): _wrap_asyncgen_fixture(fixturedef) elif inspect.iscoroutinefunction(fixturedef.func): - _wrap_async_fixture(fixturedef) + fixturedef.func = _wrap_async_fixture(fixturedef) # type: ignore[misc] def _add_kwargs( @@ -346,12 +346,12 @@ async def async_finalizer() -> None: fixturedef.func = _asyncgen_fixture_wrapper # type: ignore[misc] -def _wrap_async_fixture(fixturedef: FixtureDef) -> None: - fixture = fixturedef.func +def _wrap_async_fixture(fixturedef: FixtureDef) -> Callable: + fixture_function = fixturedef.func - @functools.wraps(fixture) + @functools.wraps(fixture_function) def _async_fixture_wrapper(request: FixtureRequest, **kwargs: Any): - func = _perhaps_rebind_fixture_func(fixture, request.instance) + func = _perhaps_rebind_fixture_func(fixture_function, request.instance) event_loop_fixture_id = _get_event_loop_fixture_id_for_async_fixture( request, func ) @@ -380,7 +380,7 @@ async def setup(): return result - fixturedef.func = _async_fixture_wrapper # type: ignore[misc] + return _async_fixture_wrapper def _get_event_loop_fixture_id_for_async_fixture( From 06ef78e8c65d745ac0770778cc9745f61ce38251 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 18 Jun 2025 08:53:15 +0200 Subject: [PATCH 20/72] refactor: _wrap_asyncgen_fixture returns a Callable rather than modifying the FixtureDef --- pytest_asyncio/plugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 6e35f862..8e67b66c 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -266,7 +266,7 @@ def _preprocess_async_fixtures( def _synchronize_async_fixture(fixturedef: FixtureDef) -> None: """Wraps the fixture function of an async fixture in a synchronous function.""" if inspect.isasyncgenfunction(fixturedef.func): - _wrap_asyncgen_fixture(fixturedef) + fixturedef.func = _wrap_asyncgen_fixture(fixturedef) # type: ignore[misc] elif inspect.iscoroutinefunction(fixturedef.func): fixturedef.func = _wrap_async_fixture(fixturedef) # type: ignore[misc] @@ -299,7 +299,7 @@ def _perhaps_rebind_fixture_func(func: _T, instance: Any | None) -> _T: return func -def _wrap_asyncgen_fixture(fixturedef: FixtureDef) -> None: +def _wrap_asyncgen_fixture(fixturedef: FixtureDef) -> Callable: fixture = fixturedef.func @functools.wraps(fixture) @@ -343,7 +343,7 @@ async def async_finalizer() -> None: request.addfinalizer(finalizer) return result - fixturedef.func = _asyncgen_fixture_wrapper # type: ignore[misc] + return _asyncgen_fixture_wrapper def _wrap_async_fixture(fixturedef: FixtureDef) -> Callable: From 626d44b0f8bd01fdcee9ff1dd57b13dc2d0827af Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 18 Jun 2025 09:48:38 +0200 Subject: [PATCH 21/72] refactor: _wrap_async_fixture receives the Fixture function rather than the FixtureDef. --- pytest_asyncio/plugin.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 8e67b66c..aa60091e 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -21,6 +21,7 @@ Iterator, Sequence, ) +from types import CoroutineType from typing import ( Any, Callable, @@ -268,7 +269,7 @@ def _synchronize_async_fixture(fixturedef: FixtureDef) -> None: if inspect.isasyncgenfunction(fixturedef.func): fixturedef.func = _wrap_asyncgen_fixture(fixturedef) # type: ignore[misc] elif inspect.iscoroutinefunction(fixturedef.func): - fixturedef.func = _wrap_async_fixture(fixturedef) # type: ignore[misc] + fixturedef.func = _wrap_async_fixture(fixturedef.func) # type: ignore[misc] def _add_kwargs( @@ -346,10 +347,14 @@ async def async_finalizer() -> None: return _asyncgen_fixture_wrapper -def _wrap_async_fixture(fixturedef: FixtureDef) -> Callable: - fixture_function = fixturedef.func +AsyncFixtureReturnType = TypeVar("AsyncFixtureReturnType") - @functools.wraps(fixture_function) + +def _wrap_async_fixture( + fixture_function: Callable[..., CoroutineType[Any, Any, AsyncFixtureReturnType]], +) -> Callable[..., AsyncFixtureReturnType]: + + @functools.wraps(fixture_function) # type: ignore[arg-type] def _async_fixture_wrapper(request: FixtureRequest, **kwargs: Any): func = _perhaps_rebind_fixture_func(fixture_function, request.instance) event_loop_fixture_id = _get_event_loop_fixture_id_for_async_fixture( From 1a83fb042f20a926a2d185bb351e9fed0a09a5d4 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 18 Jun 2025 09:52:24 +0200 Subject: [PATCH 22/72] refactor: _wrap_async_fixture forwards positional args from the original fixture function to the wrapper. --- pytest_asyncio/plugin.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index aa60091e..85da35af 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -51,9 +51,9 @@ ) if sys.version_info >= (3, 10): - from typing import ParamSpec + from typing import Concatenate, ParamSpec else: - from typing_extensions import ParamSpec + from typing_extensions import Concatenate, ParamSpec _ScopeName = Literal["session", "package", "module", "class", "function"] @@ -347,15 +347,22 @@ async def async_finalizer() -> None: return _asyncgen_fixture_wrapper +AsyncFixtureParams = ParamSpec("AsyncFixtureParams") AsyncFixtureReturnType = TypeVar("AsyncFixtureReturnType") def _wrap_async_fixture( - fixture_function: Callable[..., CoroutineType[Any, Any, AsyncFixtureReturnType]], -) -> Callable[..., AsyncFixtureReturnType]: + fixture_function: Callable[ + AsyncFixtureParams, CoroutineType[Any, Any, AsyncFixtureReturnType] + ], +) -> Callable[Concatenate[FixtureRequest, AsyncFixtureParams], AsyncFixtureReturnType]: @functools.wraps(fixture_function) # type: ignore[arg-type] - def _async_fixture_wrapper(request: FixtureRequest, **kwargs: Any): + def _async_fixture_wrapper( + request: FixtureRequest, + *args: AsyncFixtureParams.args, + **kwargs: AsyncFixtureParams.kwargs, + ): func = _perhaps_rebind_fixture_func(fixture_function, request.instance) event_loop_fixture_id = _get_event_loop_fixture_id_for_async_fixture( request, func @@ -364,7 +371,7 @@ def _async_fixture_wrapper(request: FixtureRequest, **kwargs: Any): kwargs.pop(event_loop_fixture_id, None) async def setup(): - res = await func(**_add_kwargs(func, kwargs, request)) + res = await func(*args, **_add_kwargs(func, kwargs, request)) return res context = contextvars.copy_context() From ba324ef040c5a30216789149b755e642aa7fd7f6 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 18 Jun 2025 10:32:42 +0200 Subject: [PATCH 23/72] refactor: _wrap_asyncgen_fixture receives the fixture function rather than the FixtureDef. --- pytest_asyncio/plugin.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 85da35af..a9e961e0 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -21,7 +21,7 @@ Iterator, Sequence, ) -from types import CoroutineType +from types import AsyncGeneratorType, CoroutineType from typing import ( Any, Callable, @@ -267,7 +267,7 @@ def _preprocess_async_fixtures( def _synchronize_async_fixture(fixturedef: FixtureDef) -> None: """Wraps the fixture function of an async fixture in a synchronous function.""" if inspect.isasyncgenfunction(fixturedef.func): - fixturedef.func = _wrap_asyncgen_fixture(fixturedef) # type: ignore[misc] + fixturedef.func = _wrap_asyncgen_fixture(fixturedef.func) # type: ignore[misc] elif inspect.iscoroutinefunction(fixturedef.func): fixturedef.func = _wrap_async_fixture(fixturedef.func) # type: ignore[misc] @@ -300,12 +300,15 @@ def _perhaps_rebind_fixture_func(func: _T, instance: Any | None) -> _T: return func -def _wrap_asyncgen_fixture(fixturedef: FixtureDef) -> Callable: - fixture = fixturedef.func +AsyncGenFixtureYieldType = TypeVar("AsyncGenFixtureYieldType") - @functools.wraps(fixture) + +def _wrap_asyncgen_fixture( + fixture_function: Callable[..., AsyncGeneratorType[AsyncGenFixtureYieldType, Any]], +) -> Callable[..., AsyncGenFixtureYieldType]: + @functools.wraps(fixture_function) def _asyncgen_fixture_wrapper(request: FixtureRequest, **kwargs: Any): - func = _perhaps_rebind_fixture_func(fixture, request.instance) + func = _perhaps_rebind_fixture_func(fixture_function, request.instance) event_loop_fixture_id = _get_event_loop_fixture_id_for_async_fixture( request, func ) From 395c738264775b28fb33ac9def4c00bbb1763fca Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 18 Jun 2025 10:37:51 +0200 Subject: [PATCH 24/72] refactor: _wrap_asyncgen_fixture forwards positional args from the original fixture function to the wrapper. --- pytest_asyncio/plugin.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index a9e961e0..73370704 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -300,21 +300,30 @@ def _perhaps_rebind_fixture_func(func: _T, instance: Any | None) -> _T: return func +AsyncGenFixtureParams = ParamSpec("AsyncGenFixtureParams") AsyncGenFixtureYieldType = TypeVar("AsyncGenFixtureYieldType") def _wrap_asyncgen_fixture( - fixture_function: Callable[..., AsyncGeneratorType[AsyncGenFixtureYieldType, Any]], -) -> Callable[..., AsyncGenFixtureYieldType]: + fixture_function: Callable[ + AsyncGenFixtureParams, AsyncGeneratorType[AsyncGenFixtureYieldType, Any] + ], +) -> Callable[ + Concatenate[FixtureRequest, AsyncGenFixtureParams], AsyncGenFixtureYieldType +]: @functools.wraps(fixture_function) - def _asyncgen_fixture_wrapper(request: FixtureRequest, **kwargs: Any): + def _asyncgen_fixture_wrapper( + request: FixtureRequest, + *args: AsyncGenFixtureParams.args, + **kwargs: AsyncGenFixtureParams.kwargs, + ): func = _perhaps_rebind_fixture_func(fixture_function, request.instance) event_loop_fixture_id = _get_event_loop_fixture_id_for_async_fixture( request, func ) event_loop = request.getfixturevalue(event_loop_fixture_id) kwargs.pop(event_loop_fixture_id, None) - gen_obj = func(**_add_kwargs(func, kwargs, request)) + gen_obj = func(*args, **_add_kwargs(func, kwargs, request)) async def setup(): res = await gen_obj.__anext__() # type: ignore[union-attr] From 843233320b3495c567eb543c81425c9af1fa1498 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 18 Jun 2025 10:46:17 +0200 Subject: [PATCH 25/72] refactor: _synchronize_fixture returns a callable rather than modifying the fixture. --- pytest_asyncio/plugin.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 73370704..6b2a67d7 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -259,17 +259,19 @@ def _preprocess_async_fixtures( _make_asyncio_fixture_function(func, loop_scope) if "request" not in fixturedef.argnames: fixturedef.argnames += ("request",) - _synchronize_async_fixture(fixturedef) + fixturedef.func = _fixture_synchronizer(fixturedef) # type: ignore[misc] assert _is_asyncio_fixture_function(fixturedef.func) processed_fixturedefs.add(fixturedef) -def _synchronize_async_fixture(fixturedef: FixtureDef) -> None: - """Wraps the fixture function of an async fixture in a synchronous function.""" +def _fixture_synchronizer(fixturedef: FixtureDef) -> Callable: + """Returns a synchronous function evaluating the specified fixture.""" if inspect.isasyncgenfunction(fixturedef.func): - fixturedef.func = _wrap_asyncgen_fixture(fixturedef.func) # type: ignore[misc] + return _wrap_asyncgen_fixture(fixturedef.func) elif inspect.iscoroutinefunction(fixturedef.func): - fixturedef.func = _wrap_async_fixture(fixturedef.func) # type: ignore[misc] + return _wrap_async_fixture(fixturedef.func) + else: + return fixturedef.func def _add_kwargs( From 8b8e6e9d255362573915c45e30e719d15e476968 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 20 Jun 2025 07:42:03 +0200 Subject: [PATCH 26/72] refactor: Async fixtures are synchronized on demand rather than statically. Previously, async coroutines and async generators used as fixture functions were wrapped with a fixture synchronizer during collection time. This allowed fixture function to be run as synchronous functions. This patch changes the synchronization to occur during the pytest_fixture_setup hook. The synchronization is now temporary, which means the wrapper fixture function is restored after the fixture setup has finished. --- pytest_asyncio/plugin.py | 77 ++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 50 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 6b2a67d7..9ff2af71 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -36,7 +36,6 @@ import pytest from _pytest.scope import Scope from pytest import ( - Collector, Config, FixtureDef, FixtureRequest, @@ -44,6 +43,7 @@ Item, Mark, Metafunc, + MonkeyPatch, Parser, PytestCollectionWarning, PytestDeprecationWarning, @@ -231,39 +231,6 @@ def pytest_report_header(config: Config) -> list[str]: ] -def _preprocess_async_fixtures( - collector: Collector, - processed_fixturedefs: set[FixtureDef], -) -> None: - config = collector.config - default_loop_scope = config.getini("asyncio_default_fixture_loop_scope") - asyncio_mode = _get_asyncio_mode(config) - fixturemanager = config.pluginmanager.get_plugin("funcmanage") - assert fixturemanager is not None - for fixtures in fixturemanager._arg2fixturedefs.values(): - for fixturedef in fixtures: - func = fixturedef.func - if fixturedef in processed_fixturedefs or not _is_coroutine_or_asyncgen( - func - ): - continue - if asyncio_mode == Mode.STRICT and not _is_asyncio_fixture_function(func): - # Ignore async fixtures without explicit asyncio mark in strict mode - # This applies to pytest_trio fixtures, for example - continue - loop_scope = ( - getattr(func, "_loop_scope", None) - or default_loop_scope - or fixturedef.scope - ) - _make_asyncio_fixture_function(func, loop_scope) - if "request" not in fixturedef.argnames: - fixturedef.argnames += ("request",) - fixturedef.func = _fixture_synchronizer(fixturedef) # type: ignore[misc] - assert _is_asyncio_fixture_function(fixturedef.func) - processed_fixturedefs.add(fixturedef) - - def _fixture_synchronizer(fixturedef: FixtureDef) -> Callable: """Returns a synchronous function evaluating the specified fixture.""" if inspect.isasyncgenfunction(fixturedef.func): @@ -599,22 +566,6 @@ def runtest(self) -> None: super().runtest() -_HOLDER: set[FixtureDef] = set() - - -# The function name needs to start with "pytest_" -# see https://github.com/pytest-dev/pytest/issues/11307 -@pytest.hookimpl(specname="pytest_pycollect_makeitem", tryfirst=True) -def pytest_pycollect_makeitem_preprocess_async_fixtures( - collector: pytest.Module | pytest.Class, name: str, obj: object -) -> pytest.Item | pytest.Collector | list[pytest.Item | pytest.Collector] | None: - """A pytest hook to collect asyncio coroutines.""" - if not collector.funcnamefilter(name): - return None - _preprocess_async_fixtures(collector, _HOLDER) - return None - - # The function name needs to start with "pytest_" # see https://github.com/pytest-dev/pytest/issues/11307 @pytest.hookimpl(specname="pytest_pycollect_makeitem", hookwrapper=True) @@ -829,6 +780,32 @@ def pytest_runtest_setup(item: pytest.Item) -> None: ) +@pytest.hookimpl(wrapper=True) +def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: + asyncio_mode = _get_asyncio_mode(request.config) + if not _is_asyncio_fixture_function(fixturedef.func): + if asyncio_mode == Mode.STRICT: + # Ignore async fixtures without explicit asyncio mark in strict mode + # This applies to pytest_trio fixtures, for example + return (yield) + if not _is_coroutine_or_asyncgen(fixturedef.func): + return (yield) + default_loop_scope = request.config.getini("asyncio_default_fixture_loop_scope") + loop_scope = ( + getattr(fixturedef.func, "_loop_scope", None) + or default_loop_scope + or fixturedef.scope + ) + synchronizer = _fixture_synchronizer(fixturedef) + _make_asyncio_fixture_function(synchronizer, loop_scope) + with MonkeyPatch.context() as c: + if "request" not in fixturedef.argnames: + c.setattr(fixturedef, "argnames", (*fixturedef.argnames, "request")) + c.setattr(fixturedef, "func", synchronizer) + hook_result = yield + return hook_result + + _DUPLICATE_LOOP_SCOPE_DEFINITION_ERROR = """\ An asyncio pytest marker defines both "scope" and "loop_scope", \ but it should only use "loop_scope". From 25d42a814e6f6d349dfe17915b58bf7ab9bb425d Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 20 Jun 2025 07:55:27 +0200 Subject: [PATCH 27/72] refactor: Removed obsolete logic for removing the name of the loop fixture from a fixture wrapper's arguments. --- pytest_asyncio/plugin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 9ff2af71..2ef66d62 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -291,7 +291,6 @@ def _asyncgen_fixture_wrapper( request, func ) event_loop = request.getfixturevalue(event_loop_fixture_id) - kwargs.pop(event_loop_fixture_id, None) gen_obj = func(*args, **_add_kwargs(func, kwargs, request)) async def setup(): @@ -349,7 +348,6 @@ def _async_fixture_wrapper( request, func ) event_loop = request.getfixturevalue(event_loop_fixture_id) - kwargs.pop(event_loop_fixture_id, None) async def setup(): res = await func(*args, **_add_kwargs(func, kwargs, request)) From 6c9914b26c91f85e1717215985607e4593655836 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 20 Jun 2025 08:37:42 +0200 Subject: [PATCH 28/72] refactor: Fixture synchronizers rely on the caller to pass the correct event loop instance. --- pytest_asyncio/plugin.py | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 2ef66d62..5eadd110 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -28,7 +28,6 @@ Literal, TypeVar, Union, - cast, overload, ) @@ -231,12 +230,14 @@ def pytest_report_header(config: Config) -> list[str]: ] -def _fixture_synchronizer(fixturedef: FixtureDef) -> Callable: +def _fixture_synchronizer( + fixturedef: FixtureDef, event_loop: AbstractEventLoop +) -> Callable: """Returns a synchronous function evaluating the specified fixture.""" if inspect.isasyncgenfunction(fixturedef.func): - return _wrap_asyncgen_fixture(fixturedef.func) + return _wrap_asyncgen_fixture(fixturedef.func, event_loop) elif inspect.iscoroutinefunction(fixturedef.func): - return _wrap_async_fixture(fixturedef.func) + return _wrap_async_fixture(fixturedef.func, event_loop) else: return fixturedef.func @@ -277,6 +278,7 @@ def _wrap_asyncgen_fixture( fixture_function: Callable[ AsyncGenFixtureParams, AsyncGeneratorType[AsyncGenFixtureYieldType, Any] ], + event_loop: AbstractEventLoop, ) -> Callable[ Concatenate[FixtureRequest, AsyncGenFixtureParams], AsyncGenFixtureYieldType ]: @@ -287,10 +289,6 @@ def _asyncgen_fixture_wrapper( **kwargs: AsyncGenFixtureParams.kwargs, ): func = _perhaps_rebind_fixture_func(fixture_function, request.instance) - event_loop_fixture_id = _get_event_loop_fixture_id_for_async_fixture( - request, func - ) - event_loop = request.getfixturevalue(event_loop_fixture_id) gen_obj = func(*args, **_add_kwargs(func, kwargs, request)) async def setup(): @@ -335,6 +333,7 @@ def _wrap_async_fixture( fixture_function: Callable[ AsyncFixtureParams, CoroutineType[Any, Any, AsyncFixtureReturnType] ], + event_loop: AbstractEventLoop, ) -> Callable[Concatenate[FixtureRequest, AsyncFixtureParams], AsyncFixtureReturnType]: @functools.wraps(fixture_function) # type: ignore[arg-type] @@ -344,10 +343,6 @@ def _async_fixture_wrapper( **kwargs: AsyncFixtureParams.kwargs, ): func = _perhaps_rebind_fixture_func(fixture_function, request.instance) - event_loop_fixture_id = _get_event_loop_fixture_id_for_async_fixture( - request, func - ) - event_loop = request.getfixturevalue(event_loop_fixture_id) async def setup(): res = await func(*args, **_add_kwargs(func, kwargs, request)) @@ -374,18 +369,6 @@ async def setup(): return _async_fixture_wrapper -def _get_event_loop_fixture_id_for_async_fixture( - request: FixtureRequest, func: Any -) -> str: - default_loop_scope = cast( - _ScopeName, request.config.getini("asyncio_default_fixture_loop_scope") - ) - loop_scope = ( - getattr(func, "_loop_scope", None) or default_loop_scope or request.scope - ) - return f"_{loop_scope}_event_loop" - - def _create_task_in_context( loop: asyncio.AbstractEventLoop, coro: AbstractCoroutine[Any, Any, _T], @@ -794,7 +777,9 @@ def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: or default_loop_scope or fixturedef.scope ) - synchronizer = _fixture_synchronizer(fixturedef) + event_loop_fixture_id = f"_{loop_scope}_event_loop" + event_loop = request.getfixturevalue(event_loop_fixture_id) + synchronizer = _fixture_synchronizer(fixturedef, event_loop) _make_asyncio_fixture_function(synchronizer, loop_scope) with MonkeyPatch.context() as c: if "request" not in fixturedef.argnames: From ed6ae16bedad29ce4a6c94eb1badba4123626c77 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Thu, 15 May 2025 06:36:35 +0200 Subject: [PATCH 29/72] ci: Pin third-party actions to a commit hash. This detects changed action code for the same tag. --- .github/workflows/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 89d990ac..c30bbd42 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -102,7 +102,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Decide whether the needed jobs succeeded or failed - uses: re-actors/alls-green@release/v1 + uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 with: jobs: ${{ toJSON(needs) }} - uses: actions/checkout@v4 @@ -124,7 +124,7 @@ jobs: coverage combine coverage xml - name: Upload coverage report - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2 with: files: coverage.xml fail_ci_if_error: true @@ -184,7 +184,7 @@ jobs: run: | tree dist - name: PyPI upload - uses: pypa/gh-action-pypi-publish@v1.12.4 + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 with: attestations: true packages-dir: dist @@ -195,7 +195,7 @@ jobs: name: release-notes.md path: release-notes.md - name: GitHub Release - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1.16.0 with: name: pytest-asyncio ${{ needs.lint.outputs.version }} artifacts: dist/* From c4ba0defbf590ada63c525b5cb4a1b477ea55602 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Thu, 15 May 2025 06:41:28 +0200 Subject: [PATCH 30/72] ci: Avoid persisting credentials in the checkout action. see https://docs.zizmor.sh/audits/#artipacked --- .github/workflows/main.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c30bbd42..ca602d88 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,6 +24,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false - uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_LATEST }} @@ -75,6 +76,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} @@ -106,6 +109,8 @@ jobs: with: jobs: ${{ toJSON(needs) }} - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_LATEST }} @@ -139,6 +144,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false - name: Install Python uses: actions/setup-python@v5 - name: Install towncrier From 868d5137daa04e1e24bde35c56fff07cc9df35b7 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Thu, 15 May 2025 06:52:18 +0200 Subject: [PATCH 31/72] ci: Narrow permissions of Github Actions. see https://docs.zizmor.sh/audits/#excessive-permissions --- .github/workflows/main.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ca602d88..efe31827 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,6 +10,8 @@ on: merge_group: workflow_dispatch: +permissions: {} + env: PYTHON_LATEST: 3.13 @@ -180,6 +182,8 @@ jobs: if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') needs: [lint, check, prepare-release-notes] runs-on: ubuntu-latest + permissions: + id-token: write steps: - name: Download distributions uses: actions/download-artifact@v4 From 8b69a47f8f2fc1ce270194f96223b16cd9d8c769 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Thu, 15 May 2025 06:55:29 +0200 Subject: [PATCH 32/72] ci: Silence zizmore warning about not using trusted publishing. This is already tracked in #700 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index efe31827..06191c77 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -194,7 +194,7 @@ jobs: run: | tree dist - name: PyPI upload - uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 # zizmor: ignore[use-trusted-publishing] # see #700 with: attestations: true packages-dir: dist From 63b437ae977f63d0f1b2ae202f98fc5126ca166e Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Thu, 15 May 2025 06:59:18 +0200 Subject: [PATCH 33/72] ci: Avoid template expression in Bash script for assembling release notes. --- .github/workflows/main.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 06191c77..0d424beb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -158,7 +158,9 @@ jobs: run: pip install . - name: Compile Release Notes Draft if: ${{ !contains(github.ref, 'refs/tags/') }} - run: towncrier build --draft --version "${{ needs.lint.outputs.version }}" > release-notes.rst + run: towncrier build --draft --version "${version}" > release-notes.rst + env: + version: ${{ needs.lint.outputs.version }} - name: Extract release notes from Git tag if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') run: | From fe9bff3d552812253d69a53012770f98c1c04477 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Thu, 15 May 2025 06:59:27 +0200 Subject: [PATCH 34/72] build: Add zizmor to the pre-commit hooks. --- .pre-commit-config.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 959d43ec..38d06dd0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -74,6 +74,10 @@ repos: - id: pyproject-fmt # https://pyproject-fmt.readthedocs.io/en/latest/#calculating-max-supported-python-version additional_dependencies: [tox>=4.9] +- repo: https://github.com/zizmorcore/zizmor-pre-commit + rev: v1.7.0 + hooks: + - id: zizmor ci: skip: - actionlint-docker From d149e25febf217fc8945dd861718d3872e25ef31 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Thu, 15 May 2025 07:05:00 +0200 Subject: [PATCH 35/72] ci: Add linting job with GitHub Actions with zizmor. --- .github/workflows/main.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0d424beb..eae98219 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -100,6 +100,12 @@ jobs: path: coverage/coverage.* if-no-files-found: error + lint-github-actions: + name: Lint GitHub Actions + permissions: + security-events: write + uses: zizmorcore/workflow/.github/workflows/reusable-zizmor.yml@1ae473d8672fe7613e809d86d202a35063736e16 + check: name: Check if: always() From 72dda47203747ae1fdd9b6e329b67a5b83029f93 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 24 Jun 2025 06:06:51 +0200 Subject: [PATCH 36/72] ci: Update zizmor workflow. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index eae98219..5fea86c5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -104,7 +104,7 @@ jobs: name: Lint GitHub Actions permissions: security-events: write - uses: zizmorcore/workflow/.github/workflows/reusable-zizmor.yml@1ae473d8672fe7613e809d86d202a35063736e16 + uses: zizmorcore/workflow/.github/workflows/reusable-zizmor.yml@3bb5e95068d0f44b6d2f3f7e91379bed1d2f96a8 check: name: Check From 8cdf29e93908383a083a184afccf420030b28d5e Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 24 Jun 2025 06:05:21 +0200 Subject: [PATCH 37/72] ci: Remove obsolete password from PyPI upload. --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5fea86c5..05026310 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -206,7 +206,6 @@ jobs: with: attestations: true packages-dir: dist - password: ${{ secrets.PYPI_API_TOKEN }} - name: Download Release Notes uses: actions/download-artifact@v4 with: From 140ac5a95ed8782fb0d068c5aaff59b2c9864848 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 25 Jun 2025 18:31:27 +0200 Subject: [PATCH 38/72] ci: Remove unnecssary arguments and linting ignores from publishing action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) --- .github/workflows/main.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 05026310..7ab5726e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -202,10 +202,7 @@ jobs: run: | tree dist - name: PyPI upload - uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 # zizmor: ignore[use-trusted-publishing] # see #700 - with: - attestations: true - packages-dir: dist + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 - name: Download Release Notes uses: actions/download-artifact@v4 with: From 55a5a10a0411e97362f755fc62b55699afe84634 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 24 Jun 2025 09:50:53 +0200 Subject: [PATCH 39/72] build: Added dependency on backports.asyncio.runner for Python 3.10 and older. --- changelog.d/+6fe51a75.downstream.rst | 1 + pyproject.toml | 1 + 2 files changed, 2 insertions(+) create mode 100644 changelog.d/+6fe51a75.downstream.rst diff --git a/changelog.d/+6fe51a75.downstream.rst b/changelog.d/+6fe51a75.downstream.rst new file mode 100644 index 00000000..c42f9cf2 --- /dev/null +++ b/changelog.d/+6fe51a75.downstream.rst @@ -0,0 +1 @@ +Added runtime dependency on `backports.asyncio.runner `__ for use with Python 3.10 and older diff --git a/pyproject.toml b/pyproject.toml index b55bc8e6..7438fa78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ dynamic = [ ] dependencies = [ + "backports-asyncio-runner>=1.1,<2; python_version<'3.11'", "pytest>=8.2,<9", "typing-extensions>=4.12; python_version<'3.10'", ] From 78e93b8d91d8a9f8deec9a3242c764c5716a5033 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 24 Jun 2025 11:32:37 +0200 Subject: [PATCH 40/72] feat: Use asyncio.Runner to manage scoped event loops. Previously, pytest-asyncio used a combination of low-level asyncio functions to manage event loops. This patch replaces the low-level functions with the use of asyncio.Runner. The runner takes care of cancelling remaining tasks at the end of its scope, avoiding errors about pending tasks. This also fixes an issue that caused RuntimeErrors in abanoned tasks that called functions which depend on a running loop. Moreover, the use of asyncio.Runner allows backporting the propagation of contextvars from fixtures to tests to Python 3.9 and Python 3.10. --- changelog.d/+127.added.rst | 1 + changelog.d/+6aa3d3e0.added.rst | 1 + changelog.d/+878.fixed.rst | 1 + changelog.d/200.added.rst | 1 + pytest_asyncio/plugin.py | 95 ++++++++++++------- .../test_async_fixtures_contextvars.py | 32 ------- tests/test_event_loop_fixture.py | 13 ++- tests/test_task_cleanup.py | 30 ++++++ 8 files changed, 101 insertions(+), 73 deletions(-) create mode 100644 changelog.d/+127.added.rst create mode 100644 changelog.d/+6aa3d3e0.added.rst create mode 100644 changelog.d/+878.fixed.rst create mode 100644 changelog.d/200.added.rst create mode 100644 tests/test_task_cleanup.py diff --git a/changelog.d/+127.added.rst b/changelog.d/+127.added.rst new file mode 100644 index 00000000..e6402852 --- /dev/null +++ b/changelog.d/+127.added.rst @@ -0,0 +1 @@ +Propagation of ContextVars from async fixtures to other fixtures and tests on Python 3.10 and older diff --git a/changelog.d/+6aa3d3e0.added.rst b/changelog.d/+6aa3d3e0.added.rst new file mode 100644 index 00000000..7df0a155 --- /dev/null +++ b/changelog.d/+6aa3d3e0.added.rst @@ -0,0 +1 @@ +Warning when the current event loop is closed by a test diff --git a/changelog.d/+878.fixed.rst b/changelog.d/+878.fixed.rst new file mode 100644 index 00000000..4bdb5d72 --- /dev/null +++ b/changelog.d/+878.fixed.rst @@ -0,0 +1 @@ +Error about missing loop when calling functions requiring a loop in the `finally` clause of a task diff --git a/changelog.d/200.added.rst b/changelog.d/200.added.rst new file mode 100644 index 00000000..6c2cb39e --- /dev/null +++ b/changelog.d/200.added.rst @@ -0,0 +1 @@ +Cancellation of tasks when the `loop_scope` ends diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 5eadd110..587dca7a 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -10,6 +10,7 @@ import inspect import socket import sys +import traceback import warnings from asyncio import AbstractEventLoop, AbstractEventLoopPolicy from collections.abc import ( @@ -54,6 +55,10 @@ else: from typing_extensions import Concatenate, ParamSpec +if sys.version_info >= (3, 11): + from asyncio import Runner +else: + from backports.asyncio.runner import Runner _ScopeName = Literal["session", "package", "module", "class", "function"] _T = TypeVar("_T") @@ -230,14 +235,12 @@ def pytest_report_header(config: Config) -> list[str]: ] -def _fixture_synchronizer( - fixturedef: FixtureDef, event_loop: AbstractEventLoop -) -> Callable: +def _fixture_synchronizer(fixturedef: FixtureDef, runner: Runner) -> Callable: """Returns a synchronous function evaluating the specified fixture.""" if inspect.isasyncgenfunction(fixturedef.func): - return _wrap_asyncgen_fixture(fixturedef.func, event_loop) + return _wrap_asyncgen_fixture(fixturedef.func, runner) elif inspect.iscoroutinefunction(fixturedef.func): - return _wrap_async_fixture(fixturedef.func, event_loop) + return _wrap_async_fixture(fixturedef.func, runner) else: return fixturedef.func @@ -278,7 +281,7 @@ def _wrap_asyncgen_fixture( fixture_function: Callable[ AsyncGenFixtureParams, AsyncGeneratorType[AsyncGenFixtureYieldType, Any] ], - event_loop: AbstractEventLoop, + runner: Runner, ) -> Callable[ Concatenate[FixtureRequest, AsyncGenFixtureParams], AsyncGenFixtureYieldType ]: @@ -296,8 +299,7 @@ async def setup(): return res context = contextvars.copy_context() - setup_task = _create_task_in_context(event_loop, setup(), context) - result = event_loop.run_until_complete(setup_task) + result = runner.run(setup(), context=context) reset_contextvars = _apply_contextvar_changes(context) @@ -314,8 +316,7 @@ async def async_finalizer() -> None: msg += "Yield only once." raise ValueError(msg) - task = _create_task_in_context(event_loop, async_finalizer(), context) - event_loop.run_until_complete(task) + runner.run(async_finalizer(), context=context) if reset_contextvars is not None: reset_contextvars() @@ -333,7 +334,7 @@ def _wrap_async_fixture( fixture_function: Callable[ AsyncFixtureParams, CoroutineType[Any, Any, AsyncFixtureReturnType] ], - event_loop: AbstractEventLoop, + runner: Runner, ) -> Callable[Concatenate[FixtureRequest, AsyncFixtureParams], AsyncFixtureReturnType]: @functools.wraps(fixture_function) # type: ignore[arg-type] @@ -349,8 +350,7 @@ async def setup(): return res context = contextvars.copy_context() - setup_task = _create_task_in_context(event_loop, setup(), context) - result = event_loop.run_until_complete(setup_task) + result = runner.run(setup(), context=context) # Copy the context vars modified by the setup task into the current # context, and (if needed) add a finalizer to reset them. @@ -610,12 +610,12 @@ def pytest_generate_tests(metafunc: Metafunc) -> None: return default_loop_scope = _get_default_test_loop_scope(metafunc.config) loop_scope = _get_marked_loop_scope(marker, default_loop_scope) - event_loop_fixture_id = f"_{loop_scope}_event_loop" + runner_fixture_id = f"_{loop_scope}_scoped_runner" # This specific fixture name may already be in metafunc.argnames, if this # test indirectly depends on the fixture. For example, this is the case # when the test depends on an async fixture, both of which share the same # event loop fixture mark. - if event_loop_fixture_id in metafunc.fixturenames: + if runner_fixture_id in metafunc.fixturenames: return fixturemanager = metafunc.config.pluginmanager.get_plugin("funcmanage") assert fixturemanager is not None @@ -623,9 +623,9 @@ def pytest_generate_tests(metafunc: Metafunc) -> None: # fixturedefs and leave the actual parametrization to pytest # 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.fixturenames.append(runner_fixture_id) + metafunc._arg2fixturedefs[runner_fixture_id] = fixturemanager._arg2fixturedefs[ + runner_fixture_id ] @@ -747,10 +747,10 @@ def pytest_runtest_setup(item: pytest.Item) -> None: return default_loop_scope = _get_default_test_loop_scope(item.config) loop_scope = _get_marked_loop_scope(marker, default_loop_scope) - event_loop_fixture_id = f"_{loop_scope}_event_loop" + runner_fixture_id = f"_{loop_scope}_scoped_runner" fixturenames = item.fixturenames # type: ignore[attr-defined] - if event_loop_fixture_id not in fixturenames: - fixturenames.append(event_loop_fixture_id) + if runner_fixture_id not in fixturenames: + fixturenames.append(runner_fixture_id) obj = getattr(item, "obj", None) if not getattr(obj, "hypothesis", False) and getattr( obj, "is_hypothesis_test", False @@ -777,9 +777,9 @@ def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: or default_loop_scope or fixturedef.scope ) - event_loop_fixture_id = f"_{loop_scope}_event_loop" - event_loop = request.getfixturevalue(event_loop_fixture_id) - synchronizer = _fixture_synchronizer(fixturedef, event_loop) + runner_fixture_id = f"_{loop_scope}_scoped_runner" + runner = request.getfixturevalue(runner_fixture_id) + synchronizer = _fixture_synchronizer(fixturedef, runner) _make_asyncio_fixture_function(synchronizer, loop_scope) with MonkeyPatch.context() as c: if "request" not in fixturedef.argnames: @@ -825,28 +825,51 @@ def _get_default_test_loop_scope(config: Config) -> _ScopeName: return config.getini("asyncio_default_test_loop_scope") -def _create_scoped_event_loop_fixture(scope: _ScopeName) -> Callable: +_RUNNER_TEARDOWN_WARNING = """\ +An exception occurred during teardown of an asyncio.Runner. \ +The reason is likely that you closed the underlying event loop in a test, \ +which prevents the cleanup of asynchronous generators by the runner. +This warning will become an error in future versions of pytest-asyncio. \ +Please ensure that your tests don't close the event loop. \ +Here is the traceback of the exception triggered during teardown: +%s +""" + + +def _create_scoped_runner_fixture(scope: _ScopeName) -> Callable: @pytest.fixture( scope=scope, - name=f"_{scope}_event_loop", + name=f"_{scope}_scoped_runner", ) - def _scoped_event_loop( + def _scoped_runner( *args, # Function needs to accept "cls" when collected by pytest.Class event_loop_policy, - ) -> Iterator[asyncio.AbstractEventLoop]: + ) -> Iterator[Runner]: new_loop_policy = event_loop_policy - with ( - _temporary_event_loop_policy(new_loop_policy), - _provide_event_loop() as loop, - ): - _set_event_loop(loop) - yield loop + with _temporary_event_loop_policy(new_loop_policy): + runner = Runner().__enter__() + try: + yield runner + except Exception as e: + runner.__exit__(type(e), e, e.__traceback__) + else: + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", ".*BaseEventLoop.shutdown_asyncgens.*", RuntimeWarning + ) + try: + runner.__exit__(None, None, None) + except RuntimeError: + warnings.warn( + _RUNNER_TEARDOWN_WARNING % traceback.format_exc(), + RuntimeWarning, + ) - return _scoped_event_loop + return _scoped_runner for scope in Scope: - globals()[f"_{scope.value}_event_loop"] = _create_scoped_event_loop_fixture( + globals()[f"_{scope.value}_scoped_runner"] = _create_scoped_runner_fixture( scope.value ) diff --git a/tests/async_fixtures/test_async_fixtures_contextvars.py b/tests/async_fixtures/test_async_fixtures_contextvars.py index ff79e17e..20bad303 100644 --- a/tests/async_fixtures/test_async_fixtures_contextvars.py +++ b/tests/async_fixtures/test_async_fixtures_contextvars.py @@ -5,10 +5,8 @@ from __future__ import annotations -import sys from textwrap import dedent -import pytest from pytest import Pytester _prelude = dedent( @@ -56,11 +54,6 @@ async def test(check_var_fixture): result.assert_outcomes(passed=1) -@pytest.mark.xfail( - sys.version_info < (3, 11), - reason="requires asyncio Task context support", - strict=True, -) def test_var_from_async_generator_propagates_to_sync(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( @@ -86,11 +79,6 @@ async def test(check_var_fixture): result.assert_outcomes(passed=1) -@pytest.mark.xfail( - sys.version_info < (3, 11), - reason="requires asyncio Task context support", - strict=True, -) def test_var_from_async_fixture_propagates_to_sync(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( @@ -115,11 +103,6 @@ def test(check_var_fixture): result.assert_outcomes(passed=1) -@pytest.mark.xfail( - sys.version_info < (3, 11), - reason="requires asyncio Task context support", - strict=True, -) def test_var_from_generator_reset_before_previous_fixture_cleanup(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( @@ -149,11 +132,6 @@ async def test(var_fixture): result.assert_outcomes(passed=1) -@pytest.mark.xfail( - sys.version_info < (3, 11), - reason="requires asyncio Task context support", - strict=True, -) def test_var_from_fixture_reset_before_previous_fixture_cleanup(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( @@ -183,11 +161,6 @@ async def test(var_fixture): result.assert_outcomes(passed=1) -@pytest.mark.xfail( - sys.version_info < (3, 11), - reason="requires asyncio Task context support", - strict=True, -) def test_var_previous_value_restored_after_fixture(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( @@ -216,11 +189,6 @@ async def test(var_fixture_2): result.assert_outcomes(passed=1) -@pytest.mark.xfail( - sys.version_info < (3, 11), - reason="requires asyncio Task context support", - strict=True, -) def test_var_set_to_existing_value_ok(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( diff --git a/tests/test_event_loop_fixture.py b/tests/test_event_loop_fixture.py index 5417a14d..8b9ac634 100644 --- a/tests/test_event_loop_fixture.py +++ b/tests/test_event_loop_fixture.py @@ -82,7 +82,7 @@ async def generator_fn(): result.assert_outcomes(passed=1, warnings=0) -def test_event_loop_already_closed( +def test_closing_event_loop_in_sync_fixture_teardown_raises_warning( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") @@ -99,19 +99,22 @@ async def _event_loop(): return asyncio.get_running_loop() @pytest.fixture - def cleanup_after(_event_loop): + def close_event_loop(_event_loop): yield # fixture has its own cleanup code _event_loop.close() @pytest.mark.asyncio - async def test_something(cleanup_after): + async def test_something(close_event_loop): await asyncio.sleep(0.01) """ ) ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W", "default") - result.assert_outcomes(passed=1, warnings=0) + result = pytester.runpytest_subprocess("--asyncio-mode=strict") + result.assert_outcomes(passed=1, warnings=1) + result.stdout.fnmatch_lines( + ["*An exception occurred during teardown of an asyncio.Runner*"] + ) def test_event_loop_fixture_asyncgen_error( diff --git a/tests/test_task_cleanup.py b/tests/test_task_cleanup.py new file mode 100644 index 00000000..eb1f7d3c --- /dev/null +++ b/tests/test_task_cleanup.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from textwrap import dedent + +from pytest import Pytester + + +def test_task_is_cancelled_when_abandoned_by_test(pytester: Pytester): + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makepyfile( + dedent( + """\ + import asyncio + import pytest + + @pytest.mark.asyncio + async def test_create_task(): + async def coroutine(): + try: + while True: + await asyncio.sleep(0) + finally: + raise RuntimeError("The task should be cancelled at this point.") + + asyncio.create_task(coroutine()) + """ + ) + ) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=1) From 0f6f6dcad79ac354bce62cc65c3128168bb10f8c Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 24 Jun 2025 11:34:26 +0200 Subject: [PATCH 41/72] refactor: Removed obsolete function _provide_event_loop --- pytest_asyncio/plugin.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 587dca7a..6e542b22 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -874,23 +874,6 @@ def _scoped_runner( ) -@contextlib.contextmanager -def _provide_event_loop() -> Iterator[asyncio.AbstractEventLoop]: - policy = _get_event_loop_policy() - loop = policy.new_event_loop() - try: - yield loop - finally: - # cleanup the event loop if it hasn't been cleaned up already - if not loop.is_closed(): - try: - loop.run_until_complete(loop.shutdown_asyncgens()) - except Exception as e: - warnings.warn(f"Error cleaning up asyncio loop: {e}", RuntimeWarning) - finally: - loop.close() - - @pytest.fixture(scope="session", autouse=True) def event_loop_policy() -> AbstractEventLoopPolicy: """Return an instance of the policy used to create asyncio event loops.""" From 4c248ab8d7c477dc81c6daabd179c92714a5b3de Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 24 Jun 2025 11:35:35 +0200 Subject: [PATCH 42/72] refactor: Removed obsolete function _create_task_in_context --- pytest_asyncio/plugin.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 6e542b22..b92c762f 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -16,7 +16,6 @@ from collections.abc import ( AsyncIterator, Awaitable, - Coroutine as AbstractCoroutine, Generator, Iterable, Iterator, @@ -369,29 +368,6 @@ async def setup(): return _async_fixture_wrapper -def _create_task_in_context( - loop: asyncio.AbstractEventLoop, - coro: AbstractCoroutine[Any, Any, _T], - context: contextvars.Context, -) -> asyncio.Task[_T]: - """ - Return an asyncio task that runs the coro in the specified context, - if possible. - - This allows fixture setup and teardown to be run as separate asyncio tasks, - while still being able to use context-manager idioms to maintain context - variables and make those variables visible to test functions. - - This is only fully supported on Python 3.11 and newer, as it requires - the API added for https://github.com/python/cpython/issues/91150. - On earlier versions, the returned task will use the default context instead. - """ - try: - return loop.create_task(coro, context=context) - except TypeError: - return loop.create_task(coro) - - def _apply_contextvar_changes( context: contextvars.Context, ) -> Callable[[], None] | None: From f9c5d675fa4e63696bd19406bad9f0ad6ea0723b Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 24 Jun 2025 11:53:01 +0200 Subject: [PATCH 43/72] refactor: Removed obsolete argument to scoped runner. --- pytest_asyncio/plugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index b92c762f..7383c643 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -818,7 +818,6 @@ def _create_scoped_runner_fixture(scope: _ScopeName) -> Callable: name=f"_{scope}_scoped_runner", ) def _scoped_runner( - *args, # Function needs to accept "cls" when collected by pytest.Class event_loop_policy, ) -> Iterator[Runner]: new_loop_policy = event_loop_policy From 8dee421a4d2c13ec293086afdf572e4cde495d89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Jun 2025 18:30:40 +0000 Subject: [PATCH 44/72] Build(deps): Bump codecov/codecov-action from 5.4.2 to 5.4.3 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.4.2 to 5.4.3. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/ad3126e916f78f00edff4ed0317cf185271ccc2d...18283e04ce6e62d37312384ff67231eb8fd56d24) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: 5.4.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7ab5726e..39a22fa2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -137,7 +137,7 @@ jobs: coverage combine coverage xml - name: Upload coverage report - uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2 + uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 with: files: coverage.xml fail_ci_if_error: true From e9965b562e681cd9a9cefc30ddd96c513587ce1c Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 24 Jun 2025 14:22:11 +0200 Subject: [PATCH 45/72] refactor: Synchronization wrappers of test items are temporary. Previously, synchronizers modified the obj attribute of a function item permanently. This could possibly result in multiple levels of wrapping. This patch restores the original function object after the test finished. --- pytest_asyncio/plugin.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 7383c643..77beb892 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -451,11 +451,10 @@ def _can_substitute(item: Function) -> bool: return inspect.iscoroutinefunction(func) def runtest(self) -> None: - self.obj = wrap_in_sync( - # https://github.com/pytest-dev/pytest-asyncio/issues/596 - self.obj, # type: ignore[has-type] - ) - super().runtest() + synchronized_obj = wrap_in_sync(self.obj) + with MonkeyPatch.context() as c: + c.setattr(self, "obj", synchronized_obj) + super().runtest() class AsyncGenerator(PytestAsyncioFunction): @@ -494,11 +493,10 @@ def _can_substitute(item: Function) -> bool: ) def runtest(self) -> None: - self.obj = wrap_in_sync( - # https://github.com/pytest-dev/pytest-asyncio/issues/596 - self.obj, # type: ignore[has-type] - ) - super().runtest() + synchronized_obj = wrap_in_sync(self.obj) + with MonkeyPatch.context() as c: + c.setattr(self, "obj", synchronized_obj) + super().runtest() class AsyncHypothesisTest(PytestAsyncioFunction): @@ -517,10 +515,10 @@ def _can_substitute(item: Function) -> bool: ) def runtest(self) -> None: - self.obj.hypothesis.inner_test = wrap_in_sync( - self.obj.hypothesis.inner_test, - ) - super().runtest() + synchronized_obj = wrap_in_sync(self.obj.hypothesis.inner_test) + with MonkeyPatch.context() as c: + c.setattr(self.obj.hypothesis, "inner_test", synchronized_obj) + super().runtest() # The function name needs to start with "pytest_" From b3e21666a8b85d869476732f8114bf3083894041 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 24 Jun 2025 14:23:54 +0200 Subject: [PATCH 46/72] refactor: Remove obsolete logic to prevent double wrapping in wrap_in_sync. --- pytest_asyncio/plugin.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 77beb892..556badaf 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -689,12 +689,6 @@ def wrap_in_sync( Return a sync wrapper around an async function executing it in the current event loop. """ - # if the function is already wrapped, we rewrap using the original one - # not using __wrapped__ because the original function may already be - # a wrapped one - raw_func = getattr(func, "_raw_test_func", None) - if raw_func is not None: - func = raw_func @functools.wraps(func) def inner(*args, **kwargs): @@ -711,7 +705,6 @@ def inner(*args, **kwargs): task.exception() raise - inner._raw_test_func = func # type: ignore[attr-defined] return inner From b47cfa43cfae47d0aec40cdb9be335a121e6a429 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 24 Jun 2025 15:17:17 +0200 Subject: [PATCH 47/72] refactor: Replace custom fixture func rebinding with pytest function --- pytest_asyncio/plugin.py | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 556badaf..cffb6920 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -33,6 +33,7 @@ import pluggy import pytest +from _pytest.fixtures import resolve_fixture_function from _pytest.scope import Scope from pytest import ( Config, @@ -60,7 +61,6 @@ from backports.asyncio.runner import Runner _ScopeName = Literal["session", "package", "module", "class", "function"] -_T = TypeVar("_T") _R = TypeVar("_R", bound=Union[Awaitable[Any], AsyncIterator[Any]]) _P = ParamSpec("_P") FixtureFunction = Callable[_P, _R] @@ -234,12 +234,15 @@ def pytest_report_header(config: Config) -> list[str]: ] -def _fixture_synchronizer(fixturedef: FixtureDef, runner: Runner) -> Callable: +def _fixture_synchronizer( + fixturedef: FixtureDef, runner: Runner, request: FixtureRequest +) -> Callable: """Returns a synchronous function evaluating the specified fixture.""" + fixture_function = resolve_fixture_function(fixturedef, request) if inspect.isasyncgenfunction(fixturedef.func): - return _wrap_asyncgen_fixture(fixturedef.func, runner) + return _wrap_asyncgen_fixture(fixture_function, runner) # type: ignore[arg-type] elif inspect.iscoroutinefunction(fixturedef.func): - return _wrap_async_fixture(fixturedef.func, runner) + return _wrap_async_fixture(fixture_function, runner) # type: ignore[arg-type] else: return fixturedef.func @@ -256,22 +259,6 @@ def _add_kwargs( return ret -def _perhaps_rebind_fixture_func(func: _T, instance: Any | None) -> _T: - if instance is not None: - # The fixture needs to be bound to the actual request.instance - # so it is bound to the same object as the test method. - unbound, cls = func, None - try: - unbound, cls = func.__func__, type(func.__self__) # type: ignore - except AttributeError: - pass - # Only if the fixture was bound before to an instance of - # the same type. - if cls is not None and isinstance(instance, cls): - func = unbound.__get__(instance) # type: ignore - return func - - AsyncGenFixtureParams = ParamSpec("AsyncGenFixtureParams") AsyncGenFixtureYieldType = TypeVar("AsyncGenFixtureYieldType") @@ -290,8 +277,9 @@ def _asyncgen_fixture_wrapper( *args: AsyncGenFixtureParams.args, **kwargs: AsyncGenFixtureParams.kwargs, ): - func = _perhaps_rebind_fixture_func(fixture_function, request.instance) - gen_obj = func(*args, **_add_kwargs(func, kwargs, request)) + gen_obj = fixture_function( + *args, **_add_kwargs(fixture_function, kwargs, request) + ) async def setup(): res = await gen_obj.__anext__() # type: ignore[union-attr] @@ -342,10 +330,10 @@ def _async_fixture_wrapper( *args: AsyncFixtureParams.args, **kwargs: AsyncFixtureParams.kwargs, ): - func = _perhaps_rebind_fixture_func(fixture_function, request.instance) - async def setup(): - res = await func(*args, **_add_kwargs(func, kwargs, request)) + res = await fixture_function( + *args, **_add_kwargs(fixture_function, kwargs, request) + ) return res context = contextvars.copy_context() @@ -746,7 +734,7 @@ def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: ) runner_fixture_id = f"_{loop_scope}_scoped_runner" runner = request.getfixturevalue(runner_fixture_id) - synchronizer = _fixture_synchronizer(fixturedef, runner) + synchronizer = _fixture_synchronizer(fixturedef, runner, request) _make_asyncio_fixture_function(synchronizer, loop_scope) with MonkeyPatch.context() as c: if "request" not in fixturedef.argnames: From cf3cd4c0d245fef6b1c9a234e07b578286b2cfca Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 24 Jun 2025 15:31:19 +0200 Subject: [PATCH 48/72] refactor: Forward fixture request into synchronizers instead of modifying fixture argnames. --- pytest_asyncio/plugin.py | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index cffb6920..4dba7a2b 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -51,9 +51,9 @@ ) if sys.version_info >= (3, 10): - from typing import Concatenate, ParamSpec + from typing import ParamSpec else: - from typing_extensions import Concatenate, ParamSpec + from typing_extensions import ParamSpec if sys.version_info >= (3, 11): from asyncio import Runner @@ -240,9 +240,9 @@ def _fixture_synchronizer( """Returns a synchronous function evaluating the specified fixture.""" fixture_function = resolve_fixture_function(fixturedef, request) if inspect.isasyncgenfunction(fixturedef.func): - return _wrap_asyncgen_fixture(fixture_function, runner) # type: ignore[arg-type] + return _wrap_asyncgen_fixture(fixture_function, runner, request) # type: ignore[arg-type] elif inspect.iscoroutinefunction(fixturedef.func): - return _wrap_async_fixture(fixture_function, runner) # type: ignore[arg-type] + return _wrap_async_fixture(fixture_function, runner, request) # type: ignore[arg-type] else: return fixturedef.func @@ -268,18 +268,14 @@ def _wrap_asyncgen_fixture( AsyncGenFixtureParams, AsyncGeneratorType[AsyncGenFixtureYieldType, Any] ], runner: Runner, -) -> Callable[ - Concatenate[FixtureRequest, AsyncGenFixtureParams], AsyncGenFixtureYieldType -]: + request: FixtureRequest, +) -> Callable[AsyncGenFixtureParams, AsyncGenFixtureYieldType]: @functools.wraps(fixture_function) def _asyncgen_fixture_wrapper( - request: FixtureRequest, *args: AsyncGenFixtureParams.args, **kwargs: AsyncGenFixtureParams.kwargs, ): - gen_obj = fixture_function( - *args, **_add_kwargs(fixture_function, kwargs, request) - ) + gen_obj = fixture_function(*args, **kwargs) async def setup(): res = await gen_obj.__anext__() # type: ignore[union-attr] @@ -322,18 +318,16 @@ def _wrap_async_fixture( AsyncFixtureParams, CoroutineType[Any, Any, AsyncFixtureReturnType] ], runner: Runner, -) -> Callable[Concatenate[FixtureRequest, AsyncFixtureParams], AsyncFixtureReturnType]: + request: FixtureRequest, +) -> Callable[AsyncFixtureParams, AsyncFixtureReturnType]: @functools.wraps(fixture_function) # type: ignore[arg-type] def _async_fixture_wrapper( - request: FixtureRequest, *args: AsyncFixtureParams.args, **kwargs: AsyncFixtureParams.kwargs, ): async def setup(): - res = await fixture_function( - *args, **_add_kwargs(fixture_function, kwargs, request) - ) + res = await fixture_function(*args, **kwargs) return res context = contextvars.copy_context() @@ -737,8 +731,6 @@ def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: synchronizer = _fixture_synchronizer(fixturedef, runner, request) _make_asyncio_fixture_function(synchronizer, loop_scope) with MonkeyPatch.context() as c: - if "request" not in fixturedef.argnames: - c.setattr(fixturedef, "argnames", (*fixturedef.argnames, "request")) c.setattr(fixturedef, "func", synchronizer) hook_result = yield return hook_result From d1c68f859cb9f45299e1f1ef960e3b2ccbc47c2c Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 24 Jun 2025 15:48:38 +0200 Subject: [PATCH 49/72] refactor: Remove obsolete function _add_kwargs --- pytest_asyncio/plugin.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 4dba7a2b..e216d075 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -247,18 +247,6 @@ def _fixture_synchronizer( return fixturedef.func -def _add_kwargs( - func: Callable[..., Any], - kwargs: dict[str, Any], - request: FixtureRequest, -) -> dict[str, Any]: - sig = inspect.signature(func) - ret = kwargs.copy() - if "request" in sig.parameters: - ret["request"] = request - return ret - - AsyncGenFixtureParams = ParamSpec("AsyncGenFixtureParams") AsyncGenFixtureYieldType = TypeVar("AsyncGenFixtureYieldType") From b6deb15f94e02f34208144b775e50dc5123a9d66 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 24 Jun 2025 16:05:20 +0200 Subject: [PATCH 50/72] fix: Remove pytest_generate_tests. This also fixes an issue that could cause the warning for the deprecated *scope* argument to the asyncio marker to be reported multiple times. --- changelog.d/+b22c903a.fixed.rst | 1 + pytest_asyncio/plugin.py | 27 --------------------------- tests/markers/test_function_scope.py | 2 +- 3 files changed, 2 insertions(+), 28 deletions(-) create mode 100644 changelog.d/+b22c903a.fixed.rst diff --git a/changelog.d/+b22c903a.fixed.rst b/changelog.d/+b22c903a.fixed.rst new file mode 100644 index 00000000..5af4f6e7 --- /dev/null +++ b/changelog.d/+b22c903a.fixed.rst @@ -0,0 +1 @@ +An error that could cause duplicate warnings to be issued diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index e216d075..9bfcfc64 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -42,7 +42,6 @@ Function, Item, Mark, - Metafunc, MonkeyPatch, Parser, PytestCollectionWarning, @@ -547,32 +546,6 @@ def _temporary_event_loop_policy(policy: AbstractEventLoopPolicy) -> Iterator[No _set_event_loop(old_loop) -@pytest.hookimpl(tryfirst=True) -def pytest_generate_tests(metafunc: Metafunc) -> None: - marker = metafunc.definition.get_closest_marker("asyncio") - if not marker: - return - default_loop_scope = _get_default_test_loop_scope(metafunc.config) - loop_scope = _get_marked_loop_scope(marker, default_loop_scope) - runner_fixture_id = f"_{loop_scope}_scoped_runner" - # This specific fixture name may already be in metafunc.argnames, if this - # test indirectly depends on the fixture. For example, this is the case - # when the test depends on an async fixture, both of which share the same - # event loop fixture mark. - if runner_fixture_id in metafunc.fixturenames: - return - fixturemanager = metafunc.config.pluginmanager.get_plugin("funcmanage") - assert fixturemanager is not None - # Add the scoped event loop fixture to Metafunc's list of fixture names and - # fixturedefs and leave the actual parametrization to pytest - # The fixture needs to be appended to avoid messing up the fixture evaluation - # order - metafunc.fixturenames.append(runner_fixture_id) - metafunc._arg2fixturedefs[runner_fixture_id] = fixturemanager._arg2fixturedefs[ - runner_fixture_id - ] - - def _get_event_loop_policy() -> AbstractEventLoopPolicy: with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) diff --git a/tests/markers/test_function_scope.py b/tests/markers/test_function_scope.py index f750ba58..feb6bae3 100644 --- a/tests/markers/test_function_scope.py +++ b/tests/markers/test_function_scope.py @@ -88,7 +88,7 @@ async def test_warns(): ) ) result = pytester.runpytest_subprocess("--asyncio-mode=strict") - result.assert_outcomes(passed=1, warnings=2) + result.assert_outcomes(passed=1, warnings=1) result.stdout.fnmatch_lines("*DeprecationWarning*") From 554e84df8618c28d2478e723723f5ef964a8ce95 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 25 Jun 2025 19:23:02 +0200 Subject: [PATCH 51/72] ci: Add missing "contents: write" permission for the GitHub Release step. Fixes #1127 --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 39a22fa2..072aea32 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -191,6 +191,7 @@ jobs: needs: [lint, check, prepare-release-notes] runs-on: ubuntu-latest permissions: + contents: write id-token: write steps: - name: Download distributions From 154ea14e956575dcdedb4f324ef47431439d6863 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 25 Jun 2025 19:47:57 +0200 Subject: [PATCH 52/72] ci: Publish to test.pypi.org on every commit against main. --- .github/workflows/main.yml | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 072aea32..841188fc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -183,10 +183,28 @@ jobs: name: release-notes.md path: release-notes.md - deploy: - name: Deploy + publish-test-pypi: + name: Publish packages to test.pypi.org + environment: release + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: [lint, check] + runs-on: ubuntu-latest + permissions: + id-token: write + steps: + - name: Download distributions + uses: actions/download-artifact@v4 + with: + name: dist + path: dist + - name: Upload to test.pypi.org + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 + with: + repository-url: https://test.pypi.org/legacy/ + + publish-pypi: + name: Publish packages to pypi.org environment: release - # Run only on pushing a tag if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') needs: [lint, check, prepare-release-notes] runs-on: ubuntu-latest From d523340f265aaf81faa49b16eeb2198bf7f1fbf6 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 25 Jun 2025 19:56:13 +0200 Subject: [PATCH 53/72] ci: Move step for creating a GitHub release to the job that creates the release notes. This addresses an issue with the step having elevated rights. --- .github/workflows/main.yml | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 841188fc..1a2a3b69 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -143,10 +143,12 @@ jobs: fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} - prepare-release-notes: - name: Prepare Release Notes - needs: [lint] + create-github-release: + name: Create GitHub release + needs: [lint, check] runs-on: ubuntu-latest + permissions: + contents: write steps: - name: Checkout uses: actions/checkout@v4 @@ -182,6 +184,20 @@ jobs: with: name: release-notes.md path: release-notes.md + - name: Download distributions + uses: actions/download-artifact@v4 + with: + name: dist + path: dist + - name: Create GitHub Release + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1.16.0 + with: + name: pytest-asyncio ${{ needs.lint.outputs.version }} + artifacts: dist/* + bodyFile: release-notes.md + prerelease: ${{ needs.lint.outputs.prerelease }} + token: ${{ secrets.GITHUB_TOKEN }} publish-test-pypi: name: Publish packages to test.pypi.org @@ -206,10 +222,9 @@ jobs: name: Publish packages to pypi.org environment: release if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - needs: [lint, check, prepare-release-notes] + needs: [lint, check] runs-on: ubuntu-latest permissions: - contents: write id-token: write steps: - name: Download distributions @@ -222,16 +237,3 @@ jobs: tree dist - name: PyPI upload uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 - - name: Download Release Notes - uses: actions/download-artifact@v4 - with: - name: release-notes.md - path: release-notes.md - - name: GitHub Release - uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1.16.0 - with: - name: pytest-asyncio ${{ needs.lint.outputs.version }} - artifacts: dist/* - bodyFile: release-notes.md - prerelease: ${{ needs.lint.outputs.prerelease }} - token: ${{ secrets.GITHUB_TOKEN }} From 419c98268f0026b0deb7e079be8472dc773796db Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Mon, 30 Jun 2025 06:30:02 +0200 Subject: [PATCH 54/72] build: Create development versions without local version identifier. This allows upload of development versions to test.pypi.org. --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 7438fa78..8a522ecf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,6 +64,7 @@ include-package-data = true [tool.setuptools_scm] write_to = "pytest_asyncio/_version.py" +local_scheme = "no-local-version" [tool.ruff] line-length = 88 From 7b3abdcc412f86a60845a39724bb51921083a8c3 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Mon, 30 Jun 2025 06:32:13 +0200 Subject: [PATCH 55/72] build: Mark package as stable in project metadata. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8a522ecf..798f77fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ authors = [ ] requires-python = ">=3.9" classifiers = [ - "Development Status :: 4 - Beta", + "Development Status :: 5 - Production/Stable", "Framework :: AsyncIO", "Framework :: Pytest", "Intended Audience :: Developers", From ce1d68b6bf87af56e53c9ba54d8dd86c5cbcb55d Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Mon, 30 Jun 2025 06:34:38 +0200 Subject: [PATCH 56/72] build: Add seifertm to the list of maintainers. --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 798f77fe..497e521f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,9 @@ license = "Apache-2.0" license-files = [ "LICENSE", ] +maintainers = [ + { name = "Michael Seifert", email = "m.seifert@digitalernachschub.de" }, +] authors = [ { name = "Tin Tvrtković ", email = "tinchester@gmail.com" }, ] From 20b9965109014a4d32a8d6312840c209927ab20e Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Mon, 30 Jun 2025 06:35:14 +0200 Subject: [PATCH 57/72] build: Fix author entry for @tinchester. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 497e521f..1851bf5d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ maintainers = [ { name = "Michael Seifert", email = "m.seifert@digitalernachschub.de" }, ] authors = [ - { name = "Tin Tvrtković ", email = "tinchester@gmail.com" }, + { name = "Tin Tvrtković", email = "tinchester@gmail.com" }, ] requires-python = ">=3.9" classifiers = [ From 3e93927857e970346e5e6f9c01a11b71d6cdfc07 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Mon, 30 Jun 2025 06:39:00 +0200 Subject: [PATCH 58/72] build: Declare Python 3.14 support. --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 1851bf5d..3ba6f317 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Software Development :: Testing", "Typing :: Typed", ] @@ -111,6 +112,9 @@ lint.ignore = [ "D415", # First line should end with a period, question mark, or exclamation point ] +[tool.pyproject-fmt] +max_supported_python = "3.14" + [tool.pytest.ini_options] python_files = [ "test_*.py", From 5a1099f5c0739a2d26f839dfa9f5d6a191a7ce02 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Mon, 30 Jun 2025 06:40:11 +0200 Subject: [PATCH 59/72] ci: Publish to test.pypi.org outside of the release environment to avoid manual approval. --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1a2a3b69..835cc817 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -201,7 +201,6 @@ jobs: publish-test-pypi: name: Publish packages to test.pypi.org - environment: release if: github.event_name == 'push' && github.ref == 'refs/heads/main' needs: [lint, check] runs-on: ubuntu-latest From 86781a19848a89c5098fe0192f6d9171c91fc88d Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Mon, 30 Jun 2025 06:51:10 +0200 Subject: [PATCH 60/72] docs: Rename news fragments to enable link to underlying issue in resulting changelog. --- changelog.d/{+127.added.rst => 127.added.rst} | 0 changelog.d/{+878.fixed.rst => 878.fixed.rst} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename changelog.d/{+127.added.rst => 127.added.rst} (100%) rename changelog.d/{+878.fixed.rst => 878.fixed.rst} (100%) diff --git a/changelog.d/+127.added.rst b/changelog.d/127.added.rst similarity index 100% rename from changelog.d/+127.added.rst rename to changelog.d/127.added.rst diff --git a/changelog.d/+878.fixed.rst b/changelog.d/878.fixed.rst similarity index 100% rename from changelog.d/+878.fixed.rst rename to changelog.d/878.fixed.rst From 119669a2516b2dcbec5024772ad44ca2595b309f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 21:35:23 +0000 Subject: [PATCH 61/72] Build(deps): Bump hypothesis in /dependencies/default Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.135.14 to 6.135.18. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.135.14...hypothesis-python-6.135.18) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.135.18 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 88575c6e..ce713652 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,7 +1,7 @@ attrs==25.3.0 coverage==7.9.1 exceptiongroup==1.3.0 -hypothesis==6.135.14 +hypothesis==6.135.18 iniconfig==2.1.0 packaging==25.0 pluggy==1.6.0 From 93efae3b125427a11aeae96e488674b0983c1ffe Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 20:00:55 +0000 Subject: [PATCH 62/72] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.12.0 → v0.12.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.0...v0.12.1) - [github.com/zizmorcore/zizmor-pre-commit: v1.7.0 → v1.11.0](https://github.com/zizmorcore/zizmor-pre-commit/compare/v1.7.0...v1.11.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 38d06dd0..4e208606 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: check-merge-conflict exclude: rst$ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.0 + rev: v0.12.1 hooks: - id: ruff args: [--fix] @@ -75,7 +75,7 @@ repos: # https://pyproject-fmt.readthedocs.io/en/latest/#calculating-max-supported-python-version additional_dependencies: [tox>=4.9] - repo: https://github.com/zizmorcore/zizmor-pre-commit - rev: v1.7.0 + rev: v1.11.0 hooks: - id: zizmor ci: From fb6bfbf6707506a6b16f06aa332c826e3345f471 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 20:46:32 +0000 Subject: [PATCH 63/72] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.12.1 → v0.12.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.1...v0.12.2) - [github.com/sirosen/check-jsonschema: 0.33.1 → 0.33.2](https://github.com/sirosen/check-jsonschema/compare/0.33.1...0.33.2) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4e208606..a2ea9b49 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: check-merge-conflict exclude: rst$ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.1 + rev: v0.12.2 hooks: - id: ruff args: [--fix] @@ -65,7 +65,7 @@ repos: - 'SC1004:' stages: [manual] - repo: https://github.com/sirosen/check-jsonschema - rev: 0.33.1 + rev: 0.33.2 hooks: - id: check-github-actions - repo: https://github.com/tox-dev/pyproject-fmt From 8cc378d64e7241d54142b27f42821ba704505302 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 21:08:36 +0000 Subject: [PATCH 64/72] Build(deps): Bump typing-extensions in /dependencies/default Bumps [typing-extensions](https://github.com/python/typing_extensions) from 4.14.0 to 4.14.1. - [Release notes](https://github.com/python/typing_extensions/releases) - [Changelog](https://github.com/python/typing_extensions/blob/main/CHANGELOG.md) - [Commits](https://github.com/python/typing_extensions/compare/4.14.0...4.14.1) --- updated-dependencies: - dependency-name: typing-extensions dependency-version: 4.14.1 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 ce713652..d68df1c1 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -8,4 +8,4 @@ pluggy==1.6.0 pytest==8.4.1 sortedcontainers==2.4.0 tomli==2.2.1 -typing_extensions==4.14.0 +typing_extensions==4.14.1 From b7a8ab599fded97e46c2701ad73f958f0ed9e736 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 21:52:27 +0000 Subject: [PATCH 65/72] Build(deps): Bump coverage from 7.9.1 to 7.9.2 in /dependencies/default Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.9.1 to 7.9.2. - [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.9.1...7.9.2) --- updated-dependencies: - dependency-name: coverage dependency-version: 7.9.2 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 d68df1c1..2b80e033 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,5 +1,5 @@ attrs==25.3.0 -coverage==7.9.1 +coverage==7.9.2 exceptiongroup==1.3.0 hypothesis==6.135.18 iniconfig==2.1.0 From 8e2030532c7dba00d51856a2b7bfaddaf3c03d6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 21:57:09 +0000 Subject: [PATCH 66/72] Build(deps): Bump hypothesis in /dependencies/default Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.135.18 to 6.135.26. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.135.18...hypothesis-python-6.135.26) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.135.26 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 2b80e033..1399a34a 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,7 +1,7 @@ attrs==25.3.0 coverage==7.9.2 exceptiongroup==1.3.0 -hypothesis==6.135.18 +hypothesis==6.135.26 iniconfig==2.1.0 packaging==25.0 pluggy==1.6.0 From bd4551c11f8860cc3e83bdb6ce89090d70b43a53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 21:19:58 +0000 Subject: [PATCH 67/72] Build(deps): Bump ncipollo/release-action from 1.16.0 to 1.18.0 Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.16.0 to 1.18.0. - [Release notes](https://github.com/ncipollo/release-action/releases) - [Commits](https://github.com/ncipollo/release-action/compare/440c8c1cb0ed28b9f43e4d1d670870f059653174...bcfe5470707e8832e12347755757cec0eb3c22af) --- updated-dependencies: - dependency-name: ncipollo/release-action dependency-version: 1.18.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 835cc817..b8d2ddea 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -191,7 +191,7 @@ jobs: path: dist - name: Create GitHub Release if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1.16.0 + uses: ncipollo/release-action@bcfe5470707e8832e12347755757cec0eb3c22af # v1.18.0 with: name: pytest-asyncio ${{ needs.lint.outputs.version }} artifacts: dist/* From 0e6342323d93e60218f0625591a33e967a545a65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 21:35:24 +0000 Subject: [PATCH 68/72] Build(deps): Bump hypothesis in /dependencies/default Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.135.26 to 6.135.29. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.135.26...hypothesis-python-6.135.29) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.135.29 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 1399a34a..3d762017 100644 --- a/dependencies/default/constraints.txt +++ b/dependencies/default/constraints.txt @@ -1,7 +1,7 @@ attrs==25.3.0 coverage==7.9.2 exceptiongroup==1.3.0 -hypothesis==6.135.26 +hypothesis==6.135.29 iniconfig==2.1.0 packaging==25.0 pluggy==1.6.0 From 9e5e25f05727c2d1569e614545bd1712481910f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 21:30:48 +0000 Subject: [PATCH 69/72] Build(deps): Bump certifi in /dependencies/docs Bumps [certifi](https://github.com/certifi/python-certifi) from 2025.6.15 to 2025.7.14. - [Commits](https://github.com/certifi/python-certifi/compare/2025.06.15...2025.07.14) --- updated-dependencies: - dependency-name: certifi dependency-version: 2025.7.14 dependency-type: direct:production update-type: version-update:semver-minor ... 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 292c0f10..84345d61 100644 --- a/dependencies/docs/constraints.txt +++ b/dependencies/docs/constraints.txt @@ -1,6 +1,6 @@ alabaster==0.7.16 Babel==2.17.0 -certifi==2025.6.15 +certifi==2025.7.14 charset-normalizer==3.4.2 docutils==0.21.2 idna==3.10 From d66e12f8d7869c02e196359a3e1c2d55171dcbe0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 20:11:34 +0000 Subject: [PATCH 70/72] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.12.2 → v0.12.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.2...v0.12.3) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a2ea9b49..60bb582f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - id: check-merge-conflict exclude: rst$ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.2 + rev: v0.12.3 hooks: - id: ruff args: [--fix] From d9a8dcc15e5db9eda76601d7f86d2680e82777eb Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 16 Jul 2025 05:49:49 +0200 Subject: [PATCH 71/72] ci: Workaround missing Tag annotation during release. Closes #1128 --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b8d2ddea..1198bdab 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -173,6 +173,7 @@ jobs: if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') run: | set -e + git fetch --tags --force # see https://github.com/actions/checkout/issues/290 git for-each-ref "${GITHUB_REF}" --format='%(contents)' > release-notes.rst # Strip PGP signature from signed tags sed -i "/-----BEGIN PGP SIGNATURE-----/,/-----END PGP SIGNATURE-----\n/d" release-notes.rst From ce06c0733056a9df4f9ec1a074a1504814886368 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 16 Jul 2025 05:50:17 +0200 Subject: [PATCH 72/72] chore: Prepare release of v1.1.0. --- changelog.d/+6aa3d3e0.added.rst | 1 - changelog.d/+6fe51a75.downstream.rst | 1 - changelog.d/+b22c903a.fixed.rst | 1 - changelog.d/127.added.rst | 1 - changelog.d/200.added.rst | 1 - changelog.d/878.fixed.rst | 1 - docs/reference/changelog.rst | 24 ++++++++++++++++++++++++ 7 files changed, 24 insertions(+), 6 deletions(-) delete mode 100644 changelog.d/+6aa3d3e0.added.rst delete mode 100644 changelog.d/+6fe51a75.downstream.rst delete mode 100644 changelog.d/+b22c903a.fixed.rst delete mode 100644 changelog.d/127.added.rst delete mode 100644 changelog.d/200.added.rst delete mode 100644 changelog.d/878.fixed.rst diff --git a/changelog.d/+6aa3d3e0.added.rst b/changelog.d/+6aa3d3e0.added.rst deleted file mode 100644 index 7df0a155..00000000 --- a/changelog.d/+6aa3d3e0.added.rst +++ /dev/null @@ -1 +0,0 @@ -Warning when the current event loop is closed by a test diff --git a/changelog.d/+6fe51a75.downstream.rst b/changelog.d/+6fe51a75.downstream.rst deleted file mode 100644 index c42f9cf2..00000000 --- a/changelog.d/+6fe51a75.downstream.rst +++ /dev/null @@ -1 +0,0 @@ -Added runtime dependency on `backports.asyncio.runner `__ for use with Python 3.10 and older diff --git a/changelog.d/+b22c903a.fixed.rst b/changelog.d/+b22c903a.fixed.rst deleted file mode 100644 index 5af4f6e7..00000000 --- a/changelog.d/+b22c903a.fixed.rst +++ /dev/null @@ -1 +0,0 @@ -An error that could cause duplicate warnings to be issued diff --git a/changelog.d/127.added.rst b/changelog.d/127.added.rst deleted file mode 100644 index e6402852..00000000 --- a/changelog.d/127.added.rst +++ /dev/null @@ -1 +0,0 @@ -Propagation of ContextVars from async fixtures to other fixtures and tests on Python 3.10 and older diff --git a/changelog.d/200.added.rst b/changelog.d/200.added.rst deleted file mode 100644 index 6c2cb39e..00000000 --- a/changelog.d/200.added.rst +++ /dev/null @@ -1 +0,0 @@ -Cancellation of tasks when the `loop_scope` ends diff --git a/changelog.d/878.fixed.rst b/changelog.d/878.fixed.rst deleted file mode 100644 index 4bdb5d72..00000000 --- a/changelog.d/878.fixed.rst +++ /dev/null @@ -1 +0,0 @@ -Error about missing loop when calling functions requiring a loop in the `finally` clause of a task diff --git a/docs/reference/changelog.rst b/docs/reference/changelog.rst index ea6ea8da..e31395a4 100644 --- a/docs/reference/changelog.rst +++ b/docs/reference/changelog.rst @@ -10,6 +10,30 @@ This project uses `towncrier `__ for changlog .. towncrier release notes start +`1.1.0 `_ - 2025-07-16 +=============================================================================== + +Added +----- + +- Propagation of ContextVars from async fixtures to other fixtures and tests on Python 3.10 and older (`#127 `_) +- Cancellation of tasks when the `loop_scope` ends (`#200 `_) +- Warning when the current event loop is closed by a test + + +Fixed +----- + +- Error about missing loop when calling functions requiring a loop in the `finally` clause of a task (`#878 `_) +- An error that could cause duplicate warnings to be issued + + +Notes for Downstream Packagers +------------------------------ + +- Added runtime dependency on `backports.asyncio.runner `__ for use with Python 3.10 and older + + `1.0.0 `_ - 2025-05-26 ===============================================================================