From 4b751cd043eb415c1bc26c8d1f1abd14af1c6a46 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Mon, 10 Nov 2025 16:51:00 +0100 Subject: [PATCH 001/168] chore: Bump dependencies on pydantic and pydantic-core. --- constraints.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/constraints.txt b/constraints.txt index 6dffd2f9..3183b8b1 100644 --- a/constraints.txt +++ b/constraints.txt @@ -35,8 +35,8 @@ packaging==25.0 pluggy==1.6.0 Pygments==2.19.2 pycparser==2.23 -pydantic==2.11.10 -pydantic-core==2.33.2 +pydantic==2.12.4 +pydantic-core==2.41.5 pytest==9.0.0 readme-renderer==44.0 requests==2.32.5 From 0f1c38a6369a66998f4582da9a0f6493da9e0c69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 19:07:43 +0000 Subject: [PATCH 002/168] Build(deps): Bump hypothesis from 6.141.1 to 6.147.0 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.141.1 to 6.147.0. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.141.1...hypothesis-python-6.147.0) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.147.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 3183b8b1..4d2af4b8 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.10.7 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.0 -hypothesis==6.141.1 +hypothesis==6.147.0 iniconfig==2.1.0 id==1.5.0 idna==3.11 From 0bcf98c409517e186e186800933dcbaf6debc2cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 19:07:29 +0000 Subject: [PATCH 003/168] Build(deps): Bump alabaster from 0.7.16 to 1.0.0 Bumps [alabaster](https://github.com/sphinx-doc/alabaster) from 0.7.16 to 1.0.0. - [Release notes](https://github.com/sphinx-doc/alabaster/releases) - [Changelog](https://github.com/sphinx-doc/alabaster/blob/master/docs/changelog.rst) - [Commits](https://github.com/sphinx-doc/alabaster/compare/0.7.16...1.0.0) --- updated-dependencies: - dependency-name: alabaster dependency-version: 1.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 4d2af4b8..a910d4cf 100644 --- a/constraints.txt +++ b/constraints.txt @@ -1,4 +1,4 @@ -alabaster==0.7.16 +alabaster==1.0.0 annotated-types-0.7.0 attrs==25.4.0 babel==2.17.0 From 0c0e67bae4a318b263ba6e0e2a0042c93c88de9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 19:07:23 +0000 Subject: [PATCH 004/168] Build(deps): Bump click from 8.1.8 to 8.3.0 Bumps [click](https://github.com/pallets/click) from 8.1.8 to 8.3.0. - [Release notes](https://github.com/pallets/click/releases) - [Changelog](https://github.com/pallets/click/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/click/compare/8.1.8...8.3.0) --- updated-dependencies: - dependency-name: click dependency-version: 8.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index a910d4cf..4f812477 100644 --- a/constraints.txt +++ b/constraints.txt @@ -8,7 +8,7 @@ certifi==2025.10.5 charset-normalizer==3.4.4 check-wheel-contents==0.6.1 cffi==2.0.0 -click==8.1.8 +click==8.3.0 coverage==7.10.7 cryptography==46.0.3 docutils==0.21.2 From 250a3193465160f14d11e55cebf44dfa4ee090dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 19:07:00 +0000 Subject: [PATCH 005/168] Build(deps): Bump sphinx from 8.0.2 to 8.1.3 Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 8.0.2 to 8.1.3. - [Release notes](https://github.com/sphinx-doc/sphinx/releases) - [Changelog](https://github.com/sphinx-doc/sphinx/blob/v8.1.3/CHANGES.rst) - [Commits](https://github.com/sphinx-doc/sphinx/compare/v8.0.2...v8.1.3) --- updated-dependencies: - dependency-name: sphinx dependency-version: 8.1.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 4f812477..8c541c8b 100644 --- a/constraints.txt +++ b/constraints.txt @@ -48,7 +48,7 @@ setuptools==80.9.0 setuptools-scm==9.2.2 snowballstemmer==3.0.1 sortedcontainers==2.4.0 -Sphinx==8.0.2 +Sphinx==8.1.3 sphinx-rtd-theme==3.0.2 sphinxcontrib-applehelp==2.0.0 sphinxcontrib-devhelp==2.0.0 From 5d8e8365df9ff6ecb04bfded764a26d95eb4fb48 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 19:38:20 +0000 Subject: [PATCH 006/168] Build(deps): Bump iniconfig from 2.1.0 to 2.3.0 Bumps [iniconfig](https://github.com/pytest-dev/iniconfig) from 2.1.0 to 2.3.0. - [Release notes](https://github.com/pytest-dev/iniconfig/releases) - [Changelog](https://github.com/pytest-dev/iniconfig/blob/main/CHANGELOG) - [Commits](https://github.com/pytest-dev/iniconfig/compare/v2.1.0...v2.3.0) --- updated-dependencies: - dependency-name: iniconfig dependency-version: 2.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/constraints.txt b/constraints.txt index 8c541c8b..3bd068b1 100644 --- a/constraints.txt +++ b/constraints.txt @@ -14,12 +14,12 @@ cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.0 hypothesis==6.147.0 -iniconfig==2.1.0 +iniconfig==2.3.0 id==1.5.0 idna==3.11 imagesize==1.4.1 importlib_metadata==8.7.0 -iniconfig==2.1.0 +iniconfig==2.3.0 jaraco.classes==3.4.0 jaraco.context==6.0.1 jaraco.functools==4.3.0 From 045e9743730c50b56ea6117c952ad89b07bdc39c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 19:06:48 +0000 Subject: [PATCH 007/168] Build(deps): Bump secretstorage from 3.3.3 to 3.4.0 Bumps [secretstorage](https://github.com/mitya57/secretstorage) from 3.3.3 to 3.4.0. - [Changelog](https://github.com/mitya57/secretstorage/blob/master/changelog) - [Commits](https://github.com/mitya57/secretstorage/compare/3.3.3...3.4.0) --- updated-dependencies: - dependency-name: secretstorage dependency-version: 3.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 3bd068b1..2244243a 100644 --- a/constraints.txt +++ b/constraints.txt @@ -43,7 +43,7 @@ requests==2.32.5 requests-toolbelt==1.0.0 rfc3986==2.0.0 rich==14.2.0 -SecretStorage==3.3.3 +SecretStorage==3.4.0 setuptools==80.9.0 setuptools-scm==9.2.2 snowballstemmer==3.0.1 From cb0ebf0ba9315ca9211f1067798ec00447772f92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 19:07:19 +0000 Subject: [PATCH 008/168] Build(deps): Bump markdown-it-py from 3.0.0 to 4.0.0 Bumps [markdown-it-py](https://github.com/executablebooks/markdown-it-py) from 3.0.0 to 4.0.0. - [Release notes](https://github.com/executablebooks/markdown-it-py/releases) - [Changelog](https://github.com/executablebooks/markdown-it-py/blob/master/CHANGELOG.md) - [Commits](https://github.com/executablebooks/markdown-it-py/compare/v3.0.0...v4.0.0) --- updated-dependencies: - dependency-name: markdown-it-py dependency-version: 4.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 2244243a..bc5f9921 100644 --- a/constraints.txt +++ b/constraints.txt @@ -26,7 +26,7 @@ jaraco.functools==4.3.0 jeepney==0.9.0 Jinja2==3.1.6 keyring==25.6.0 -markdown-it-py==3.0.0 +markdown-it-py==4.0.0 MarkupSafe==3.0.3 mdurl==0.1.2 more-itertools==10.8.0 From b32fdf9776b499d8f6378980a24f8457f80b3a5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 19:56:22 +0000 Subject: [PATCH 009/168] Build(deps): Bump check-wheel-contents from 0.6.1 to 0.6.3 Bumps [check-wheel-contents](https://github.com/jwodder/check-wheel-contents) from 0.6.1 to 0.6.3. - [Release notes](https://github.com/jwodder/check-wheel-contents/releases) - [Changelog](https://github.com/jwodder/check-wheel-contents/blob/master/CHANGELOG.md) - [Commits](https://github.com/jwodder/check-wheel-contents/compare/v0.6.1...v0.6.3) --- updated-dependencies: - dependency-name: check-wheel-contents dependency-version: 0.6.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index bc5f9921..4295ea78 100644 --- a/constraints.txt +++ b/constraints.txt @@ -6,7 +6,7 @@ backports.asyncio.runner==1.2.0 backports.tarfile==1.2.0 certifi==2025.10.5 charset-normalizer==3.4.4 -check-wheel-contents==0.6.1 +check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.0 coverage==7.10.7 From 7a69596585025dd6e2703ea5ed4b0a553b703ba4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 20:05:01 +0000 Subject: [PATCH 010/168] Build(deps): Bump coverage from 7.10.7 to 7.11.3 Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.10.7 to 7.11.3. - [Release notes](https://github.com/coveragepy/coveragepy/releases) - [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst) - [Commits](https://github.com/coveragepy/coveragepy/compare/7.10.7...7.11.3) --- updated-dependencies: - dependency-name: coverage dependency-version: 7.11.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 4295ea78..ba3cf612 100644 --- a/constraints.txt +++ b/constraints.txt @@ -9,7 +9,7 @@ charset-normalizer==3.4.4 check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.0 -coverage==7.10.7 +coverage==7.11.3 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.0 From ca4d958a1b948c8c3f32e7d4838974215008eaeb Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 15 Nov 2025 17:41:34 +0100 Subject: [PATCH 011/168] Fix typos discovered by codespell --- docs/reference/changelog.rst | 2 +- pytest_asyncio/plugin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/changelog.rst b/docs/reference/changelog.rst index 5c0922f7..e6bd7b86 100644 --- a/docs/reference/changelog.rst +++ b/docs/reference/changelog.rst @@ -6,7 +6,7 @@ All notable changes to this project will be documented in this file. The format is based on `Keep a Changelog `__, and this project adheres to `Semantic Versioning `__. -This project uses `towncrier `__ for changlog management and the changes for the upcoming release can be found in https://github.com/pytest-dev/pytest-asyncio/tree/main/changelog.d/. +This project uses `towncrier `__ for changelog management and the changes for the upcoming release can be found in https://github.com/pytest-dev/pytest-asyncio/tree/main/changelog.d/. .. towncrier release notes start diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index fccb6cff..3b63b250 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -488,7 +488,7 @@ def _synchronization_target_attr(self) -> tuple[object, str]: """ Return the coroutine that needs to be synchronized during the test run. - This method is inteded to be overwritten by subclasses when they need to apply + This method is intended to be overwritten by subclasses when they need to apply the coroutine synchronizer to a value that's different from self.obj e.g. the AsyncHypothesisTest subclass. """ From bf7e8bff7a11e03d8cd1eca92a3c36869eabece4 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 15 Nov 2025 17:30:16 +0100 Subject: [PATCH 012/168] uvx zizmor --fix=safe . --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c79c0de8..421c2f86 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,8 +7,12 @@ updates: interval: weekly open-pull-requests-limit: 10 target-branch: main + cooldown: + default-days: 7 - package-ecosystem: github-actions directory: / schedule: interval: daily open-pull-requests-limit: 10 + cooldown: + default-days: 7 From 702ba8b6231b818e3eff2f5c4097cb299ec65209 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 21:31:23 +0000 Subject: [PATCH 013/168] [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.14.3 → v0.14.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.3...v0.14.4) - [github.com/asottile/pyupgrade: v3.21.0 → v3.21.1](https://github.com/asottile/pyupgrade/compare/v3.21.0...v3.21.1) - [github.com/tox-dev/pyproject-fmt: v2.11.0 → v2.11.1](https://github.com/tox-dev/pyproject-fmt/compare/v2.11.0...v2.11.1) - [github.com/zizmorcore/zizmor-pre-commit: v1.16.2 → v1.16.3](https://github.com/zizmorcore/zizmor-pre-commit/compare/v1.16.2...v1.16.3) --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 365bc928..7894ea4c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,12 +13,12 @@ repos: - id: check-yaml - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.3 + rev: v0.14.4 hooks: - id: ruff args: [--fix] - repo: https://github.com/asottile/pyupgrade - rev: v3.21.0 + rev: v3.21.1 hooks: - id: pyupgrade - repo: https://github.com/asottile/yesqa @@ -68,13 +68,13 @@ repos: hooks: - id: check-github-actions - repo: https://github.com/tox-dev/pyproject-fmt - rev: v2.11.0 + rev: v2.11.1 hooks: - id: pyproject-fmt # https://pyproject-fmt.readthedocs.io/en/latest/#calculating-max-supported-python-version additional_dependencies: [tox>=4.28] - repo: https://github.com/zizmorcore/zizmor-pre-commit - rev: v1.16.2 + rev: v1.16.3 hooks: - id: zizmor ci: From bfd1ca5c5b7c0fa875253d2d3a543451fa36a1be Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 20:57:16 +0000 Subject: [PATCH 014/168] [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.14.4 → v0.14.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.4...v0.14.5) - [github.com/sirosen/check-jsonschema: 0.34.1 → 0.35.0](https://github.com/sirosen/check-jsonschema/compare/0.34.1...0.35.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 7894ea4c..2d6db289 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: - id: check-yaml - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.4 + rev: v0.14.5 hooks: - id: ruff args: [--fix] @@ -64,7 +64,7 @@ repos: - 'SC1004:' stages: [manual] - repo: https://github.com/sirosen/check-jsonschema - rev: 0.34.1 + rev: 0.35.0 hooks: - id: check-github-actions - repo: https://github.com/tox-dev/pyproject-fmt From cb723a29494451e8f61c89d9841d5fcd2bf7fb09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 19:47:10 +0000 Subject: [PATCH 015/168] Build(deps): Bump hypothesis from 6.147.0 to 6.148.1 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.147.0 to 6.148.1. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.147.0...hypothesis-python-6.148.1) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.148.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index ba3cf612..34570f5b 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.11.3 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.0 -hypothesis==6.147.0 +hypothesis==6.148.1 iniconfig==2.3.0 id==1.5.0 idna==3.11 From d8f46d44617a523fc3c6bcd55c49ee7c2b2cad1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 19:47:18 +0000 Subject: [PATCH 016/168] Build(deps): Bump click from 8.3.0 to 8.3.1 Bumps [click](https://github.com/pallets/click) from 8.3.0 to 8.3.1. - [Release notes](https://github.com/pallets/click/releases) - [Changelog](https://github.com/pallets/click/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/click/compare/8.3.0...8.3.1) --- updated-dependencies: - dependency-name: click dependency-version: 8.3.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 34570f5b..49a3a7ab 100644 --- a/constraints.txt +++ b/constraints.txt @@ -8,7 +8,7 @@ certifi==2025.10.5 charset-normalizer==3.4.4 check-wheel-contents==0.6.3 cffi==2.0.0 -click==8.3.0 +click==8.3.1 coverage==7.11.3 cryptography==46.0.3 docutils==0.21.2 From 99475c71e09b243fa5812f405b4805b1a56a3f4f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 19:46:44 +0000 Subject: [PATCH 017/168] Build(deps): Bump secretstorage from 3.4.0 to 3.4.1 Bumps [secretstorage](https://github.com/mitya57/secretstorage) from 3.4.0 to 3.4.1. - [Changelog](https://github.com/mitya57/secretstorage/blob/master/changelog) - [Commits](https://github.com/mitya57/secretstorage/compare/3.4.0...3.4.1) --- updated-dependencies: - dependency-name: secretstorage dependency-version: 3.4.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 49a3a7ab..d7e548d9 100644 --- a/constraints.txt +++ b/constraints.txt @@ -43,7 +43,7 @@ requests==2.32.5 requests-toolbelt==1.0.0 rfc3986==2.0.0 rich==14.2.0 -SecretStorage==3.4.0 +SecretStorage==3.4.1 setuptools==80.9.0 setuptools-scm==9.2.2 snowballstemmer==3.0.1 From e39d82832f520fbbbe5c0c1a3009c81c3bc0fcc2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 21:21:45 +0000 Subject: [PATCH 018/168] [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.14.5 → v0.14.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.5...v0.14.6) - [github.com/asottile/pyupgrade: v3.21.1 → v3.21.2](https://github.com/asottile/pyupgrade/compare/v3.21.1...v3.21.2) - [github.com/rhysd/actionlint: v1.7.8 → v1.7.9](https://github.com/rhysd/actionlint/compare/v1.7.8...v1.7.9) --- .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 2d6db289..0879d65d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,12 +13,12 @@ repos: - id: check-yaml - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.5 + rev: v0.14.6 hooks: - id: ruff args: [--fix] - repo: https://github.com/asottile/pyupgrade - rev: v3.21.1 + rev: v3.21.2 hooks: - id: pyupgrade - repo: https://github.com/asottile/yesqa @@ -52,7 +52,7 @@ repos: hooks: - id: python-use-type-annotations - repo: https://github.com/rhysd/actionlint - rev: v1.7.8 + rev: v1.7.9 hooks: - id: actionlint-docker args: From f742d507f2cfae034913cad93f47742c4f23b8e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Nov 2025 18:02:29 +0000 Subject: [PATCH 019/168] Build(deps): Bump actions/checkout from 5 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 843f2bd4..87285310 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,7 +23,7 @@ jobs: version: ${{ steps.version.outputs.version }} prerelease: ${{ steps.version.outputs.prerelease }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 persist-credentials: false @@ -51,7 +51,7 @@ jobs: name: Run linters runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 persist-credentials: false @@ -77,7 +77,7 @@ jobs: required: [true] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: persist-credentials: false - uses: actions/setup-python@v6 @@ -116,7 +116,7 @@ jobs: uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 with: jobs: ${{ toJSON(needs) }} - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: persist-credentials: false - uses: actions/setup-python@v6 @@ -151,7 +151,7 @@ jobs: contents: write steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 persist-credentials: false From a429edecc205ab73875c7820547c78cbe3305c73 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 21:33:52 +0000 Subject: [PATCH 020/168] [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.14.6 → v0.14.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.6...v0.14.7) - [github.com/pre-commit/mirrors-mypy: v1.18.2 → v1.19.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.18.2...v1.19.0) - [github.com/zizmorcore/zizmor-pre-commit: v1.16.3 → v1.18.0](https://github.com/zizmorcore/zizmor-pre-commit/compare/v1.16.3...v1.18.0) --- .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 0879d65d..9dd5d893 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: - id: check-yaml - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.6 + rev: v0.14.7 hooks: - id: ruff args: [--fix] @@ -41,7 +41,7 @@ repos: - id: yamlfmt args: [--mapping, '2', --sequence, '2', --offset, '0'] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.18.2 + rev: v1.19.0 hooks: - id: mypy exclude: ^(docs|tests)/.* @@ -74,7 +74,7 @@ repos: # https://pyproject-fmt.readthedocs.io/en/latest/#calculating-max-supported-python-version additional_dependencies: [tox>=4.28] - repo: https://github.com/zizmorcore/zizmor-pre-commit - rev: v1.16.3 + rev: v1.18.0 hooks: - id: zizmor ci: From a602b33c37550953a2a10abea7e0d9e9d73e4cb7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 21:29:20 +0000 Subject: [PATCH 021/168] Build(deps): Bump hypothesis from 6.148.1 to 6.148.2 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.148.1 to 6.148.2. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.148.1...hypothesis-python-6.148.2) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.148.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index d7e548d9..313431fc 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.11.3 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.0 -hypothesis==6.148.1 +hypothesis==6.148.2 iniconfig==2.3.0 id==1.5.0 idna==3.11 From 5007490377712639f44cc3b36056f86e7a5a77de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 21:28:58 +0000 Subject: [PATCH 022/168] Build(deps): Bump coverage from 7.11.3 to 7.12.0 Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.11.3 to 7.12.0. - [Release notes](https://github.com/coveragepy/coveragepy/releases) - [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst) - [Commits](https://github.com/coveragepy/coveragepy/compare/7.11.3...7.12.0) --- updated-dependencies: - dependency-name: coverage dependency-version: 7.12.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 313431fc..f7cf1754 100644 --- a/constraints.txt +++ b/constraints.txt @@ -9,7 +9,7 @@ charset-normalizer==3.4.4 check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.1 -coverage==7.11.3 +coverage==7.12.0 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.0 From 8e78b91d334fa80459c8cc49ba7f9ff5d9753f87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 21:29:06 +0000 Subject: [PATCH 023/168] Build(deps): Bump secretstorage from 3.4.1 to 3.5.0 Bumps [secretstorage](https://github.com/mitya57/secretstorage) from 3.4.1 to 3.5.0. - [Changelog](https://github.com/mitya57/secretstorage/blob/master/changelog) - [Commits](https://github.com/mitya57/secretstorage/compare/3.4.1...3.5.0) --- updated-dependencies: - dependency-name: secretstorage dependency-version: 3.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index f7cf1754..d7b48c2e 100644 --- a/constraints.txt +++ b/constraints.txt @@ -43,7 +43,7 @@ requests==2.32.5 requests-toolbelt==1.0.0 rfc3986==2.0.0 rich==14.2.0 -SecretStorage==3.4.1 +SecretStorage==3.5.0 setuptools==80.9.0 setuptools-scm==9.2.2 snowballstemmer==3.0.1 From b4139a7d1ad69d9f94129c155b681cd812923c09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 03:13:28 +0000 Subject: [PATCH 024/168] Build(deps): Bump exceptiongroup from 1.3.0 to 1.3.1 Bumps [exceptiongroup](https://github.com/agronholm/exceptiongroup) from 1.3.0 to 1.3.1. - [Release notes](https://github.com/agronholm/exceptiongroup/releases) - [Changelog](https://github.com/agronholm/exceptiongroup/blob/main/CHANGES.rst) - [Commits](https://github.com/agronholm/exceptiongroup/compare/1.3.0...1.3.1) --- updated-dependencies: - dependency-name: exceptiongroup dependency-version: 1.3.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index d7b48c2e..e0e7576a 100644 --- a/constraints.txt +++ b/constraints.txt @@ -12,7 +12,7 @@ click==8.3.1 coverage==7.12.0 cryptography==46.0.3 docutils==0.21.2 -exceptiongroup==1.3.0 +exceptiongroup==1.3.1 hypothesis==6.148.2 iniconfig==2.3.0 id==1.5.0 From 050879a0150b2784b8172f9e5dd11d1dbab20a74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Dec 2025 18:30:15 +0000 Subject: [PATCH 025/168] Build(deps): Bump hypothesis from 6.148.2 to 6.148.5 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.148.2 to 6.148.5. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.148.2...hypothesis-python-6.148.5) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.148.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index e0e7576a..ccc97268 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.12.0 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.1 -hypothesis==6.148.2 +hypothesis==6.148.5 iniconfig==2.3.0 id==1.5.0 idna==3.11 From 78756603604653c5428dc48bd49869fd69d2448d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Dec 2025 18:30:26 +0000 Subject: [PATCH 026/168] Build(deps): Bump pydantic from 2.12.4 to 2.12.5 Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.12.4 to 2.12.5. - [Release notes](https://github.com/pydantic/pydantic/releases) - [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md) - [Commits](https://github.com/pydantic/pydantic/compare/v2.12.4...v2.12.5) --- updated-dependencies: - dependency-name: pydantic dependency-version: 2.12.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index ccc97268..3a45719b 100644 --- a/constraints.txt +++ b/constraints.txt @@ -35,7 +35,7 @@ packaging==25.0 pluggy==1.6.0 Pygments==2.19.2 pycparser==2.23 -pydantic==2.12.4 +pydantic==2.12.5 pydantic-core==2.41.5 pytest==9.0.0 readme-renderer==44.0 From ad3f0d6265f35cfdbcc3c39267ef890133439c5e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Dec 2025 21:26:40 +0000 Subject: [PATCH 027/168] [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.14.7 → v0.14.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.7...v0.14.8) --- .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 9dd5d893..4698d561 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: - id: check-yaml - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.7 + rev: v0.14.8 hooks: - id: ruff args: [--fix] From b8a2f15502d82b7dd798f4cb2b2130d2b8ebb9a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Dec 2025 19:00:33 +0000 Subject: [PATCH 028/168] Build(deps): Bump pytest from 9.0.0 to 9.0.1 Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.0 to 9.0.1. - [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/9.0.0...9.0.1) --- updated-dependencies: - dependency-name: pytest dependency-version: 9.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 3a45719b..9acf01a0 100644 --- a/constraints.txt +++ b/constraints.txt @@ -37,7 +37,7 @@ Pygments==2.19.2 pycparser==2.23 pydantic==2.12.5 pydantic-core==2.41.5 -pytest==9.0.0 +pytest==9.0.1 readme-renderer==44.0 requests==2.32.5 requests-toolbelt==1.0.0 From 01e65a6c7ba952e6033d74c35765d97035958fc2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 18:30:13 +0000 Subject: [PATCH 029/168] Build(deps): Bump urllib3 from 2.5.0 to 2.6.1 Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.5.0 to 2.6.1. - [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.5.0...2.6.1) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.6.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 9acf01a0..f4d779a0 100644 --- a/constraints.txt +++ b/constraints.txt @@ -61,6 +61,6 @@ tomli==2.3.0 twine==6.2.0 typing_extensions==4.15.0 typing-inspection==0.4.2 -urllib3==2.5.0 +urllib3==2.6.1 wheel-filename==1.4.2 zipp==3.23.0 From 11c1bc6fab897e05816947c973fa513609b09f6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 18:30:21 +0000 Subject: [PATCH 030/168] Build(deps): Bump coverage from 7.12.0 to 7.13.0 Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.12.0 to 7.13.0. - [Release notes](https://github.com/coveragepy/coveragepy/releases) - [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst) - [Commits](https://github.com/coveragepy/coveragepy/compare/7.12.0...7.13.0) --- updated-dependencies: - dependency-name: coverage dependency-version: 7.13.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index f4d779a0..a9659cc8 100644 --- a/constraints.txt +++ b/constraints.txt @@ -9,7 +9,7 @@ charset-normalizer==3.4.4 check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.1 -coverage==7.12.0 +coverage==7.13.0 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.1 From 190f141a62ff1c8977b07391cfcbe34bb711c804 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 18:30:05 +0000 Subject: [PATCH 031/168] Build(deps): Bump pytest from 9.0.1 to 9.0.2 Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.1 to 9.0.2. - [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/9.0.1...9.0.2) --- updated-dependencies: - dependency-name: pytest dependency-version: 9.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index a9659cc8..2ad886fc 100644 --- a/constraints.txt +++ b/constraints.txt @@ -37,7 +37,7 @@ Pygments==2.19.2 pycparser==2.23 pydantic==2.12.5 pydantic-core==2.41.5 -pytest==9.0.1 +pytest==9.0.2 readme-renderer==44.0 requests==2.32.5 requests-toolbelt==1.0.0 From 90bf4433d11392c637ab58af88fb603ef4d9a52d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 18:30:00 +0000 Subject: [PATCH 032/168] Build(deps): Bump hypothesis from 6.148.5 to 6.148.7 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.148.5 to 6.148.7. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.148.5...hypothesis-python-6.148.7) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.148.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 2ad886fc..bc3b26c9 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.13.0 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.1 -hypothesis==6.148.5 +hypothesis==6.148.7 iniconfig==2.3.0 id==1.5.0 idna==3.11 From 569170e9c6006e7d9e54e7923f97835d3c103b6a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 21:16:10 +0000 Subject: [PATCH 033/168] [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.14.8 → v0.14.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.8...v0.14.9) - [github.com/pre-commit/mirrors-mypy: v1.19.0 → v1.19.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.19.0...v1.19.1) - [github.com/sirosen/check-jsonschema: 0.35.0 → 0.36.0](https://github.com/sirosen/check-jsonschema/compare/0.35.0...0.36.0) --- .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 4698d561..2ef54538 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: - id: check-yaml - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.8 + rev: v0.14.9 hooks: - id: ruff args: [--fix] @@ -41,7 +41,7 @@ repos: - id: yamlfmt args: [--mapping, '2', --sequence, '2', --offset, '0'] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.19.0 + rev: v1.19.1 hooks: - id: mypy exclude: ^(docs|tests)/.* @@ -64,7 +64,7 @@ repos: - 'SC1004:' stages: [manual] - repo: https://github.com/sirosen/check-jsonschema - rev: 0.35.0 + rev: 0.36.0 hooks: - id: check-github-actions - repo: https://github.com/tox-dev/pyproject-fmt From e6df5dc6574000b7f4223413ba17cc2d411527a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Dec 2025 18:02:46 +0000 Subject: [PATCH 034/168] Build(deps): Bump codecov/codecov-action from 5.5.1 to 5.5.2 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.5.1 to 5.5.2. - [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/5a1091511ad55cbe89839c7260b706298ca349f7...671740ac38dd9b0130fbe1cec585b89eea48d3de) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: 5.5.2 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 87285310..2a8f53de 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@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 with: files: coverage.xml fail_ci_if_error: true From 9d45ec87faf5033102374e8594e1daa3b72a214a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 18:24:49 +0000 Subject: [PATCH 035/168] Build(deps): Bump actions/download-artifact from 6 to 7 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 7. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .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 2a8f53de..22054442 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -127,7 +127,7 @@ jobs: set -xe python -m pip install --upgrade coverage[toml] - name: Download coverage data for all test runs - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: pattern: coverage-* path: coverage @@ -187,7 +187,7 @@ jobs: name: release-notes.md path: release-notes.md - name: Download distributions - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: name: dist path: dist @@ -213,7 +213,7 @@ jobs: id-token: write steps: - name: Download distributions - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: name: dist path: dist @@ -232,7 +232,7 @@ jobs: id-token: write steps: - name: Download distributions - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v7 with: name: dist path: dist From 1ae33ff45962870f16bc5fb1296bef523a7d87cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 18:24:44 +0000 Subject: [PATCH 036/168] Build(deps): Bump actions/upload-artifact from 5 to 6 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 22054442..77a17a8f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -42,7 +42,7 @@ jobs: id: version run: python ./tools/get-version.py >> $GITHUB_OUTPUT - name: Upload artifacts - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: dist path: dist @@ -93,7 +93,7 @@ jobs: run: python -m tox - name: Store coverage data - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 if: "!endsWith(matrix.os, 'windows')" with: name: coverage-python-${{ matrix.python-version }} @@ -182,7 +182,7 @@ jobs: run: | pandoc --wrap=preserve -o release-notes.md release-notes.rst - name: Upload artifacts - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: release-notes.md path: release-notes.md From fcd87d4d7b1a0f6ec239056c26542175e70780ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Dec 2025 18:18:23 +0000 Subject: [PATCH 037/168] Build(deps): Bump urllib3 from 2.6.1 to 2.6.2 Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.1 to 2.6.2. - [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.6.1...2.6.2) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.6.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index bc3b26c9..3b505c9f 100644 --- a/constraints.txt +++ b/constraints.txt @@ -61,6 +61,6 @@ tomli==2.3.0 twine==6.2.0 typing_extensions==4.15.0 typing-inspection==0.4.2 -urllib3==2.6.1 +urllib3==2.6.2 wheel-filename==1.4.2 zipp==3.23.0 From dd5fe91ceca78ff00c50e7c7282a802ee974e1ec Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Dec 2025 20:50:19 +0000 Subject: [PATCH 038/168] [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.14.9 → v0.14.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.9...v0.14.10) - [github.com/zizmorcore/zizmor-pre-commit: v1.18.0 → v1.19.0](https://github.com/zizmorcore/zizmor-pre-commit/compare/v1.18.0...v1.19.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 2ef54538..f009b9dd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: - id: check-yaml - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.9 + rev: v0.14.10 hooks: - id: ruff args: [--fix] @@ -74,7 +74,7 @@ repos: # https://pyproject-fmt.readthedocs.io/en/latest/#calculating-max-supported-python-version additional_dependencies: [tox>=4.28] - repo: https://github.com/zizmorcore/zizmor-pre-commit - rev: v1.18.0 + rev: v1.19.0 hooks: - id: zizmor ci: From fdd194c6dea6e674c3dc444da63706acabbdf30c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Dec 2025 18:19:10 +0000 Subject: [PATCH 039/168] Build(deps): Bump importlib-metadata from 8.7.0 to 8.7.1 Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 8.7.0 to 8.7.1. - [Release notes](https://github.com/python/importlib_metadata/releases) - [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst) - [Commits](https://github.com/python/importlib_metadata/compare/v8.7.0...v8.7.1) --- updated-dependencies: - dependency-name: importlib-metadata dependency-version: 8.7.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 3b505c9f..e3c034be 100644 --- a/constraints.txt +++ b/constraints.txt @@ -18,7 +18,7 @@ iniconfig==2.3.0 id==1.5.0 idna==3.11 imagesize==1.4.1 -importlib_metadata==8.7.0 +importlib_metadata==8.7.1 iniconfig==2.3.0 jaraco.classes==3.4.0 jaraco.context==6.0.1 From af80f74080c9fcfa08814c91a140e65968156a5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Dec 2025 18:19:34 +0000 Subject: [PATCH 040/168] Build(deps): Bump jaraco-functools from 4.3.0 to 4.4.0 Bumps [jaraco-functools](https://github.com/jaraco/jaraco.functools) from 4.3.0 to 4.4.0. - [Release notes](https://github.com/jaraco/jaraco.functools/releases) - [Changelog](https://github.com/jaraco/jaraco.functools/blob/main/NEWS.rst) - [Commits](https://github.com/jaraco/jaraco.functools/compare/v4.3.0...v4.4.0) --- updated-dependencies: - dependency-name: jaraco-functools dependency-version: 4.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index e3c034be..a84b28b6 100644 --- a/constraints.txt +++ b/constraints.txt @@ -22,7 +22,7 @@ importlib_metadata==8.7.1 iniconfig==2.3.0 jaraco.classes==3.4.0 jaraco.context==6.0.1 -jaraco.functools==4.3.0 +jaraco.functools==4.4.0 jeepney==0.9.0 Jinja2==3.1.6 keyring==25.6.0 From d5f206d53171e4a75e0d65ae5cb9e839c1942565 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 18:57:26 +0000 Subject: [PATCH 041/168] Build(deps): Bump hypothesis from 6.148.7 to 6.148.8 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.148.7 to 6.148.8. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.148.7...hypothesis-python-6.148.8) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.148.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index a84b28b6..93420609 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.13.0 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.1 -hypothesis==6.148.7 +hypothesis==6.148.8 iniconfig==2.3.0 id==1.5.0 idna==3.11 From c194827676c8ce3421412ac851440ae41a385956 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 18:57:39 +0000 Subject: [PATCH 042/168] Build(deps): Bump jaraco-context from 6.0.1 to 6.0.2 Bumps [jaraco-context](https://github.com/jaraco/jaraco.context) from 6.0.1 to 6.0.2. - [Release notes](https://github.com/jaraco/jaraco.context/releases) - [Changelog](https://github.com/jaraco/jaraco.context/blob/main/NEWS.rst) - [Commits](https://github.com/jaraco/jaraco.context/compare/v6.0.1...v6.0.2) --- updated-dependencies: - dependency-name: jaraco-context dependency-version: 6.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 93420609..b032962d 100644 --- a/constraints.txt +++ b/constraints.txt @@ -21,7 +21,7 @@ imagesize==1.4.1 importlib_metadata==8.7.1 iniconfig==2.3.0 jaraco.classes==3.4.0 -jaraco.context==6.0.1 +jaraco.context==6.0.2 jaraco.functools==4.4.0 jeepney==0.9.0 Jinja2==3.1.6 From 4a31eeb9a21c0f50a0205044cf999ff5fa4bb886 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 18:56:59 +0000 Subject: [PATCH 043/168] Build(deps): Bump coverage from 7.13.0 to 7.13.1 Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.13.0 to 7.13.1. - [Release notes](https://github.com/coveragepy/coveragepy/releases) - [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst) - [Commits](https://github.com/coveragepy/coveragepy/compare/7.13.0...7.13.1) --- updated-dependencies: - dependency-name: coverage dependency-version: 7.13.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index b032962d..4ed36064 100644 --- a/constraints.txt +++ b/constraints.txt @@ -9,7 +9,7 @@ charset-normalizer==3.4.4 check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.1 -coverage==7.13.0 +coverage==7.13.1 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.1 From 2c8219095960e5cc5972796dea55eaed1cd58e68 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 21:12:12 +0000 Subject: [PATCH 044/168] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/rhysd/actionlint: v1.7.9 → v1.7.10](https://github.com/rhysd/actionlint/compare/v1.7.9...v1.7.10) --- .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 f009b9dd..fc6eb18c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -52,7 +52,7 @@ repos: hooks: - id: python-use-type-annotations - repo: https://github.com/rhysd/actionlint - rev: v1.7.9 + rev: v1.7.10 hooks: - id: actionlint-docker args: From b6722111d5ba3837f43d94261747f6a1150e95a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jan 2026 23:43:17 +0000 Subject: [PATCH 045/168] Build(deps): Bump hypothesis from 6.148.8 to 6.149.1 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.148.8 to 6.149.1. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.148.8...hypothesis-python-6.149.1) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.149.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 4ed36064..37c6a0fe 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.13.1 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.1 -hypothesis==6.148.8 +hypothesis==6.149.1 iniconfig==2.3.0 id==1.5.0 idna==3.11 From 2362735f87aac58dc41b9da62b9dde26626e8c33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jan 2026 23:43:22 +0000 Subject: [PATCH 046/168] Build(deps): Bump certifi from 2025.10.5 to 2026.1.4 Bumps [certifi](https://github.com/certifi/python-certifi) from 2025.10.5 to 2026.1.4. - [Commits](https://github.com/certifi/python-certifi/compare/2025.10.05...2026.01.04) --- updated-dependencies: - dependency-name: certifi dependency-version: 2026.1.4 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 37c6a0fe..2e5c6505 100644 --- a/constraints.txt +++ b/constraints.txt @@ -4,7 +4,7 @@ attrs==25.4.0 babel==2.17.0 backports.asyncio.runner==1.2.0 backports.tarfile==1.2.0 -certifi==2025.10.5 +certifi==2026.1.4 charset-normalizer==3.4.4 check-wheel-contents==0.6.3 cffi==2.0.0 From f4e2dc0be22fbd9f6e4077aec6bb1ae3a04c0488 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Jan 2026 21:03:31 +0000 Subject: [PATCH 047/168] [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.14.10 → v0.14.11](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.10...v0.14.11) - [github.com/zizmorcore/zizmor-pre-commit: v1.19.0 → v1.20.0](https://github.com/zizmorcore/zizmor-pre-commit/compare/v1.19.0...v1.20.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 fc6eb18c..246c6e6b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: - id: check-yaml - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.10 + rev: v0.14.11 hooks: - id: ruff args: [--fix] @@ -74,7 +74,7 @@ repos: # https://pyproject-fmt.readthedocs.io/en/latest/#calculating-max-supported-python-version additional_dependencies: [tox>=4.28] - repo: https://github.com/zizmorcore/zizmor-pre-commit - rev: v1.19.0 + rev: v1.20.0 hooks: - id: zizmor ci: From c813669309f8d0e24b929c89880aa814e79e73ba Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 13 Jan 2026 22:30:01 +0100 Subject: [PATCH 048/168] ci: Pin versions actions/* tasks. --- .github/workflows/main.yml | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 77a17a8f..78651834 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,11 +23,11 @@ jobs: version: ${{ steps.version.outputs.version }} prerelease: ${{ steps.version.outputs.prerelease }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 with: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@v6 + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 with: python-version: ${{ env.PYTHON_LATEST }} - name: Install tox @@ -42,7 +42,7 @@ jobs: id: version run: python ./tools/get-version.py >> $GITHUB_OUTPUT - name: Upload artifacts - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f with: name: dist path: dist @@ -51,11 +51,11 @@ jobs: name: Run linters runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 with: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@v6 + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 with: python-version: ${{ env.PYTHON_LATEST }} - name: Install GitHub matcher for ActionLint checker @@ -77,10 +77,10 @@ jobs: required: [true] steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 with: persist-credentials: false - - uses: actions/setup-python@v6 + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -93,7 +93,7 @@ jobs: run: python -m tox - name: Store coverage data - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f if: "!endsWith(matrix.os, 'windows')" with: name: coverage-python-${{ matrix.python-version }} @@ -116,10 +116,10 @@ jobs: uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 with: jobs: ${{ toJSON(needs) }} - - uses: actions/checkout@v6 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 with: persist-credentials: false - - uses: actions/setup-python@v6 + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 with: python-version: ${{ env.PYTHON_LATEST }} - name: Install Coverage.py @@ -127,7 +127,7 @@ jobs: set -xe python -m pip install --upgrade coverage[toml] - name: Download coverage data for all test runs - uses: actions/download-artifact@v7 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 with: pattern: coverage-* path: coverage @@ -151,12 +151,12 @@ jobs: contents: write steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 with: fetch-depth: 0 persist-credentials: false - name: Install Python - uses: actions/setup-python@v6 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 - name: Install towncrier run: pip install towncrier==24.8.0 - name: Install pandoc @@ -182,12 +182,12 @@ jobs: run: | pandoc --wrap=preserve -o release-notes.md release-notes.rst - name: Upload artifacts - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f with: name: release-notes.md path: release-notes.md - name: Download distributions - uses: actions/download-artifact@v7 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 with: name: dist path: dist @@ -213,7 +213,7 @@ jobs: id-token: write steps: - name: Download distributions - uses: actions/download-artifact@v7 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 with: name: dist path: dist @@ -232,7 +232,7 @@ jobs: id-token: write steps: - name: Download distributions - uses: actions/download-artifact@v7 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 with: name: dist path: dist From 0d7a92e215c5b34a60567c823b661ec042fdbf09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 18:52:36 +0000 Subject: [PATCH 049/168] Build(deps): Bump actions/checkout from 6.0.1 to 6.0.2 Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.1 to 6.0.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/8e8c483db84b4bee98b60c0593521ed34d9990e8...de0fac2e4500dabe0009e67214ff5f5447ce83dd) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 78651834..af6fa534 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,7 +23,7 @@ jobs: version: ${{ steps.version.outputs.version }} prerelease: ${{ steps.version.outputs.prerelease }} steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: fetch-depth: 0 persist-credentials: false @@ -51,7 +51,7 @@ jobs: name: Run linters runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: fetch-depth: 0 persist-credentials: false @@ -77,7 +77,7 @@ jobs: required: [true] steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: persist-credentials: false - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 @@ -116,7 +116,7 @@ jobs: uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 with: jobs: ${{ toJSON(needs) }} - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: persist-credentials: false - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 @@ -151,7 +151,7 @@ jobs: contents: write steps: - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: fetch-depth: 0 persist-credentials: false From f8ddf32dac2c2a7edb65d6a3f6b9c897b48da3c3 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 23 Jan 2026 16:40:23 +0100 Subject: [PATCH 050/168] refactor: Address formatting issues recently found by shed. --- pytest_asyncio/plugin.py | 2 +- .../test_async_fixtures_contextvars.py | 85 +++--------- .../test_shared_module_fixture.py | 18 +-- tests/hypothesis/test_base.py | 24 +--- tests/markers/test_class_scope.py | 70 +++------- tests/markers/test_function_scope.py | 86 ++++-------- tests/markers/test_invalid_arguments.py | 32 ++--- tests/markers/test_mixed_scope.py | 8 +- tests/markers/test_module_scope.py | 76 ++++------- tests/markers/test_package_scope.py | 98 +++++--------- tests/markers/test_session_scope.py | 122 ++++++------------ tests/modes/test_auto_mode.py | 56 ++------ tests/modes/test_strict_mode.py | 64 +++------ tests/test_asyncio_debug.py | 80 +++--------- tests/test_asyncio_fixture.py | 8 +- tests/test_asyncio_mark.py | 88 ++++--------- tests/test_doctest.py | 12 +- tests/test_event_loop_fixture.py | 24 +--- tests/test_fixture_loop_scopes.py | 60 +++------ tests/test_import.py | 32 ++--- tests/test_is_async_test.py | 32 ++--- tests/test_port_factories.py | 32 ++--- tests/test_set_event_loop.py | 80 +++--------- tests/test_simple.py | 16 +-- tests/test_skips.py | 60 +++------ tests/test_task_cleanup.py | 8 +- 26 files changed, 343 insertions(+), 930 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 3b63b250..00c3c208 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -403,7 +403,7 @@ def _apply_contextvar_changes( def restore_contextvars(): while context_tokens: - (var, token) = context_tokens.pop() + var, token = context_tokens.pop() var.reset(token) return restore_contextvars diff --git a/tests/async_fixtures/test_async_fixtures_contextvars.py b/tests/async_fixtures/test_async_fixtures_contextvars.py index e8634d0c..0b9046eb 100644 --- a/tests/async_fixtures/test_async_fixtures_contextvars.py +++ b/tests/async_fixtures/test_async_fixtures_contextvars.py @@ -11,8 +11,7 @@ import pytest from pytest import Pytester -_prelude = dedent( - """ +_prelude = dedent(""" import pytest import pytest_asyncio from contextlib import contextmanager @@ -27,16 +26,12 @@ def context_var_manager(value): yield finally: _context_var.reset(token) -""" -) +""") def test_var_from_sync_generator_propagates_to_async(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - _prelude - + dedent( - """ + pytester.makepyfile(_prelude + dedent(""" @pytest.fixture def var_fixture(): with context_var_manager("value"): @@ -49,19 +44,14 @@ async def check_var_fixture(var_fixture): @pytest.mark.asyncio async def test(check_var_fixture): assert _context_var.get() == "value" - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) def test_var_from_async_generator_propagates_to_sync(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - _prelude - + dedent( - """ + pytester.makepyfile(_prelude + dedent(""" @pytest_asyncio.fixture async def var_fixture(): with context_var_manager("value"): @@ -74,19 +64,14 @@ def check_var_fixture(var_fixture): @pytest.mark.asyncio async def test(check_var_fixture): assert _context_var.get() == "value" - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) def test_var_from_async_fixture_propagates_to_sync(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - _prelude - + dedent( - """ + pytester.makepyfile(_prelude + dedent(""" @pytest_asyncio.fixture async def var_fixture(): _context_var.set("value") @@ -98,19 +83,14 @@ def check_var_fixture(var_fixture): def test(check_var_fixture): assert _context_var.get() == "value" - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) def test_var_from_generator_reset_before_previous_fixture_cleanup(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - _prelude - + dedent( - """ + pytester.makepyfile(_prelude + dedent(""" @pytest_asyncio.fixture async def no_var_fixture(): with pytest.raises(LookupError): @@ -127,19 +107,14 @@ async def var_fixture(no_var_fixture): @pytest.mark.asyncio async def test(var_fixture): assert _context_var.get() == "value" - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) def test_var_from_fixture_reset_before_previous_fixture_cleanup(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - _prelude - + dedent( - """ + pytester.makepyfile(_prelude + dedent(""" @pytest_asyncio.fixture async def no_var_fixture(): with pytest.raises(LookupError): @@ -156,19 +131,14 @@ async def var_fixture(no_var_fixture): @pytest.mark.asyncio async def test(var_fixture): assert _context_var.get() == "value" - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) def test_var_previous_value_restored_after_fixture(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - _prelude - + dedent( - """ + pytester.makepyfile(_prelude + dedent(""" @pytest_asyncio.fixture async def var_fixture_1(): with context_var_manager("value1"): @@ -184,19 +154,14 @@ async def var_fixture_2(var_fixture_1): @pytest.mark.asyncio async def test(var_fixture_2): assert _context_var.get() == "value2" - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) def test_var_set_to_existing_value_ok(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - _prelude - + dedent( - """ + pytester.makepyfile(_prelude + dedent(""" @pytest_asyncio.fixture async def var_fixture(): with context_var_manager("value"): @@ -210,18 +175,14 @@ async def same_var_fixture(var_fixture): @pytest.mark.asyncio async def test(same_var_fixture): assert _context_var.get() == "value" - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) def test_no_isolation_against_context_changes_in_sync_tests(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """ + pytester.makepyfile(dedent(""" import pytest import pytest_asyncio from contextvars import ContextVar @@ -234,9 +195,7 @@ def test_sync(): @pytest.mark.asyncio async def test_async(): assert _context_var.get() == "new_value" - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=2) @@ -246,9 +205,7 @@ def test_isolation_against_context_changes_in_async_tests( pytester: Pytester, loop_scope: Literal["function", "module"] ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - f""" + pytester.makepyfile(dedent(f""" import pytest import pytest_asyncio from contextvars import ContextVar @@ -263,8 +220,6 @@ async def test_async_first(): async def test_async_second(): with pytest.raises(LookupError): _context_var.get() - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=2) diff --git a/tests/async_fixtures/test_shared_module_fixture.py b/tests/async_fixtures/test_shared_module_fixture.py index 3295c83a..f37c0bb7 100644 --- a/tests/async_fixtures/test_shared_module_fixture.py +++ b/tests/async_fixtures/test_shared_module_fixture.py @@ -9,30 +9,24 @@ def test_asyncio_mark_provides_package_scoped_loop_strict_mode(pytester: Pyteste pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - conftest=dedent( - """\ + conftest=dedent("""\ import pytest_asyncio @pytest_asyncio.fixture(loop_scope="module", scope="module") async def async_shared_module_fixture(): return True - """ - ), - test_module_one=dedent( - """\ + """), + test_module_one=dedent("""\ import pytest @pytest.mark.asyncio async def test_shared_module_fixture_use_a(async_shared_module_fixture): assert async_shared_module_fixture is True - """ - ), - test_module_two=dedent( - """\ + """), + test_module_two=dedent("""\ import pytest @pytest.mark.asyncio async def test_shared_module_fixture_use_b(async_shared_module_fixture): assert async_shared_module_fixture is True - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=2) diff --git a/tests/hypothesis/test_base.py b/tests/hypothesis/test_base.py index 76392ee5..813db895 100644 --- a/tests/hypothesis/test_base.py +++ b/tests/hypothesis/test_base.py @@ -14,9 +14,7 @@ def test_hypothesis_given_decorator_before_asyncio_mark(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest from hypothesis import given, strategies as st @@ -24,9 +22,7 @@ def test_hypothesis_given_decorator_before_asyncio_mark(pytester: Pytester): @pytest.mark.asyncio async def test_mark_inner(n): assert isinstance(n, int) - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict", "-W default") result.assert_outcomes(passed=1) @@ -47,9 +43,7 @@ async def test_mark_and_parametrize(x, y): def test_async_auto_marked(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest from hypothesis import given @@ -60,9 +54,7 @@ def test_async_auto_marked(pytester: Pytester): @given(n=st.integers()) async def test_hypothesis(n: int): assert isinstance(n, int) - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(passed=1) @@ -70,9 +62,7 @@ async def test_hypothesis(n: int): def test_sync_not_auto_marked(pytester: Pytester): """Assert that synchronous Hypothesis functions are not marked with asyncio""" pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest from hypothesis import given @@ -85,8 +75,6 @@ def test_hypothesis(request, n: int): markers = [marker.name for marker in request.node.own_markers] assert "asyncio" not in markers assert isinstance(n, int) - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(passed=1) diff --git a/tests/markers/test_class_scope.py b/tests/markers/test_class_scope.py index 15a8fdc4..5394c2e9 100644 --- a/tests/markers/test_class_scope.py +++ b/tests/markers/test_class_scope.py @@ -34,9 +34,7 @@ def test_asyncio_mark_provides_class_scoped_loop_when_applied_to_functions( pytester: pytest.Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -50,9 +48,7 @@ async def test_remember_loop(self): @pytest.mark.asyncio(loop_scope="class") async def test_this_runs_in_same_loop(self): assert asyncio.get_running_loop() is TestClassScopedLoop.loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=2) @@ -61,9 +57,7 @@ def test_asyncio_mark_provides_class_scoped_loop_when_applied_to_class( pytester: pytest.Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -76,18 +70,14 @@ async def test_remember_loop(self): async def test_this_runs_in_same_loop(self): assert asyncio.get_running_loop() is TestClassScopedLoop.loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=2) def test_asyncio_mark_is_inherited_to_subclasses(pytester: pytest.Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -103,9 +93,7 @@ async def test_remember_loop(self): async def test_this_runs_in_same_loop(self): assert asyncio.get_running_loop() is TestWithoutMark.loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=2) @@ -114,9 +102,7 @@ def test_asyncio_mark_respects_the_loop_policy( pytester: pytest.Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -141,9 +127,7 @@ async def test_does_not_use_custom_event_loop_policy(): asyncio.get_event_loop_policy(), CustomEventLoopPolicy, ) - """ - ) - ) + """)) pytest_args = ["--asyncio-mode=strict"] if sys.version_info >= (3, 14): pytest_args.extend(["-W", "default"]) @@ -159,9 +143,7 @@ def test_asyncio_mark_respects_parametrized_loop_policies( pytester: pytest.Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -180,9 +162,7 @@ def event_loop_policy(request): class TestWithDifferentLoopPolicies: async def test_parametrized_loop(self, request): pass - """ - ) - ) + """)) pytest_args = ["--asyncio-mode=strict"] if sys.version_info >= (3, 14): pytest_args.extend(["-W", "default"]) @@ -198,9 +178,7 @@ def test_asyncio_mark_provides_class_scoped_loop_to_fixtures( pytester: pytest.Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -217,9 +195,7 @@ async def my_fixture(self): @pytest.mark.asyncio async def test_runs_is_same_loop_as_fixture(self, my_fixture): assert asyncio.get_running_loop() is TestClassScopedLoop.loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -229,8 +205,7 @@ def test_asyncio_mark_allows_combining_class_scoped_fixture_with_function_scoped ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( - dedent( - """\ + dedent("""\ import asyncio import pytest @@ -249,8 +224,7 @@ async def test_runs_in_different_loop_as_fixture(self, async_fixture): global loop assert asyncio.get_running_loop() is not loop - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -260,9 +234,7 @@ def test_asyncio_mark_handles_missing_event_loop_triggered_by_fixture( pytester: pytest.Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest import asyncio @@ -285,9 +257,7 @@ def sets_event_loop_to_none(self): @pytest.mark.parametrize("n", (0, 1)) async def test_does_not_fail(self, sets_event_loop_to_none, n): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=2) @@ -296,17 +266,13 @@ def test_standalone_test_does_not_trigger_warning_about_no_current_event_loop_be pytester: pytest.Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest @pytest.mark.asyncio(loop_scope="class") class TestClass: async def test_anything(self): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(warnings=0, passed=1) diff --git a/tests/markers/test_function_scope.py b/tests/markers/test_function_scope.py index 97ea08e6..5005a69a 100644 --- a/tests/markers/test_function_scope.py +++ b/tests/markers/test_function_scope.py @@ -8,9 +8,7 @@ def test_asyncio_mark_provides_function_scoped_loop_strict_mode(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -25,18 +23,14 @@ async def test_remember_loop(): async def test_does_not_run_in_same_loop(): global loop assert asyncio.get_running_loop() is not loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=2) def test_loop_scope_function_provides_function_scoped_event_loop(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -51,43 +45,33 @@ async def test_remember_loop(): async def test_does_not_run_in_same_loop(): global loop assert asyncio.get_running_loop() is not loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=2) def test_raises_when_scope_and_loop_scope_arguments_are_present(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest @pytest.mark.asyncio(scope="function", loop_scope="function") async def test_raises(): ... - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(errors=1) def test_warns_when_scope_argument_is_present(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest @pytest.mark.asyncio(scope="function") async def test_warns(): ... - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict", "-W", "default") result.assert_outcomes(passed=1, warnings=1) result.stdout.fnmatch_lines("*DeprecationWarning*") @@ -98,8 +82,7 @@ def test_asyncio_mark_respects_the_loop_policy( ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( - dedent( - """\ + dedent("""\ import asyncio import pytest @@ -117,8 +100,7 @@ async def test_uses_custom_event_loop_policy(): asyncio.get_event_loop_policy(), CustomEventLoopPolicy, ) - """ - ), + """), ) pytest_args = ["--asyncio-mode=strict"] if sys.version_info >= (3, 14): @@ -135,9 +117,7 @@ def test_asyncio_mark_respects_parametrized_loop_policies( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -162,9 +142,7 @@ async def test_parametrized_loop(): asyncio.get_event_loop_policy(), CustomEventLoopPolicy, ) - """ - ) - ) + """)) pytest_args = ["--asyncio-mode=strict"] if sys.version_info >= (3, 14): pytest_args.extend(["-W", "default"]) @@ -180,9 +158,7 @@ def test_asyncio_mark_provides_function_scoped_loop_to_fixtures( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -200,9 +176,7 @@ async def my_fixture(): async def test_runs_is_same_loop_as_fixture(my_fixture): global loop assert asyncio.get_running_loop() is loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -211,9 +185,7 @@ def test_asyncio_mark_handles_missing_event_loop_triggered_by_fixture( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest import asyncio @@ -235,9 +207,7 @@ def sets_event_loop_to_none(): @pytest.mark.parametrize("n", (0, 1)) async def test_does_not_fail(sets_event_loop_to_none, n): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=2) @@ -246,17 +216,13 @@ def test_standalone_test_does_not_trigger_warning_about_no_current_event_loop_be pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest @pytest.mark.asyncio async def test_anything(): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict", "--assert=plain") result.assert_outcomes(warnings=0, passed=1) @@ -265,19 +231,13 @@ def test_asyncio_mark_does_not_duplicate_other_marks_in_auto_mode( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makeconftest( - dedent( - """\ + pytester.makeconftest(dedent("""\ def pytest_configure(config): config.addinivalue_line( "markers", "dummy_marker: mark used for testing purposes" ) - """ - ) - ) - pytester.makepyfile( - dedent( - """\ + """)) + pytester.makepyfile(dedent("""\ import pytest @pytest.mark.dummy_marker @@ -286,8 +246,6 @@ async def test_markers_not_duplicated(request): for node, marker in request.node.iter_markers_with_node(): markers.append(marker) assert len(markers) == 2 - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto", "--assert=plain") result.assert_outcomes(warnings=0, passed=1) diff --git a/tests/markers/test_invalid_arguments.py b/tests/markers/test_invalid_arguments.py index 2663ef95..19aa3126 100644 --- a/tests/markers/test_invalid_arguments.py +++ b/tests/markers/test_invalid_arguments.py @@ -9,17 +9,13 @@ def test_no_error_when_scope_passed_as_sole_keyword_argument( pytester: pytest.Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest @pytest.mark.asyncio(loop_scope="session") async def test_anything(): pass - """ - ) - ) + """)) result = pytester.runpytest("--assert=plain") result.assert_outcomes(passed=1) result.stdout.no_fnmatch_line("*ValueError*") @@ -29,17 +25,13 @@ def test_error_when_scope_passed_as_positional_argument( pytester: pytest.Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest @pytest.mark.asyncio("session") async def test_anything(): pass - """ - ) - ) + """)) result = pytester.runpytest("--assert=plain") result.assert_outcomes(errors=1) result.stdout.fnmatch_lines( @@ -51,17 +43,13 @@ def test_error_when_wrong_keyword_argument_is_passed( pytester: pytest.Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest @pytest.mark.asyncio(cope="session") async def test_anything(): pass - """ - ) - ) + """)) result = pytester.runpytest("--assert=plain") result.assert_outcomes(errors=1) result.stdout.fnmatch_lines( @@ -73,17 +61,13 @@ def test_error_when_additional_keyword_arguments_are_passed( pytester: pytest.Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest @pytest.mark.asyncio(loop_scope="session", more="stuff") async def test_anything(): pass - """ - ) - ) + """)) result = pytester.runpytest("--assert=plain") result.assert_outcomes(errors=1) result.stdout.fnmatch_lines( diff --git a/tests/markers/test_mixed_scope.py b/tests/markers/test_mixed_scope.py index 40eaaa35..29133f7e 100644 --- a/tests/markers/test_mixed_scope.py +++ b/tests/markers/test_mixed_scope.py @@ -7,9 +7,7 @@ def test_function_scoped_loop_restores_previous_loop_scope(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -29,8 +27,6 @@ async def test_with_function_scoped_loop(): async def test_runs_in_same_loop(): global module_loop assert asyncio.get_running_loop() is module_loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=3) diff --git a/tests/markers/test_module_scope.py b/tests/markers/test_module_scope.py index 633e6121..3f7ee3e9 100644 --- a/tests/markers/test_module_scope.py +++ b/tests/markers/test_module_scope.py @@ -8,9 +8,7 @@ def test_asyncio_mark_provides_module_scoped_loop_strict_mode(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -30,9 +28,7 @@ class TestClassA: async def test_this_runs_in_same_loop(self): global loop assert asyncio.get_running_loop() is loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=3) @@ -43,16 +39,13 @@ def test_asyncio_mark_respects_the_loop_policy( pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - custom_policy=dedent( - """\ + custom_policy=dedent("""\ import asyncio class CustomEventLoopPolicy(asyncio.DefaultEventLoopPolicy): pass - """ - ), - test_uses_custom_policy=dedent( - """\ + """), + test_uses_custom_policy=dedent("""\ import asyncio import pytest @@ -69,10 +62,8 @@ async def test_uses_custom_event_loop_policy(): asyncio.get_event_loop_policy(), CustomEventLoopPolicy, ) - """ - ), - test_does_not_use_custom_policy=dedent( - """\ + """), + test_does_not_use_custom_policy=dedent("""\ import asyncio import pytest @@ -85,8 +76,7 @@ async def test_does_not_use_custom_event_loop_policy(): asyncio.get_event_loop_policy(), CustomEventLoopPolicy, ) - """ - ), + """), ) pytest_args = ["--asyncio-mode=strict"] if sys.version_info >= (3, 14): @@ -103,9 +93,7 @@ def test_asyncio_mark_respects_parametrized_loop_policies( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -124,9 +112,7 @@ def event_loop_policy(request): async def test_parametrized_loop(): pass - """ - ) - ) + """)) pytest_args = ["--asyncio-mode=strict"] if sys.version_info >= (3, 14): pytest_args.extend(["-W", "default"]) @@ -142,9 +128,7 @@ def test_asyncio_mark_provides_module_scoped_loop_to_fixtures( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -162,9 +146,7 @@ async def my_fixture(): async def test_runs_is_same_loop_as_fixture(my_fixture): global loop assert asyncio.get_running_loop() is loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -174,8 +156,7 @@ def test_asyncio_mark_allows_combining_module_scoped_fixture_with_class_scoped_t ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( - dedent( - """\ + dedent("""\ import asyncio import pytest @@ -194,8 +175,7 @@ async def test_runs_in_different_loop_as_fixture(self, async_fixture): global loop assert asyncio.get_running_loop() is not loop - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -207,8 +187,7 @@ def test_asyncio_mark_allows_combining_module_scoped_fixture_with_function_scope pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - test_mixed_scopes=dedent( - """\ + test_mixed_scopes=dedent("""\ import asyncio import pytest @@ -225,8 +204,7 @@ async def async_fixture(): async def test_runs_in_different_loop_as_fixture(async_fixture): global loop assert asyncio.get_running_loop() is not loop - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -237,8 +215,7 @@ def test_allows_combining_module_scoped_asyncgen_fixture_with_function_scoped_te ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( - dedent( - """\ + dedent("""\ import asyncio import pytest @@ -256,8 +233,7 @@ async def async_fixture(): async def test_runs_in_different_loop_as_fixture(async_fixture): global loop assert asyncio.get_running_loop() is not loop - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -267,9 +243,7 @@ def test_asyncio_mark_handles_missing_event_loop_triggered_by_fixture( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest import asyncio @@ -291,9 +265,7 @@ def sets_event_loop_to_none(): @pytest.mark.parametrize("n", (0, 1)) async def test_does_not_fail(sets_event_loop_to_none, n): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=2) @@ -302,16 +274,12 @@ def test_standalone_test_does_not_trigger_warning_about_no_current_event_loop_be pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest @pytest.mark.asyncio(loop_scope="module") async def test_anything(): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(warnings=0, passed=1) diff --git a/tests/markers/test_package_scope.py b/tests/markers/test_package_scope.py index 76bd85ea..0783eb9c 100644 --- a/tests/markers/test_package_scope.py +++ b/tests/markers/test_package_scope.py @@ -12,15 +12,12 @@ def test_asyncio_mark_provides_package_scoped_loop_strict_mode(pytester: Pyteste pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - shared_module=dedent( - """\ + shared_module=dedent("""\ import asyncio loop: asyncio.AbstractEventLoop = None - """ - ), - test_module_one=dedent( - f"""\ + """), + test_module_one=dedent(f"""\ import asyncio import pytest @@ -29,10 +26,8 @@ def test_asyncio_mark_provides_package_scoped_loop_strict_mode(pytester: Pyteste @pytest.mark.asyncio(loop_scope="package") async def test_remember_loop(): shared_module.loop = asyncio.get_running_loop() - """ - ), - test_module_two=dedent( - f"""\ + """), + test_module_two=dedent(f"""\ import asyncio import pytest @@ -46,14 +41,11 @@ async def test_this_runs_in_same_loop(): class TestClassA: async def test_this_runs_in_same_loop(self): assert asyncio.get_running_loop() is shared_module.loop - """ - ), + """), ) subpkg = pytester.mkpydir(subpackage_name) subpkg.joinpath("__init__.py").touch() - subpkg.joinpath("test_subpkg.py").write_text( - dedent( - f"""\ + subpkg.joinpath("test_subpkg.py").write_text(dedent(f"""\ import asyncio import pytest @@ -63,9 +55,7 @@ async def test_this_runs_in_same_loop(self): async def test_subpackage_runs_in_different_loop(): assert asyncio.get_running_loop() is not shared_module.loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=4) @@ -76,8 +66,7 @@ def test_asyncio_mark_respects_the_loop_policy( pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - conftest=dedent( - """\ + conftest=dedent("""\ import pytest from .custom_policy import CustomEventLoopPolicy @@ -85,18 +74,14 @@ def test_asyncio_mark_respects_the_loop_policy( @pytest.fixture(scope="package") def event_loop_policy(): return CustomEventLoopPolicy() - """ - ), - custom_policy=dedent( - """\ + """), + custom_policy=dedent("""\ import asyncio class CustomEventLoopPolicy(asyncio.DefaultEventLoopPolicy): pass - """ - ), - test_uses_custom_policy=dedent( - """\ + """), + test_uses_custom_policy=dedent("""\ import asyncio import pytest @@ -109,10 +94,8 @@ async def test_uses_custom_event_loop_policy(): asyncio.get_event_loop_policy(), CustomEventLoopPolicy, ) - """ - ), - test_also_uses_custom_policy=dedent( - """\ + """), + test_also_uses_custom_policy=dedent("""\ import asyncio import pytest @@ -125,8 +108,7 @@ async def test_also_uses_custom_event_loop_policy(): asyncio.get_event_loop_policy(), CustomEventLoopPolicy, ) - """ - ), + """), ) pytest_args = ["--asyncio-mode=strict"] if sys.version_info >= (3, 14): @@ -145,8 +127,7 @@ def test_asyncio_mark_respects_parametrized_loop_policies( pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - test_parametrization=dedent( - """\ + test_parametrization=dedent("""\ import asyncio import pytest @@ -165,8 +146,7 @@ def event_loop_policy(request): async def test_parametrized_loop(): pass - """ - ), + """), ) pytest_args = ["--asyncio-mode=strict"] if sys.version_info >= (3, 14): @@ -186,8 +166,7 @@ def test_asyncio_mark_provides_package_scoped_loop_to_fixtures( package_name = pytester.path.name pytester.makepyfile( __init__="", - conftest=dedent( - f"""\ + conftest=dedent(f"""\ import asyncio import pytest_asyncio @@ -197,17 +176,13 @@ def test_asyncio_mark_provides_package_scoped_loop_to_fixtures( @pytest_asyncio.fixture(loop_scope="package", scope="package") async def my_fixture(): shared_module.loop = asyncio.get_running_loop() - """ - ), - shared_module=dedent( - """\ + """), + shared_module=dedent("""\ import asyncio loop: asyncio.AbstractEventLoop = None - """ - ), - test_fixture_runs_in_scoped_loop=dedent( - f"""\ + """), + test_fixture_runs_in_scoped_loop=dedent(f"""\ import asyncio import pytest @@ -219,8 +194,7 @@ async def my_fixture(): async def test_runs_in_same_loop_as_fixture(my_fixture): assert asyncio.get_running_loop() is shared_module.loop - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -232,8 +206,7 @@ def test_asyncio_mark_allows_combining_package_scoped_fixture_with_module_scoped pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - test_mixed_scopes=dedent( - """\ + test_mixed_scopes=dedent("""\ import asyncio import pytest @@ -250,8 +223,7 @@ async def async_fixture(): async def test_runs_in_different_loop_as_fixture(async_fixture): global loop assert asyncio.get_running_loop() is not loop - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -263,8 +235,7 @@ def test_asyncio_mark_allows_combining_package_scoped_fixture_with_class_scoped_ pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - test_mixed_scopes=dedent( - """\ + test_mixed_scopes=dedent("""\ import asyncio import pytest @@ -282,8 +253,7 @@ class TestMixedScopes: async def test_runs_in_different_loop_as_fixture(self, async_fixture): global loop assert asyncio.get_running_loop() is not loop - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -295,8 +265,7 @@ def test_asyncio_mark_allows_combining_package_scoped_fixture_with_function_scop pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - test_mixed_scopes=dedent( - """\ + test_mixed_scopes=dedent("""\ import asyncio import pytest @@ -313,8 +282,7 @@ async def async_fixture(): async def test_runs_in_different_loop_as_fixture(async_fixture): global loop assert asyncio.get_running_loop() is not loop - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -326,8 +294,7 @@ def test_asyncio_mark_handles_missing_event_loop_triggered_by_fixture( pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - test_loop_is_none=dedent( - """\ + test_loop_is_none=dedent("""\ import pytest import asyncio @@ -349,8 +316,7 @@ def sets_event_loop_to_none(): @pytest.mark.parametrize("n", (0, 1)) async def test_does_not_fail(sets_event_loop_to_none, n): pass - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=2) diff --git a/tests/markers/test_session_scope.py b/tests/markers/test_session_scope.py index 88eec6c3..66ea5ed8 100644 --- a/tests/markers/test_session_scope.py +++ b/tests/markers/test_session_scope.py @@ -11,15 +11,12 @@ def test_asyncio_mark_provides_session_scoped_loop_strict_mode(pytester: Pyteste pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - shared_module=dedent( - """\ + shared_module=dedent("""\ import asyncio loop: asyncio.AbstractEventLoop = None - """ - ), - test_module_one=dedent( - f"""\ + """), + test_module_one=dedent(f"""\ import asyncio import pytest @@ -28,10 +25,8 @@ def test_asyncio_mark_provides_session_scoped_loop_strict_mode(pytester: Pyteste @pytest.mark.asyncio(loop_scope="session") async def test_remember_loop(): shared_module.loop = asyncio.get_running_loop() - """ - ), - test_module_two=dedent( - f"""\ + """), + test_module_two=dedent(f"""\ import asyncio import pytest @@ -45,16 +40,13 @@ async def test_this_runs_in_same_loop(): class TestClassA: async def test_this_runs_in_same_loop(self): assert asyncio.get_running_loop() is shared_module.loop - """ - ), + """), ) # subpackage_name must alphabetically come after test_module_one.py subpackage_name = "z_subpkg" subpkg = pytester.mkpydir(subpackage_name) - subpkg.joinpath("test_subpkg.py").write_text( - dedent( - f"""\ + subpkg.joinpath("test_subpkg.py").write_text(dedent(f"""\ import asyncio import pytest @@ -64,9 +56,7 @@ async def test_this_runs_in_same_loop(self): async def test_subpackage_runs_in_same_loop(): assert asyncio.get_running_loop() is shared_module.loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=4) @@ -77,8 +67,7 @@ def test_asyncio_mark_respects_the_loop_policy( pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - conftest=dedent( - """\ + conftest=dedent("""\ import pytest from .custom_policy import CustomEventLoopPolicy @@ -86,18 +75,14 @@ def test_asyncio_mark_respects_the_loop_policy( @pytest.fixture(scope="session") def event_loop_policy(): return CustomEventLoopPolicy() - """ - ), - custom_policy=dedent( - """\ + """), + custom_policy=dedent("""\ import asyncio class CustomEventLoopPolicy(asyncio.DefaultEventLoopPolicy): pass - """ - ), - test_uses_custom_policy=dedent( - """\ + """), + test_uses_custom_policy=dedent("""\ import asyncio import pytest @@ -110,10 +95,8 @@ async def test_uses_custom_event_loop_policy(): asyncio.get_event_loop_policy(), CustomEventLoopPolicy, ) - """ - ), - test_also_uses_custom_policy=dedent( - """\ + """), + test_also_uses_custom_policy=dedent("""\ import asyncio import pytest @@ -126,8 +109,7 @@ async def test_also_uses_custom_event_loop_policy(): asyncio.get_event_loop_policy(), CustomEventLoopPolicy, ) - """ - ), + """), ) pytest_args = ["--asyncio-mode=strict"] if sys.version_info >= (3, 14): @@ -146,8 +128,7 @@ def test_asyncio_mark_respects_parametrized_loop_policies( pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - test_parametrization=dedent( - """\ + test_parametrization=dedent("""\ import asyncio import pytest @@ -166,8 +147,7 @@ def event_loop_policy(request): async def test_parametrized_loop(): pass - """ - ), + """), ) pytest_args = ["--asyncio-mode=strict"] if sys.version_info >= (3, 14): @@ -187,8 +167,7 @@ def test_asyncio_mark_provides_session_scoped_loop_to_fixtures( pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - conftest=dedent( - f"""\ + conftest=dedent(f"""\ import asyncio import pytest_asyncio @@ -198,21 +177,16 @@ def test_asyncio_mark_provides_session_scoped_loop_to_fixtures( @pytest_asyncio.fixture(loop_scope="session", scope="session") async def my_fixture(): shared_module.loop = asyncio.get_running_loop() - """ - ), - shared_module=dedent( - """\ + """), + shared_module=dedent("""\ import asyncio loop: asyncio.AbstractEventLoop = None - """ - ), + """), ) subpackage_name = "subpkg" subpkg = pytester.mkpydir(subpackage_name) - subpkg.joinpath("test_subpkg.py").write_text( - dedent( - f"""\ + subpkg.joinpath("test_subpkg.py").write_text(dedent(f"""\ import asyncio import pytest @@ -224,9 +198,7 @@ async def my_fixture(): async def test_runs_in_same_loop_as_fixture(my_fixture): assert asyncio.get_running_loop() is shared_module.loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -237,8 +209,7 @@ def test_asyncio_mark_allows_combining_session_scoped_fixture_with_package_scope pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - test_mixed_scopes=dedent( - """\ + test_mixed_scopes=dedent("""\ import asyncio import pytest @@ -255,8 +226,7 @@ async def async_fixture(): async def test_runs_in_different_loop_as_fixture(async_fixture): global loop assert asyncio.get_running_loop() is not loop - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -268,8 +238,7 @@ def test_asyncio_mark_allows_combining_session_scoped_fixture_with_module_scoped pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - test_mixed_scopes=dedent( - """\ + test_mixed_scopes=dedent("""\ import asyncio import pytest @@ -286,8 +255,7 @@ async def async_fixture(): async def test_runs_in_different_loop_as_fixture(async_fixture): global loop assert asyncio.get_running_loop() is not loop - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -299,8 +267,7 @@ def test_asyncio_mark_allows_combining_session_scoped_fixture_with_class_scoped_ pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - test_mixed_scopes=dedent( - """\ + test_mixed_scopes=dedent("""\ import asyncio import pytest @@ -318,8 +285,7 @@ class TestMixedScopes: async def test_runs_in_different_loop_as_fixture(self, async_fixture): global loop assert asyncio.get_running_loop() is not loop - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -331,8 +297,7 @@ def test_asyncio_mark_allows_combining_session_scoped_fixture_with_function_scop pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - test_mixed_scopes=dedent( - """\ + test_mixed_scopes=dedent("""\ import asyncio import pytest @@ -349,8 +314,7 @@ async def async_fixture(): async def test_runs_in_different_loop_as_fixture(async_fixture): global loop assert asyncio.get_running_loop() is not loop - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -362,8 +326,7 @@ def test_allows_combining_session_scoped_asyncgen_fixture_with_function_scoped_t pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - test_mixed_scopes=dedent( - """\ + test_mixed_scopes=dedent("""\ import asyncio import pytest @@ -381,8 +344,7 @@ async def async_fixture(): async def test_runs_in_different_loop_as_fixture(async_fixture): global loop assert asyncio.get_running_loop() is not loop - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -392,9 +354,7 @@ def test_asyncio_mark_handles_missing_event_loop_triggered_by_fixture( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest import asyncio @@ -416,9 +376,7 @@ def sets_event_loop_to_none(): @pytest.mark.parametrize("n", (0, 1)) async def test_does_not_fail(sets_event_loop_to_none, n): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=2) @@ -427,16 +385,12 @@ def test_standalone_test_does_not_trigger_warning_about_no_current_event_loop_be pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest @pytest.mark.asyncio(loop_scope="session") async def test_anything(): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(warnings=0, passed=1) diff --git a/tests/modes/test_auto_mode.py b/tests/modes/test_auto_mode.py index 21c48d87..7c6bea29 100644 --- a/tests/modes/test_auto_mode.py +++ b/tests/modes/test_auto_mode.py @@ -7,9 +7,7 @@ def test_auto_mode_cmdline(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -17,26 +15,18 @@ def test_auto_mode_cmdline(pytester: Pytester): async def test_a(): await asyncio.sleep(0) - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(passed=1) def test_auto_mode_cfg(pytester: Pytester): - pytester.makeini( - dedent( - """\ + pytester.makeini(dedent("""\ [pytest] asyncio_default_fixture_loop_scope = function asyncio_mode = auto - """ - ) - ) - pytester.makepyfile( - dedent( - """\ + """)) + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -44,18 +34,14 @@ def test_auto_mode_cfg(pytester: Pytester): async def test_a(): await asyncio.sleep(0) - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(passed=1) def test_auto_mode_async_fixture(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -69,18 +55,14 @@ async def fixture_a(): async def test_a(fixture_a): await asyncio.sleep(0) assert fixture_a == 1 - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(passed=1) def test_auto_mode_method_fixture(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -97,18 +79,14 @@ async def fixture_a(self): async def test_a(self, fixture_a): await asyncio.sleep(0) assert fixture_a == 1 - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(passed=1) def test_auto_mode_static_method(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio pytest_plugins = 'pytest_asyncio' @@ -119,18 +97,14 @@ class TestA: @staticmethod async def test_a(): await asyncio.sleep(0) - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(passed=1) def test_auto_mode_static_method_fixture(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -149,8 +123,6 @@ async def fixture_a(): async def test_a(fixture_a): await asyncio.sleep(0) assert fixture_a == 1 - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(passed=1) diff --git a/tests/modes/test_strict_mode.py b/tests/modes/test_strict_mode.py index 0a467c18..be4d8d99 100644 --- a/tests/modes/test_strict_mode.py +++ b/tests/modes/test_strict_mode.py @@ -7,9 +7,7 @@ def test_strict_mode_cmdline(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -18,26 +16,18 @@ def test_strict_mode_cmdline(pytester: Pytester): @pytest.mark.asyncio async def test_a(): await asyncio.sleep(0) - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) def test_strict_mode_cfg(pytester: Pytester): - pytester.makeini( - dedent( - """\ + pytester.makeini(dedent("""\ [pytest] asyncio_default_fixture_loop_scope = function asyncio_mode = strict - """ - ) - ) - pytester.makepyfile( - dedent( - """\ + """)) + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -46,18 +36,14 @@ def test_strict_mode_cfg(pytester: Pytester): @pytest.mark.asyncio async def test_a(): await asyncio.sleep(0) - """ - ) - ) + """)) result = pytester.runpytest() result.assert_outcomes(passed=1) def test_strict_mode_method_fixture(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest import pytest_asyncio @@ -75,25 +61,19 @@ async def fixture_a(self): async def test_a(self, fixture_a): await asyncio.sleep(0) assert fixture_a == 1 - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(passed=1) def test_strict_mode_ignores_unmarked_coroutine(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest async def test_anything(): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict", "-W default", "--assert=plain") if pytest_version >= (8, 4, 0): result.assert_outcomes(failed=1, skipped=0, warnings=0) @@ -104,9 +84,7 @@ async def test_anything(): def test_strict_mode_ignores_unmarked_fixture(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest # Not using pytest_asyncio.fixture @@ -116,9 +94,7 @@ async def any_fixture(): async def test_anything(any_fixture): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict", "-W default", "--assert=plain") if pytest_version >= (8, 4, 0): @@ -135,9 +111,7 @@ async def test_anything(any_fixture): def test_strict_mode_marked_test_unmarked_fixture_warning(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest # Not using pytest_asyncio.fixture @@ -152,9 +126,7 @@ async def test_anything(any_fixture): any_fixture.send(None) except StopIteration: pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict", "-W default", "--assert=plain") if pytest_version >= (8, 4, 0): result.assert_outcomes(passed=1, failed=0, skipped=0, warnings=2) @@ -182,9 +154,7 @@ async def test_anything(any_fixture): # autouse is not handled in any special way currently def test_strict_mode_marked_test_unmarked_autouse_fixture_warning(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest # Not using pytest_asyncio.fixture @@ -199,9 +169,7 @@ async def test_anything(any_fixture): any_fixture.send(None) except StopIteration: pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict", "-W default", "--assert=plain") if pytest_version >= (8, 4, 0): result.assert_outcomes(passed=1, warnings=2) diff --git a/tests/test_asyncio_debug.py b/tests/test_asyncio_debug.py index b097b63c..4c40a2fc 100644 --- a/tests/test_asyncio_debug.py +++ b/tests/test_asyncio_debug.py @@ -8,9 +8,7 @@ def test_asyncio_debug_disabled_by_default(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -20,18 +18,14 @@ def test_asyncio_debug_disabled_by_default(pytester: Pytester): async def test_debug_mode_disabled(): loop = asyncio.get_running_loop() assert not loop.get_debug() - """ - ) - ) + """)) result = pytester.runpytest() result.assert_outcomes(passed=1) def test_asyncio_debug_enabled_via_cli_option(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -41,27 +35,19 @@ def test_asyncio_debug_enabled_via_cli_option(pytester: Pytester): async def test_debug_mode_enabled(): loop = asyncio.get_running_loop() assert loop.get_debug() - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-debug") result.assert_outcomes(passed=1) @pytest.mark.parametrize("config_value", ("true", "1")) def test_asyncio_debug_enabled_via_config_option(pytester: Pytester, config_value: str): - pytester.makeini( - dedent( - f"""\ + pytester.makeini(dedent(f"""\ [pytest] asyncio_default_fixture_loop_scope = function asyncio_debug = {config_value} - """ - ) - ) - pytester.makepyfile( - dedent( - """\ + """)) + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -71,9 +57,7 @@ def test_asyncio_debug_enabled_via_config_option(pytester: Pytester, config_valu async def test_debug_mode_enabled(): loop = asyncio.get_running_loop() assert loop.get_debug() - """ - ) - ) + """)) result = pytester.runpytest() result.assert_outcomes(passed=1) @@ -83,18 +67,12 @@ def test_asyncio_debug_disabled_via_config_option( pytester: Pytester, config_value: str, ): - pytester.makeini( - dedent( - f"""\ + pytester.makeini(dedent(f"""\ [pytest] asyncio_default_fixture_loop_scope = function asyncio_debug = {config_value} - """ - ) - ) - pytester.makepyfile( - dedent( - """\ + """)) + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -104,9 +82,7 @@ def test_asyncio_debug_disabled_via_config_option( async def test_debug_mode_disabled(): loop = asyncio.get_running_loop() assert not loop.get_debug() - """ - ) - ) + """)) result = pytester.runpytest() result.assert_outcomes(passed=1) @@ -115,9 +91,7 @@ def test_asyncio_debug_cli_option_overrides_config(pytester: Pytester): pytester.makeini( "[pytest]\nasyncio_default_fixture_loop_scope = function\nasyncio_debug = false" ) - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -127,9 +101,7 @@ def test_asyncio_debug_cli_option_overrides_config(pytester: Pytester): async def test_debug_mode_enabled(): loop = asyncio.get_running_loop() assert loop.get_debug() - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-debug") result.assert_outcomes(passed=1) @@ -137,9 +109,7 @@ async def test_debug_mode_enabled(): @pytest.mark.parametrize("loop_scope", ("function", "module", "session")) def test_asyncio_debug_with_different_loop_scopes(pytester: Pytester, loop_scope: str): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - f"""\ + pytester.makepyfile(dedent(f"""\ import asyncio import pytest @@ -149,18 +119,14 @@ def test_asyncio_debug_with_different_loop_scopes(pytester: Pytester, loop_scope async def test_debug_mode_with_scope(): loop = asyncio.get_running_loop() assert loop.get_debug() - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-debug") result.assert_outcomes(passed=1) def test_asyncio_debug_with_async_fixtures(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest import pytest_asyncio @@ -178,18 +144,14 @@ async def test_debug_mode_with_fixture(async_fixture): loop = asyncio.get_running_loop() assert loop.get_debug() assert async_fixture == "fixture_value" - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-debug") result.assert_outcomes(passed=1) def test_asyncio_debug_multiple_test_functions(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -209,8 +171,6 @@ async def test_debug_second(): async def test_debug_third(): loop = asyncio.get_running_loop() assert loop.get_debug() - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-debug") result.assert_outcomes(passed=3) diff --git a/tests/test_asyncio_fixture.py b/tests/test_asyncio_fixture.py index 91e5d8d4..32cd7d8c 100644 --- a/tests/test_asyncio_fixture.py +++ b/tests/test_asyncio_fixture.py @@ -48,9 +48,7 @@ async def test_fixture_with_params(fixture_with_params): @pytest.mark.parametrize("mode", ("auto", "strict")) def test_sync_function_uses_async_fixture(pytester: Pytester, mode): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest_asyncio pytest_plugins = 'pytest_asyncio' @@ -61,8 +59,6 @@ async def always_true(): def test_sync_function_uses_async_fixture(always_true): assert always_true is True - """ - ) - ) + """)) result = pytester.runpytest(f"--asyncio-mode={mode}") result.assert_outcomes(passed=1) diff --git a/tests/test_asyncio_mark.py b/tests/test_asyncio_mark.py index f4e88ba2..a04240b4 100644 --- a/tests/test_asyncio_mark.py +++ b/tests/test_asyncio_mark.py @@ -7,17 +7,13 @@ def test_asyncio_mark_on_sync_function_emits_warning(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest @pytest.mark.asyncio def test_a(): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict", "-W default", "--assert=plain") result.assert_outcomes(passed=1) result.stdout.fnmatch_lines( @@ -29,17 +25,13 @@ def test_asyncio_mark_on_async_generator_function_emits_warning_in_strict_mode( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest @pytest.mark.asyncio async def test_a(): yield - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict", "-W default", "--assert=plain") result.assert_outcomes(xfailed=1, warnings=1) result.stdout.fnmatch_lines( @@ -51,14 +43,10 @@ def test_asyncio_mark_on_async_generator_function_emits_warning_in_auto_mode( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ async def test_a(): yield - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto", "-W default", "--assert=plain") result.assert_outcomes(xfailed=1, warnings=1) result.stdout.fnmatch_lines( @@ -70,18 +58,14 @@ def test_asyncio_mark_on_async_generator_method_emits_warning_in_strict_mode( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest class TestAsyncGenerator: @pytest.mark.asyncio async def test_a(self): yield - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict", "-W default", "--assert=plain") result.assert_outcomes(xfailed=1, warnings=1) result.stdout.fnmatch_lines( @@ -93,16 +77,12 @@ def test_asyncio_mark_on_async_generator_method_emits_warning_in_auto_mode( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ class TestAsyncGenerator: @staticmethod async def test_a(): yield - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto", "-W default", "--assert=plain") result.assert_outcomes(xfailed=1, warnings=1) result.stdout.fnmatch_lines( @@ -114,9 +94,7 @@ def test_asyncio_mark_on_async_generator_staticmethod_emits_warning_in_strict_mo pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest class TestAsyncGenerator: @@ -124,9 +102,7 @@ class TestAsyncGenerator: @pytest.mark.asyncio async def test_a(): yield - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict", "-W default", "--assert=plain") result.assert_outcomes(xfailed=1, warnings=1) result.stdout.fnmatch_lines( @@ -138,16 +114,12 @@ def test_asyncio_mark_on_async_generator_staticmethod_emits_warning_in_auto_mode pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ class TestAsyncGenerator: @staticmethod async def test_a(): yield - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto", "-W default", "--assert=plain") result.assert_outcomes(xfailed=1, warnings=1) result.stdout.fnmatch_lines( @@ -158,19 +130,13 @@ async def test_a(): def test_asyncio_marker_fallbacks_to_configured_default_loop_scope_if_not_set( pytester: Pytester, ): - pytester.makeini( - dedent( - """\ + pytester.makeini(dedent("""\ [pytest] asyncio_default_fixture_loop_scope = function asyncio_default_test_loop_scope = session - """ - ) - ) + """)) - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest_asyncio import pytest @@ -185,9 +151,7 @@ async def session_loop_fixture(): async def test_a(session_loop_fixture): global loop assert asyncio.get_running_loop() is loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(passed=1) @@ -196,19 +160,13 @@ async def test_a(session_loop_fixture): def test_asyncio_marker_uses_marker_loop_scope_even_if_config_is_set( pytester: Pytester, ): - pytester.makeini( - dedent( - """\ + pytester.makeini(dedent("""\ [pytest] asyncio_default_fixture_loop_scope = function asyncio_default_test_loop_scope = module - """ - ) - ) + """)) - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest_asyncio import pytest @@ -224,9 +182,7 @@ async def session_loop_fixture(): async def test_a(session_loop_fixture): global loop assert asyncio.get_running_loop() is loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(passed=1) diff --git a/tests/test_doctest.py b/tests/test_doctest.py index d175789e..5a455c45 100644 --- a/tests/test_doctest.py +++ b/tests/test_doctest.py @@ -8,15 +8,13 @@ def test_plugin_does_not_interfere_with_doctest_collection(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( - dedent( - '''\ + dedent('''\ def any_function(): """ >>> 42 42 """ - ''' - ), + '''), ) result = pytester.runpytest("--asyncio-mode=strict", "--doctest-modules") result.assert_outcomes(passed=1) @@ -27,8 +25,7 @@ def test_plugin_does_not_interfere_with_doctest_textfile_collection(pytester: Py pytester.makefile(".txt", "") # collected as DoctestTextfile pytester.makepyfile( __init__="", - test_python_file=dedent( - """\ + test_python_file=dedent("""\ import pytest pytest_plugins = "pytest_asyncio" @@ -36,8 +33,7 @@ def test_plugin_does_not_interfere_with_doctest_textfile_collection(pytester: Py @pytest.mark.asyncio async def test_anything(): pass - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) diff --git a/tests/test_event_loop_fixture.py b/tests/test_event_loop_fixture.py index 5a03341f..8902511b 100644 --- a/tests/test_event_loop_fixture.py +++ b/tests/test_event_loop_fixture.py @@ -9,9 +9,7 @@ def test_event_loop_fixture_handles_unclosed_async_gen( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -25,9 +23,7 @@ async def generator_fn(): gen = generator_fn() await gen.__anext__() - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict", "-W", "default") result.assert_outcomes(passed=1, warnings=0) @@ -36,9 +32,7 @@ def test_closing_event_loop_in_sync_fixture_teardown_raises_warning( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest import pytest_asyncio @@ -57,9 +51,7 @@ def close_event_loop(_event_loop): @pytest.mark.asyncio async def test_something(close_event_loop): await asyncio.sleep(0.01) - """ - ) - ) + """)) result = pytester.runpytest_subprocess("--asyncio-mode=strict", "--assert=plain") result.assert_outcomes(passed=1, warnings=1) result.stdout.fnmatch_lines( @@ -71,9 +63,7 @@ def test_event_loop_fixture_asyncgen_error( pytester: Pytester, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -86,8 +76,6 @@ async def test_something(): async def fail(): raise RuntimeError("mock error cleaning up...") loop.shutdown_asyncgens = fail - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict", "-W", "default") result.assert_outcomes(passed=1, warnings=1) diff --git a/tests/test_fixture_loop_scopes.py b/tests/test_fixture_loop_scopes.py index 037b561a..a25e56a0 100644 --- a/tests/test_fixture_loop_scopes.py +++ b/tests/test_fixture_loop_scopes.py @@ -14,9 +14,7 @@ def test_loop_scope_session_is_independent_of_fixture_scope( fixture_scope: str, ): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - f"""\ + pytester.makepyfile(dedent(f"""\ import asyncio import pytest import pytest_asyncio @@ -32,9 +30,7 @@ async def fixture(): async def test_runs_in_same_loop_as_fixture(fixture): global loop assert loop == asyncio.get_running_loop() - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -44,17 +40,11 @@ def test_default_loop_scope_config_option_changes_fixture_loop_scope( pytester: Pytester, default_loop_scope: str, ): - pytester.makeini( - dedent( - f"""\ + pytester.makeini(dedent(f"""\ [pytest] asyncio_default_fixture_loop_scope = {default_loop_scope} - """ - ) - ) - pytester.makepyfile( - dedent( - f"""\ + """)) + pytester.makepyfile(dedent(f"""\ import asyncio import pytest import pytest_asyncio @@ -66,9 +56,7 @@ async def fixture_loop(): @pytest.mark.asyncio(loop_scope="{default_loop_scope}") async def test_runs_in_fixture_loop(fixture_loop): assert asyncio.get_running_loop() is fixture_loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -76,17 +64,11 @@ async def test_runs_in_fixture_loop(fixture_loop): def test_default_class_loop_scope_config_option_changes_fixture_loop_scope( pytester: Pytester, ): - pytester.makeini( - dedent( - """\ + pytester.makeini(dedent("""\ [pytest] asyncio_default_fixture_loop_scope = class - """ - ) - ) - pytester.makepyfile( - dedent( - """\ + """)) + pytester.makepyfile(dedent("""\ import asyncio import pytest import pytest_asyncio @@ -99,9 +81,7 @@ async def fixture_loop(self): @pytest.mark.asyncio(loop_scope="class") async def test_runs_in_fixture_loop(self, fixture_loop): assert asyncio.get_running_loop() is fixture_loop - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) @@ -109,18 +89,13 @@ async def test_runs_in_fixture_loop(self, fixture_loop): def test_default_package_loop_scope_config_option_changes_fixture_loop_scope( pytester: Pytester, ): - pytester.makeini( - dedent( - """\ + pytester.makeini(dedent("""\ [pytest] asyncio_default_fixture_loop_scope = package - """ - ) - ) + """)) pytester.makepyfile( __init__="", - test_a=dedent( - """\ + test_a=dedent("""\ import asyncio import pytest import pytest_asyncio @@ -132,20 +107,17 @@ async def fixture_loop(): @pytest.mark.asyncio(loop_scope="package") async def test_runs_in_fixture_loop(fixture_loop): assert asyncio.get_running_loop() is fixture_loop - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) def test_invalid_default_fixture_loop_scope_raises_error(pytester: Pytester): - pytester.makeini( - """\ + pytester.makeini("""\ [pytest] asyncio_default_fixture_loop_scope = invalid_scope - """ - ) + """) result = pytester.runpytest("--assert=plain") result.stderr.fnmatch_lines( [ diff --git a/tests/test_import.py b/tests/test_import.py index 2272704a..a3b71f65 100644 --- a/tests/test_import.py +++ b/tests/test_import.py @@ -7,16 +7,12 @@ def test_import_warning_does_not_cause_internal_error(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ raise ImportWarning() async def test_errors_out(): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(errors=1) @@ -24,17 +20,13 @@ async def test_errors_out(): def test_import_warning_in_package_does_not_cause_internal_error(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( - __init__=dedent( - """\ + __init__=dedent("""\ raise ImportWarning() - """ - ), - test_a=dedent( - """\ + """), + test_a=dedent("""\ async def test_errors_out(): pass - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(errors=1) @@ -44,20 +36,16 @@ def test_does_not_import_unrelated_packages(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pkg_dir = pytester.mkpydir("mypkg") pkg_dir.joinpath("__init__.py").write_text( - dedent( - """\ + dedent("""\ raise ImportError() - """ - ), + """), ) test_dir = pytester.mkdir("tests") test_dir.joinpath("test_a.py").write_text( - dedent( - """\ + dedent("""\ async def test_passes(): pass - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(passed=1) diff --git a/tests/test_is_async_test.py b/tests/test_is_async_test.py index f99dc0d9..2d887e5a 100644 --- a/tests/test_is_async_test.py +++ b/tests/test_is_async_test.py @@ -7,9 +7,7 @@ def test_returns_false_for_sync_item(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest import pytest_asyncio @@ -23,18 +21,14 @@ def pytest_collection_modifyitems(items): if pytest_asyncio.is_async_test(item) ] assert len(async_tests) == 0 - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) def test_returns_true_for_marked_coroutine_item_in_strict_mode(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest import pytest_asyncio @@ -49,18 +43,14 @@ def pytest_collection_modifyitems(items): if pytest_asyncio.is_async_test(item) ] assert len(async_tests) == 1 - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1) def test_returns_false_for_unmarked_coroutine_item_in_strict_mode(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest import pytest_asyncio @@ -74,18 +64,14 @@ def pytest_collection_modifyitems(items): if pytest_asyncio.is_async_test(item) ] assert len(async_tests) == 0 - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(failed=1) def test_returns_true_for_unmarked_coroutine_item_in_auto_mode(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest import pytest_asyncio @@ -99,8 +85,6 @@ def pytest_collection_modifyitems(items): if pytest_asyncio.is_async_test(item) ] assert len(async_tests) == 1 - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(passed=1) diff --git a/tests/test_port_factories.py b/tests/test_port_factories.py index 713d747e..0acdea34 100644 --- a/tests/test_port_factories.py +++ b/tests/test_port_factories.py @@ -8,9 +8,7 @@ def test_unused_tcp_port_selects_unused_port(pytester: Pytester): - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -31,15 +29,11 @@ async def closer(_, writer): server1.close() await server1.wait_closed() - """ - ) - ) + """)) def test_unused_udp_port_selects_unused_port(pytester: Pytester): - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ @pytest.mark.asyncio async def test_unused_udp_port_fixture(unused_udp_port): class Closer: @@ -64,15 +58,11 @@ def connection_lost(self, *arg, **kwd): ) transport1.abort() - """ - ) - ) + """)) def test_unused_tcp_port_factory_selects_unused_port(pytester: Pytester): - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ @pytest.mark.asyncio async def test_unused_port_factory_fixture(unused_tcp_port_factory): async def closer(_, writer): @@ -104,15 +94,11 @@ async def closer(_, writer): await server2.wait_closed() server3.close() await server3.wait_closed() - """ - ) - ) + """)) def test_unused_udp_port_factory_selects_unused_port(pytester: Pytester): - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ @pytest.mark.asyncio async def test_unused_udp_port_factory_fixture(unused_udp_port_factory): class Closer: @@ -156,9 +142,7 @@ def connection_lost(self, *arg, **kwd): transport1.abort() transport2.abort() transport3.abort() - """ - ) - ) + """)) def test_unused_port_factory_duplicate(unused_tcp_port_factory, monkeypatch): diff --git a/tests/test_set_event_loop.py b/tests/test_set_event_loop.py index a2f5dfaa..7f0d5dea 100644 --- a/tests/test_set_event_loop.py +++ b/tests/test_set_event_loop.py @@ -30,18 +30,12 @@ def test_set_event_loop_none( test_loop_scope: str, loop_breaking_action: str, ): - pytester.makeini( - dedent( - f"""\ + pytester.makeini(dedent(f"""\ [pytest] asyncio_default_test_loop_scope = {test_loop_scope} asyncio_default_fixture_loop_scope = function - """ - ) - ) - pytester.makepyfile( - dedent( - f"""\ + """)) + pytester.makepyfile(dedent(f"""\ import asyncio import pytest @@ -57,9 +51,7 @@ def test_set_event_loop_none(): @pytest.mark.asyncio async def test_after(): pass - """ - ) - ) + """)) result = pytester.runpytest_subprocess() result.assert_outcomes(passed=3) @@ -79,18 +71,12 @@ async def test_after(): ], ) def test_set_event_loop_none_class(pytester: Pytester, loop_breaking_action: str): - pytester.makeini( - dedent( - """\ + pytester.makeini(dedent("""\ [pytest] asyncio_default_test_loop_scope = class asyncio_default_fixture_loop_scope = function - """ - ) - ) - pytester.makepyfile( - dedent( - f"""\ + """)) + pytester.makepyfile(dedent(f"""\ import asyncio import pytest @@ -108,9 +94,7 @@ def test_set_event_loop_none(self): @pytest.mark.asyncio async def test_after(self): pass - """ - ) - ) + """)) result = pytester.runpytest_subprocess() result.assert_outcomes(passed=3) @@ -135,18 +119,12 @@ def test_original_shared_loop_is_reinstated_not_fresh_loop( test_loop_scope: str, loop_breaking_action: str, ): - pytester.makeini( - dedent( - f"""\ + pytester.makeini(dedent(f"""\ [pytest] asyncio_default_test_loop_scope = {test_loop_scope} asyncio_default_fixture_loop_scope = function - """ - ) - ) - pytester.makepyfile( - dedent( - f"""\ + """)) + pytester.makepyfile(dedent(f"""\ import asyncio import pytest @@ -170,9 +148,7 @@ async def test_verify_original_loop_reinstated(): assert current_loop is original_shared_loop assert hasattr(current_loop, '_custom_marker') assert current_loop._custom_marker == "original_loop_marker" - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=3) @@ -197,18 +173,12 @@ def test_shared_loop_with_fixture_preservation( test_loop_scope: str, loop_breaking_action: str, ): - pytester.makeini( - dedent( - f"""\ + pytester.makeini(dedent(f"""\ [pytest] asyncio_default_test_loop_scope = {test_loop_scope} asyncio_default_fixture_loop_scope = {test_loop_scope} - """ - ) - ) - pytester.makepyfile( - dedent( - f"""\ + """)) + pytester.makepyfile(dedent(f"""\ import asyncio import pytest import pytest_asyncio @@ -249,9 +219,7 @@ async def test_after(webserver): current_loop = asyncio.get_running_loop() assert current_loop is fixture_loop assert not long_running_task.done() - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=3) @@ -287,18 +255,12 @@ def test_shared_loop_with_multiple_fixtures_preservation( second_scope: str, loop_breaking_action: str, ): - pytester.makeini( - dedent( - """\ + pytester.makeini(dedent("""\ [pytest] asyncio_default_test_loop_scope = session asyncio_default_fixture_loop_scope = session - """ - ) - ) - pytester.makepyfile( - dedent( - f"""\ + """)) + pytester.makepyfile(dedent(f"""\ import asyncio import pytest import pytest_asyncio @@ -364,8 +326,6 @@ async def test_after_second(second_webserver): current_loop = asyncio.get_running_loop() assert current_loop is second_fixture_loop assert not second_long_running_task.done() - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=5) diff --git a/tests/test_simple.py b/tests/test_simple.py index f92ef4e7..8ddeade3 100644 --- a/tests/test_simple.py +++ b/tests/test_simple.py @@ -22,9 +22,7 @@ async def test_asyncio_marker(): def test_asyncio_marker_compatibility_with_xfail(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest pytest_plugins = "pytest_asyncio" @@ -33,18 +31,14 @@ def test_asyncio_marker_compatibility_with_xfail(pytester: Pytester): @pytest.mark.asyncio async def test_asyncio_marker_fail(): raise AssertionError - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(xfailed=1) def test_asyncio_auto_mode_compatibility_with_xfail(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest pytest_plugins = "pytest_asyncio" @@ -52,9 +46,7 @@ def test_asyncio_auto_mode_compatibility_with_xfail(pytester: Pytester): @pytest.mark.xfail(reason="need a failure", strict=True) async def test_asyncio_marker_fail(): raise AssertionError - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(xfailed=1) diff --git a/tests/test_skips.py b/tests/test_skips.py index d32273cd..8671b79a 100644 --- a/tests/test_skips.py +++ b/tests/test_skips.py @@ -7,9 +7,7 @@ def test_asyncio_strict_mode_skip(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest pytest_plugins = "pytest_asyncio" @@ -17,36 +15,28 @@ def test_asyncio_strict_mode_skip(pytester: Pytester): @pytest.mark.asyncio async def test_no_warning_on_skip(): pytest.skip("Test a skip error inside asyncio") - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(skipped=1) def test_asyncio_auto_mode_skip(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest pytest_plugins = "pytest_asyncio" async def test_no_warning_on_skip(): pytest.skip("Test a skip error inside asyncio") - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(skipped=1) def test_asyncio_strict_mode_module_level_skip(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest pytest.skip("Skip all tests", allow_module_level=True) @@ -54,63 +44,49 @@ def test_asyncio_strict_mode_module_level_skip(pytester: Pytester): @pytest.mark.asyncio async def test_is_skipped(): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(skipped=1) def test_asyncio_auto_mode_module_level_skip(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest pytest.skip("Skip all tests", allow_module_level=True) async def test_is_skipped(): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(skipped=1) def test_asyncio_auto_mode_wrong_skip_usage(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import pytest pytest.skip("Skip all tests") async def test_is_skipped(): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(errors=1) def test_unittest_skiptest_compatibility(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ from unittest import SkipTest raise SkipTest("Skip all tests") async def test_is_skipped(): pass - """ - ) - ) + """)) result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(skipped=1) @@ -119,8 +95,7 @@ def test_skip_in_module_does_not_skip_package(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( __init__="", - test_skip=dedent( - """\ + test_skip=dedent("""\ import pytest pytest.skip("Skip all tests", allow_module_level=True) @@ -130,17 +105,14 @@ def test_a(): def test_b(): pass - """ - ), - test_something=dedent( - """\ + """), + test_something=dedent("""\ import pytest @pytest.mark.asyncio async def test_something(): pass - """ - ), + """), ) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=1, skipped=1) diff --git a/tests/test_task_cleanup.py b/tests/test_task_cleanup.py index eb1f7d3c..fda60b81 100644 --- a/tests/test_task_cleanup.py +++ b/tests/test_task_cleanup.py @@ -7,9 +7,7 @@ def test_task_is_cancelled_when_abandoned_by_test(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ + pytester.makepyfile(dedent("""\ import asyncio import pytest @@ -23,8 +21,6 @@ async def coroutine(): 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 aa24c935d97e2371cc3a5e5572cd2ae6e8ad89e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 15:53:33 +0000 Subject: [PATCH 051/168] Build(deps): Bump keyring from 25.6.0 to 25.7.0 Bumps [keyring](https://github.com/jaraco/keyring) from 25.6.0 to 25.7.0. - [Release notes](https://github.com/jaraco/keyring/releases) - [Changelog](https://github.com/jaraco/keyring/blob/main/NEWS.rst) - [Commits](https://github.com/jaraco/keyring/compare/v25.6.0...v25.7.0) --- updated-dependencies: - dependency-name: keyring dependency-version: 25.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 2e5c6505..cd9ddad4 100644 --- a/constraints.txt +++ b/constraints.txt @@ -25,7 +25,7 @@ jaraco.context==6.0.2 jaraco.functools==4.4.0 jeepney==0.9.0 Jinja2==3.1.6 -keyring==25.6.0 +keyring==25.7.0 markdown-it-py==4.0.0 MarkupSafe==3.0.3 mdurl==0.1.2 From 6682fe175b971e04577bde2c2b602d71dfbb95ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 16:28:15 +0000 Subject: [PATCH 052/168] Build(deps): Bump urllib3 from 2.6.2 to 2.6.3 Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.2 to 2.6.3. - [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.6.2...2.6.3) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.6.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index cd9ddad4..cc758de4 100644 --- a/constraints.txt +++ b/constraints.txt @@ -61,6 +61,6 @@ tomli==2.3.0 twine==6.2.0 typing_extensions==4.15.0 typing-inspection==0.4.2 -urllib3==2.6.2 +urllib3==2.6.3 wheel-filename==1.4.2 zipp==3.23.0 From 3f6beb17c8eb10cd71baedd0edc4c85ac7fe5427 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 16:28:31 +0000 Subject: [PATCH 053/168] Build(deps): Bump sphinx-rtd-theme from 3.0.2 to 3.1.0 Bumps [sphinx-rtd-theme](https://github.com/readthedocs/sphinx_rtd_theme) from 3.0.2 to 3.1.0. - [Changelog](https://github.com/readthedocs/sphinx_rtd_theme/blob/master/docs/changelog.rst) - [Commits](https://github.com/readthedocs/sphinx_rtd_theme/compare/3.0.2...3.1.0) --- updated-dependencies: - dependency-name: sphinx-rtd-theme dependency-version: 3.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index cc758de4..1f66faf2 100644 --- a/constraints.txt +++ b/constraints.txt @@ -49,7 +49,7 @@ setuptools-scm==9.2.2 snowballstemmer==3.0.1 sortedcontainers==2.4.0 Sphinx==8.1.3 -sphinx-rtd-theme==3.0.2 +sphinx-rtd-theme==3.1.0 sphinxcontrib-applehelp==2.0.0 sphinxcontrib-devhelp==2.0.0 sphinxcontrib-htmlhelp==2.1.0 From b8a95058fe5a3182c822c06beb58843a176be3a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 16:28:27 +0000 Subject: [PATCH 054/168] Build(deps): Bump tomli from 2.3.0 to 2.4.0 Bumps [tomli](https://github.com/hukkin/tomli) from 2.3.0 to 2.4.0. - [Changelog](https://github.com/hukkin/tomli/blob/master/CHANGELOG.md) - [Commits](https://github.com/hukkin/tomli/compare/2.3.0...2.4.0) --- updated-dependencies: - dependency-name: tomli dependency-version: 2.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 1f66faf2..f74e4939 100644 --- a/constraints.txt +++ b/constraints.txt @@ -57,7 +57,7 @@ sphinxcontrib-jquery==4.1 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==2.0.0 sphinxcontrib-serializinghtml==2.0.0 -tomli==2.3.0 +tomli==2.4.0 twine==6.2.0 typing_extensions==4.15.0 typing-inspection==0.4.2 From 47f34cc389d68cf64395a4a3f4d59976ee1608ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 16:28:42 +0000 Subject: [PATCH 055/168] Build(deps): Bump hypothesis from 6.149.1 to 6.150.1 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.149.1 to 6.150.1. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.149.1...hypothesis-python-6.150.1) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.150.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index f74e4939..40ec9317 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.13.1 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.1 -hypothesis==6.149.1 +hypothesis==6.150.2 iniconfig==2.3.0 id==1.5.0 idna==3.11 From 2466be74096915b77e8a2ff0a8fafe4951f5506d Mon Sep 17 00:00:00 2001 From: Jay Bazuzi Date: Sun, 23 Nov 2025 11:07:18 -0800 Subject: [PATCH 056/168] trivial: Formatting in default fixture loop scope message style: reformatting docs: Add news fragment. --- changelog.d/1298.changed.rst | 1 + pytest_asyncio/plugin.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 changelog.d/1298.changed.rst diff --git a/changelog.d/1298.changed.rst b/changelog.d/1298.changed.rst new file mode 100644 index 00000000..49f83c83 --- /dev/null +++ b/changelog.d/1298.changed.rst @@ -0,0 +1 @@ +Improved the readability of the warning message that is displayed when ``asyncio_default_fixture_loop_scope`` is unset diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 00c3c208..28c97cc9 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -221,10 +221,10 @@ def _get_asyncio_debug(config: Config) -> bool: _DEFAULT_FIXTURE_LOOP_SCOPE_UNSET = """\ The configuration option "asyncio_default_fixture_loop_scope" is unset. -The event loop scope for asynchronous fixtures will default to the fixture caching \ +The event loop scope for asynchronous fixtures will default to the "fixture" caching \ scope. Future versions of pytest-asyncio will default the loop scope for asynchronous \ -fixtures to function scope. Set the default fixture loop scope explicitly in order to \ -avoid unexpected behavior in the future. Valid fixture loop scopes are: \ +fixtures to "function" scope. Set the default fixture loop scope explicitly in order \ +to avoid unexpected behavior in the future. Valid fixture loop scopes are: \ "function", "class", "module", "package", "session" """ From b817cf476a6dda2fb4a847cfd1b3285209598779 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 Jan 2026 21:28:12 +0000 Subject: [PATCH 057/168] [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.14.11 → v0.14.13](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.11...v0.14.13) - [github.com/zizmorcore/zizmor-pre-commit: v1.20.0 → v1.22.0](https://github.com/zizmorcore/zizmor-pre-commit/compare/v1.20.0...v1.22.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 246c6e6b..7b1b01ae 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: - id: check-yaml - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.11 + rev: v0.14.13 hooks: - id: ruff args: [--fix] @@ -74,7 +74,7 @@ repos: # https://pyproject-fmt.readthedocs.io/en/latest/#calculating-max-supported-python-version additional_dependencies: [tox>=4.28] - repo: https://github.com/zizmorcore/zizmor-pre-commit - rev: v1.20.0 + rev: v1.22.0 hooks: - id: zizmor ci: From 234b90dde6af7d2054913e28fc4a6a172a2b2803 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 20:39:06 +0000 Subject: [PATCH 058/168] Build(deps): Bump jaraco-context from 6.0.2 to 6.1.0 Bumps [jaraco-context](https://github.com/jaraco/jaraco.context) from 6.0.2 to 6.1.0. - [Release notes](https://github.com/jaraco/jaraco.context/releases) - [Changelog](https://github.com/jaraco/jaraco.context/blob/main/NEWS.rst) - [Commits](https://github.com/jaraco/jaraco.context/compare/v6.0.2...v6.1.0) --- updated-dependencies: - dependency-name: jaraco-context dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 40ec9317..35be10f8 100644 --- a/constraints.txt +++ b/constraints.txt @@ -21,7 +21,7 @@ imagesize==1.4.1 importlib_metadata==8.7.1 iniconfig==2.3.0 jaraco.classes==3.4.0 -jaraco.context==6.0.2 +jaraco.context==6.1.0 jaraco.functools==4.4.0 jeepney==0.9.0 Jinja2==3.1.6 From 0aaebe26d777833e4c2099b55d995b89ccaae0c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 01:06:47 +0000 Subject: [PATCH 059/168] Build(deps): Bump rich from 14.2.0 to 14.3.1 Bumps [rich](https://github.com/Textualize/rich) from 14.2.0 to 14.3.1. - [Release notes](https://github.com/Textualize/rich/releases) - [Changelog](https://github.com/Textualize/rich/blob/master/CHANGELOG.md) - [Commits](https://github.com/Textualize/rich/compare/v14.2.0...v14.3.1) --- updated-dependencies: - dependency-name: rich dependency-version: 14.3.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 35be10f8..951d0dd2 100644 --- a/constraints.txt +++ b/constraints.txt @@ -42,7 +42,7 @@ readme-renderer==44.0 requests==2.32.5 requests-toolbelt==1.0.0 rfc3986==2.0.0 -rich==14.2.0 +rich==14.3.1 SecretStorage==3.5.0 setuptools==80.9.0 setuptools-scm==9.2.2 From d1a32cd6d0ca206c63b0020354cbe6033415c12e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 01:06:35 +0000 Subject: [PATCH 060/168] Build(deps): Bump packaging from 25.0 to 26.0 Bumps [packaging](https://github.com/pypa/packaging) from 25.0 to 26.0. - [Release notes](https://github.com/pypa/packaging/releases) - [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pypa/packaging/compare/25.0...26.0) --- updated-dependencies: - dependency-name: packaging dependency-version: '26.0' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 951d0dd2..301e96c2 100644 --- a/constraints.txt +++ b/constraints.txt @@ -31,7 +31,7 @@ MarkupSafe==3.0.3 mdurl==0.1.2 more-itertools==10.8.0 nh3==0.3.2 -packaging==25.0 +packaging==26.0 pluggy==1.6.0 Pygments==2.19.2 pycparser==2.23 From cb129a6df0043f8a31658c1a2791f9ca2541181f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 03:05:29 +0000 Subject: [PATCH 061/168] Build(deps): Bump setuptools from 80.9.0 to 80.10.2 Bumps [setuptools](https://github.com/pypa/setuptools) from 80.9.0 to 80.10.2. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/setuptools/compare/v80.9.0...v80.10.2) --- updated-dependencies: - dependency-name: setuptools dependency-version: 80.10.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 301e96c2..00264fc0 100644 --- a/constraints.txt +++ b/constraints.txt @@ -44,7 +44,7 @@ requests-toolbelt==1.0.0 rfc3986==2.0.0 rich==14.3.1 SecretStorage==3.5.0 -setuptools==80.9.0 +setuptools==80.10.2 setuptools-scm==9.2.2 snowballstemmer==3.0.1 sortedcontainers==2.4.0 From cb516ad2124d4006b6c43dc2d56682a1088c795b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 22:11:16 +0000 Subject: [PATCH 062/168] Build(deps): Bump hypothesis from 6.150.2 to 6.151.4 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.150.2 to 6.151.4. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.150.2...hypothesis-python-6.151.4) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.151.4 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 00264fc0..eb67254d 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.13.1 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.1 -hypothesis==6.150.2 +hypothesis==6.151.4 iniconfig==2.3.0 id==1.5.0 idna==3.11 From 56278826402ec1415ba88d697c06bf45f63001d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 05:56:18 +0000 Subject: [PATCH 063/168] Build(deps): Bump coverage from 7.13.1 to 7.13.2 Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.13.1 to 7.13.2. - [Release notes](https://github.com/coveragepy/coveragepy/releases) - [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst) - [Commits](https://github.com/coveragepy/coveragepy/compare/7.13.1...7.13.2) --- updated-dependencies: - dependency-name: coverage dependency-version: 7.13.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index eb67254d..b6afdd6a 100644 --- a/constraints.txt +++ b/constraints.txt @@ -9,7 +9,7 @@ charset-normalizer==3.4.4 check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.1 -coverage==7.13.1 +coverage==7.13.2 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.1 From 6432c19af286bf4aeb9293b9e9d56c69c1754b0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 22:11:42 +0000 Subject: [PATCH 064/168] Build(deps): Bump babel from 2.17.0 to 2.18.0 Bumps [babel](https://github.com/python-babel/babel) from 2.17.0 to 2.18.0. - [Release notes](https://github.com/python-babel/babel/releases) - [Changelog](https://github.com/python-babel/babel/blob/master/CHANGES.rst) - [Commits](https://github.com/python-babel/babel/compare/v2.17.0...v2.18.0) --- updated-dependencies: - dependency-name: babel dependency-version: 2.18.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index b6afdd6a..1818a4cb 100644 --- a/constraints.txt +++ b/constraints.txt @@ -1,7 +1,7 @@ alabaster==1.0.0 annotated-types-0.7.0 attrs==25.4.0 -babel==2.17.0 +babel==2.18.0 backports.asyncio.runner==1.2.0 backports.tarfile==1.2.0 certifi==2026.1.4 From a43052c847890654c632cda814e86ae37d05ae1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 19:29:02 +0000 Subject: [PATCH 065/168] Build(deps): Bump setuptools from 80.10.2 to 82.0.0 Bumps [setuptools](https://github.com/pypa/setuptools) from 80.10.2 to 82.0.0. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/setuptools/compare/v80.10.2...v82.0.0) --- updated-dependencies: - dependency-name: setuptools dependency-version: 82.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 1818a4cb..6a6aa9a5 100644 --- a/constraints.txt +++ b/constraints.txt @@ -44,7 +44,7 @@ requests-toolbelt==1.0.0 rfc3986==2.0.0 rich==14.3.1 SecretStorage==3.5.0 -setuptools==80.10.2 +setuptools==82.0.0 setuptools-scm==9.2.2 snowballstemmer==3.0.1 sortedcontainers==2.4.0 From f846ae2411ce1651c7f61b4e518374eef1bc63d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 19:28:55 +0000 Subject: [PATCH 066/168] Build(deps): Bump coverage from 7.13.2 to 7.13.4 Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.13.2 to 7.13.4. - [Release notes](https://github.com/coveragepy/coveragepy/releases) - [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst) - [Commits](https://github.com/coveragepy/coveragepy/compare/7.13.2...7.13.4) --- updated-dependencies: - dependency-name: coverage dependency-version: 7.13.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 6a6aa9a5..9f81563f 100644 --- a/constraints.txt +++ b/constraints.txt @@ -9,7 +9,7 @@ charset-normalizer==3.4.4 check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.1 -coverage==7.13.2 +coverage==7.13.4 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.1 From 79b4aec5c8055b1d567df6002e0d900f06f72ac8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 19:28:44 +0000 Subject: [PATCH 067/168] Build(deps): Bump id from 1.5.0 to 1.6.1 Bumps [id](https://github.com/di/id) from 1.5.0 to 1.6.1. - [Release notes](https://github.com/di/id/releases) - [Changelog](https://github.com/di/id/blob/main/CHANGELOG.md) - [Commits](https://github.com/di/id/compare/v1.5.0...v1.6.1) --- updated-dependencies: - dependency-name: id dependency-version: 1.6.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 9f81563f..68520fab 100644 --- a/constraints.txt +++ b/constraints.txt @@ -15,7 +15,7 @@ docutils==0.21.2 exceptiongroup==1.3.1 hypothesis==6.151.4 iniconfig==2.3.0 -id==1.5.0 +id==1.6.1 idna==3.11 imagesize==1.4.1 importlib_metadata==8.7.1 From a039f68243ccce2be6a666c9c4da7772e9ac7620 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 19:28:24 +0000 Subject: [PATCH 068/168] Build(deps): Bump hypothesis from 6.151.4 to 6.151.5 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.151.4 to 6.151.5. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.151.4...hypothesis-python-6.151.5) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.151.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 68520fab..aabebab7 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.13.4 cryptography==46.0.3 docutils==0.21.2 exceptiongroup==1.3.1 -hypothesis==6.151.4 +hypothesis==6.151.5 iniconfig==2.3.0 id==1.6.1 idna==3.11 From f42c59ff0545e69e3a55829b5e490b6d9420d411 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 20:16:34 +0000 Subject: [PATCH 069/168] Build(deps): Bump rich from 14.3.1 to 14.3.2 Bumps [rich](https://github.com/Textualize/rich) from 14.3.1 to 14.3.2. - [Release notes](https://github.com/Textualize/rich/releases) - [Changelog](https://github.com/Textualize/rich/blob/master/CHANGELOG.md) - [Commits](https://github.com/Textualize/rich/compare/v14.3.1...v14.3.2) --- updated-dependencies: - dependency-name: rich dependency-version: 14.3.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index aabebab7..b66f2d79 100644 --- a/constraints.txt +++ b/constraints.txt @@ -42,7 +42,7 @@ readme-renderer==44.0 requests==2.32.5 requests-toolbelt==1.0.0 rfc3986==2.0.0 -rich==14.3.1 +rich==14.3.2 SecretStorage==3.5.0 setuptools==82.0.0 setuptools-scm==9.2.2 From 8fe52b47737896cf24d6e181e7d9300b7305b65b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 20:21:57 +0000 Subject: [PATCH 070/168] Build(deps): Bump cryptography from 46.0.3 to 46.0.4 Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.3 to 46.0.4. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/46.0.3...46.0.4) --- updated-dependencies: - dependency-name: cryptography dependency-version: 46.0.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index b66f2d79..a33d1685 100644 --- a/constraints.txt +++ b/constraints.txt @@ -10,7 +10,7 @@ check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.1 coverage==7.13.4 -cryptography==46.0.3 +cryptography==46.0.4 docutils==0.21.2 exceptiongroup==1.3.1 hypothesis==6.151.5 From 403d0145066a12eb10466bb237ca78cf2319ea2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 20:41:30 +0000 Subject: [PATCH 071/168] Build(deps): Bump cryptography from 46.0.4 to 46.0.5 Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.4 to 46.0.5. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/46.0.4...46.0.5) --- updated-dependencies: - dependency-name: cryptography dependency-version: 46.0.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index a33d1685..f11d42a4 100644 --- a/constraints.txt +++ b/constraints.txt @@ -10,7 +10,7 @@ check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.1 coverage==7.13.4 -cryptography==46.0.4 +cryptography==46.0.5 docutils==0.21.2 exceptiongroup==1.3.1 hypothesis==6.151.5 From 01e7a859ad2407ea68762053d430f402bb659660 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 05:57:59 +0000 Subject: [PATCH 072/168] Build(deps): Bump hypothesis from 6.151.5 to 6.151.8 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.151.5 to 6.151.8. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.151.5...hypothesis-python-6.151.8) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.151.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index f11d42a4..fc734503 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.13.4 cryptography==46.0.5 docutils==0.21.2 exceptiongroup==1.3.1 -hypothesis==6.151.5 +hypothesis==6.151.9 iniconfig==2.3.0 id==1.6.1 idna==3.11 From f90d43990e2761adcbe0d65f434d6dc6255af6dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 20:42:01 +0000 Subject: [PATCH 073/168] Build(deps): Bump nh3 from 0.3.2 to 0.3.3 Bumps [nh3](https://github.com/messense/nh3) from 0.3.2 to 0.3.3. - [Release notes](https://github.com/messense/nh3/releases) - [Commits](https://github.com/messense/nh3/compare/v0.3.2...v0.3.3) --- updated-dependencies: - dependency-name: nh3 dependency-version: 0.3.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index fc734503..29830890 100644 --- a/constraints.txt +++ b/constraints.txt @@ -30,7 +30,7 @@ markdown-it-py==4.0.0 MarkupSafe==3.0.3 mdurl==0.1.2 more-itertools==10.8.0 -nh3==0.3.2 +nh3==0.3.3 packaging==26.0 pluggy==1.6.0 Pygments==2.19.2 From d8b32b583e16ce0a3d8618abb9a5bcbce7eb61fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 03:06:49 +0000 Subject: [PATCH 074/168] Build(deps): Bump pycparser from 2.23 to 3.0 Bumps [pycparser](https://github.com/eliben/pycparser) from 2.23 to 3.0. - [Release notes](https://github.com/eliben/pycparser/releases) - [Commits](https://github.com/eliben/pycparser/compare/release_v2.23...release_v3.00) --- updated-dependencies: - dependency-name: pycparser dependency-version: '3.0' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 29830890..de7123c6 100644 --- a/constraints.txt +++ b/constraints.txt @@ -34,7 +34,7 @@ nh3==0.3.3 packaging==26.0 pluggy==1.6.0 Pygments==2.19.2 -pycparser==2.23 +pycparser==3.0 pydantic==2.12.5 pydantic-core==2.41.5 pytest==9.0.2 From 173cb3951f80751b8b217d86cc6921538e76c4a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 18:52:37 +0000 Subject: [PATCH 075/168] Build(deps): Bump actions/setup-python from 6.1.0 to 6.2.0 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6.1.0 to 6.2.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/83679a892e2d95755f2dac6acb0bfd1e9ac5d548...a309ff8b426b58ec0e2a45f0f869d46889d02405) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: 6.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index af6fa534..3dd7253f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,7 +27,7 @@ jobs: with: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 with: python-version: ${{ env.PYTHON_LATEST }} - name: Install tox @@ -55,7 +55,7 @@ jobs: with: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 with: python-version: ${{ env.PYTHON_LATEST }} - name: Install GitHub matcher for ActionLint checker @@ -80,7 +80,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: persist-credentials: false - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -119,7 +119,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: persist-credentials: false - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 with: python-version: ${{ env.PYTHON_LATEST }} - name: Install Coverage.py @@ -156,7 +156,7 @@ jobs: fetch-depth: 0 persist-credentials: false - name: Install Python - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 - name: Install towncrier run: pip install towncrier==24.8.0 - name: Install pandoc From 8fe2a03100747a9d6033ab302274854b8f012489 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 20:48:11 +0000 Subject: [PATCH 076/168] Build(deps): Bump rich from 14.3.2 to 14.3.3 Bumps [rich](https://github.com/Textualize/rich) from 14.3.2 to 14.3.3. - [Release notes](https://github.com/Textualize/rich/releases) - [Changelog](https://github.com/Textualize/rich/blob/master/CHANGELOG.md) - [Commits](https://github.com/Textualize/rich/compare/v14.3.2...v14.3.3) --- updated-dependencies: - dependency-name: rich dependency-version: 14.3.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index de7123c6..1d0f8cae 100644 --- a/constraints.txt +++ b/constraints.txt @@ -42,7 +42,7 @@ readme-renderer==44.0 requests==2.32.5 requests-toolbelt==1.0.0 rfc3986==2.0.0 -rich==14.3.2 +rich==14.3.3 SecretStorage==3.5.0 setuptools==82.0.0 setuptools-scm==9.2.2 From dbacf7b378efee687eee1912ef9c1603eed11368 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 20:42:27 +0000 Subject: [PATCH 077/168] Build(deps): Bump actions/download-artifact from 7.0.0 to 8.0.0 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7.0.0 to 8.0.0. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/37930b1c2abaa49bbe596cd826c3c89aef350131...70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .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 3dd7253f..8ac1eae0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -127,7 +127,7 @@ jobs: set -xe python -m pip install --upgrade coverage[toml] - name: Download coverage data for all test runs - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 with: pattern: coverage-* path: coverage @@ -187,7 +187,7 @@ jobs: name: release-notes.md path: release-notes.md - name: Download distributions - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 with: name: dist path: dist @@ -213,7 +213,7 @@ jobs: id-token: write steps: - name: Download distributions - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 with: name: dist path: dist @@ -232,7 +232,7 @@ jobs: id-token: write steps: - name: Download distributions - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 with: name: dist path: dist From eac2441cf097be134ad26602c03f6ed27d520840 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 20:22:34 +0000 Subject: [PATCH 078/168] Build(deps): Bump certifi from 2026.1.4 to 2026.2.25 Bumps [certifi](https://github.com/certifi/python-certifi) from 2026.1.4 to 2026.2.25. - [Commits](https://github.com/certifi/python-certifi/compare/2026.01.04...2026.02.25) --- updated-dependencies: - dependency-name: certifi dependency-version: 2026.2.25 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 1d0f8cae..527891da 100644 --- a/constraints.txt +++ b/constraints.txt @@ -4,7 +4,7 @@ attrs==25.4.0 babel==2.18.0 backports.asyncio.runner==1.2.0 backports.tarfile==1.2.0 -certifi==2026.1.4 +certifi==2026.2.25 charset-normalizer==3.4.4 check-wheel-contents==0.6.3 cffi==2.0.0 From 7a73f6d7e50550d593d0fa51be6c1f5770ec8ddf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Mar 2026 18:53:05 +0000 Subject: [PATCH 079/168] Build(deps): Bump actions/upload-artifact from 6.0.0 to 7.0.0 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6.0.0 to 7.0.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/b7c566a772e6b6bfb58ed0dc250532a479d7789f...bbbca2ddaa5d8feaa63e36b76fdaad77386f024f) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8ac1eae0..fff17837 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -42,7 +42,7 @@ jobs: id: version run: python ./tools/get-version.py >> $GITHUB_OUTPUT - name: Upload artifacts - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f with: name: dist path: dist @@ -93,7 +93,7 @@ jobs: run: python -m tox - name: Store coverage data - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f if: "!endsWith(matrix.os, 'windows')" with: name: coverage-python-${{ matrix.python-version }} @@ -182,7 +182,7 @@ jobs: run: | pandoc --wrap=preserve -o release-notes.md release-notes.rst - name: Upload artifacts - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f with: name: release-notes.md path: release-notes.md From 9e5d44187e70e14f2dbcdb33417cf0ea46c9b9fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:24:43 +0000 Subject: [PATCH 080/168] Build(deps): Bump jaraco-context from 6.1.0 to 6.1.1 Bumps [jaraco-context](https://github.com/jaraco/jaraco.context) from 6.1.0 to 6.1.1. - [Release notes](https://github.com/jaraco/jaraco.context/releases) - [Changelog](https://github.com/jaraco/jaraco.context/blob/main/NEWS.rst) - [Commits](https://github.com/jaraco/jaraco.context/compare/v6.1.0...v6.1.1) --- updated-dependencies: - dependency-name: jaraco-context dependency-version: 6.1.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 527891da..41a9cf62 100644 --- a/constraints.txt +++ b/constraints.txt @@ -21,7 +21,7 @@ imagesize==1.4.1 importlib_metadata==8.7.1 iniconfig==2.3.0 jaraco.classes==3.4.0 -jaraco.context==6.1.0 +jaraco.context==6.1.1 jaraco.functools==4.4.0 jeepney==0.9.0 Jinja2==3.1.6 From 314024c560e268f787d8214364193c4d29a36626 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:24:51 +0000 Subject: [PATCH 081/168] Build(deps): Bump charset-normalizer from 3.4.4 to 3.4.5 Bumps [charset-normalizer](https://github.com/jawah/charset_normalizer) from 3.4.4 to 3.4.5. - [Release notes](https://github.com/jawah/charset_normalizer/releases) - [Changelog](https://github.com/jawah/charset_normalizer/blob/master/CHANGELOG.md) - [Commits](https://github.com/jawah/charset_normalizer/compare/3.4.4...3.4.5) --- updated-dependencies: - dependency-name: charset-normalizer dependency-version: 3.4.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 41a9cf62..929f333c 100644 --- a/constraints.txt +++ b/constraints.txt @@ -5,7 +5,7 @@ babel==2.18.0 backports.asyncio.runner==1.2.0 backports.tarfile==1.2.0 certifi==2026.2.25 -charset-normalizer==3.4.4 +charset-normalizer==3.4.5 check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.1 From e3db174d233fce7a4673ad1ed14aaa1aca647057 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:25:13 +0000 Subject: [PATCH 082/168] Build(deps): Bump setuptools from 82.0.0 to 82.0.1 Bumps [setuptools](https://github.com/pypa/setuptools) from 82.0.0 to 82.0.1. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/setuptools/compare/v82.0.0...v82.0.1) --- updated-dependencies: - dependency-name: setuptools dependency-version: 82.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 929f333c..34a10280 100644 --- a/constraints.txt +++ b/constraints.txt @@ -44,7 +44,7 @@ requests-toolbelt==1.0.0 rfc3986==2.0.0 rich==14.3.3 SecretStorage==3.5.0 -setuptools==82.0.0 +setuptools==82.0.1 setuptools-scm==9.2.2 snowballstemmer==3.0.1 sortedcontainers==2.4.0 From 98c4f55f61fc0d9a82d618174301faa395e7f9d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:51:03 +0000 Subject: [PATCH 083/168] Build(deps): Bump imagesize from 1.4.1 to 2.0.0 Bumps [imagesize](https://github.com/shibukawa/imagesize_py) from 1.4.1 to 2.0.0. - [Commits](https://github.com/shibukawa/imagesize_py/compare/1.4.1...2.0.0) --- updated-dependencies: - dependency-name: imagesize dependency-version: 2.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 34a10280..1159c62e 100644 --- a/constraints.txt +++ b/constraints.txt @@ -17,7 +17,7 @@ hypothesis==6.151.9 iniconfig==2.3.0 id==1.6.1 idna==3.11 -imagesize==1.4.1 +imagesize==2.0.0 importlib_metadata==8.7.1 iniconfig==2.3.0 jaraco.classes==3.4.0 From ff72406b32adaa95002bd305ab7de6d3670b7de1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Mar 2026 18:52:50 +0000 Subject: [PATCH 084/168] Build(deps): Bump actions/download-artifact from 8.0.0 to 8.0.1 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 8.0.0 to 8.0.1. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3...3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: 8.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .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 fff17837..1a00c870 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -127,7 +127,7 @@ jobs: set -xe python -m pip install --upgrade coverage[toml] - name: Download coverage data for all test runs - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: pattern: coverage-* path: coverage @@ -187,7 +187,7 @@ jobs: name: release-notes.md path: release-notes.md - name: Download distributions - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: name: dist path: dist @@ -213,7 +213,7 @@ jobs: id-token: write steps: - name: Download distributions - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: name: dist path: dist @@ -232,7 +232,7 @@ jobs: id-token: write steps: - name: Download distributions - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: name: dist path: dist From 05c908d87b8c931161f316e4499b9e1a54612982 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 19:05:41 +0000 Subject: [PATCH 085/168] Build(deps): Bump charset-normalizer from 3.4.5 to 3.4.6 Bumps [charset-normalizer](https://github.com/jawah/charset_normalizer) from 3.4.5 to 3.4.6. - [Release notes](https://github.com/jawah/charset_normalizer/releases) - [Changelog](https://github.com/jawah/charset_normalizer/blob/master/CHANGELOG.md) - [Commits](https://github.com/jawah/charset_normalizer/compare/3.4.5...3.4.6) --- updated-dependencies: - dependency-name: charset-normalizer dependency-version: 3.4.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 1159c62e..82dc425f 100644 --- a/constraints.txt +++ b/constraints.txt @@ -5,7 +5,7 @@ babel==2.18.0 backports.asyncio.runner==1.2.0 backports.tarfile==1.2.0 certifi==2026.2.25 -charset-normalizer==3.4.5 +charset-normalizer==3.4.6 check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.1 From 38c82efda438000f2d34641370063e7c3ba64d76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 19:00:56 +0000 Subject: [PATCH 086/168] Build(deps): Bump ncipollo/release-action from 1.20.0 to 1.21.0 Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.20.0 to 1.21.0. - [Release notes](https://github.com/ncipollo/release-action/releases) - [Commits](https://github.com/ncipollo/release-action/compare/b7eabc95ff50cbeeedec83973935c8f306dfcd0b...339a81892b84b4eeb0f6e744e4574d79d0d9b8dd) --- updated-dependencies: - dependency-name: ncipollo/release-action dependency-version: 1.21.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 1a00c870..8c9517ce 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -193,7 +193,7 @@ jobs: path: dist - name: Create GitHub Release if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0 + uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0 with: name: pytest-asyncio ${{ needs.build.outputs.version }} artifacts: dist/* From d6e5a25d1d90218c481d0eb39966ac47f8fe0172 Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Sat, 7 Mar 2026 16:27:01 +0000 Subject: [PATCH 087/168] Implement event loop factory hook --- changelog.d/1164.added.rst | 3 + docs/how-to-guides/custom_loop_factory.rst | 26 + docs/how-to-guides/index.rst | 2 + .../run_test_with_specific_loop_factories.rst | 14 + docs/how-to-guides/uvloop.rst | 27 +- docs/reference/hooks.rst | 16 + docs/reference/index.rst | 1 + docs/reference/markers/index.rst | 2 + pytest_asyncio/plugin.py | 181 +++++- tests/markers/test_invalid_arguments.py | 39 +- tests/test_loop_factory_parametrization.py | 573 ++++++++++++++++++ 11 files changed, 859 insertions(+), 25 deletions(-) create mode 100644 changelog.d/1164.added.rst create mode 100644 docs/how-to-guides/custom_loop_factory.rst create mode 100644 docs/how-to-guides/run_test_with_specific_loop_factories.rst create mode 100644 docs/reference/hooks.rst create mode 100644 tests/test_loop_factory_parametrization.py diff --git a/changelog.d/1164.added.rst b/changelog.d/1164.added.rst new file mode 100644 index 00000000..ccf2988a --- /dev/null +++ b/changelog.d/1164.added.rst @@ -0,0 +1,3 @@ +Added the ``pytest_asyncio_loop_factories`` hook to parametrize asyncio tests with custom event loop factories. + +The hook now returns a mapping of factory names to loop factories, and ``pytest.mark.asyncio(loop_factories=[...])`` can be used to select a subset of configured factories per test. diff --git a/docs/how-to-guides/custom_loop_factory.rst b/docs/how-to-guides/custom_loop_factory.rst new file mode 100644 index 00000000..6e176d95 --- /dev/null +++ b/docs/how-to-guides/custom_loop_factory.rst @@ -0,0 +1,26 @@ +================================================ +How to use custom event loop factories for tests +================================================ + +pytest-asyncio can run asynchronous tests with custom event loop factories by implementing ``pytest_asyncio_loop_factories`` in ``conftest.py``. The hook returns a mapping from factory names to loop factory callables: + +.. code-block:: python + + import asyncio + + import pytest + + + class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + + def pytest_asyncio_loop_factories(config, item): + return { + "stdlib": asyncio.new_event_loop, + "custom": CustomEventLoop, + } + +See :doc:`run_test_with_specific_loop_factories` for running tests with only a subset of configured factories. + +See :doc:`../reference/hooks` and :doc:`../reference/markers/index` for the hook and marker reference. diff --git a/docs/how-to-guides/index.rst b/docs/how-to-guides/index.rst index 2dadc881..acf0d177 100644 --- a/docs/how-to-guides/index.rst +++ b/docs/how-to-guides/index.rst @@ -10,6 +10,8 @@ How-To Guides change_fixture_loop change_default_fixture_loop change_default_test_loop + custom_loop_factory + run_test_with_specific_loop_factories run_class_tests_in_same_loop run_module_tests_in_same_loop run_package_tests_in_same_loop diff --git a/docs/how-to-guides/run_test_with_specific_loop_factories.rst b/docs/how-to-guides/run_test_with_specific_loop_factories.rst new file mode 100644 index 00000000..338d28ab --- /dev/null +++ b/docs/how-to-guides/run_test_with_specific_loop_factories.rst @@ -0,0 +1,14 @@ +========================================================= +How to run a test with specific event loop factories only +========================================================= + +To run a test with only a subset of configured factories, use the ``loop_factories`` argument of ``pytest.mark.asyncio``: + +.. code-block:: python + + import pytest + + + @pytest.mark.asyncio(loop_factories=["custom"]) + async def test_only_with_custom_event_loop(): + pass diff --git a/docs/how-to-guides/uvloop.rst b/docs/how-to-guides/uvloop.rst index a796bea7..99afd238 100644 --- a/docs/how-to-guides/uvloop.rst +++ b/docs/how-to-guides/uvloop.rst @@ -2,8 +2,31 @@ How to test with uvloop ======================= -Redefining the *event_loop_policy* fixture will parametrize all async tests. The following example causes all async tests to run multiple times, once for each event loop in the fixture parameters: -Replace the default event loop policy in your *conftest.py:* +Define a ``pytest_asyncio_loop_factories`` hook in your *conftest.py* that maps factory names to loop factories: + +.. code-block:: python + + import uvloop + + + def pytest_asyncio_loop_factories(config, item): + return { + "uvloop": uvloop.new_event_loop, + } + +.. seealso:: + + :doc:`custom_loop_factory` + More details on the ``pytest_asyncio_loop_factories`` hook, including per-test factory selection and multiple factory parametrization. + +Using the event_loop_policy fixture +----------------------------------- + +.. note:: + + ``asyncio.AbstractEventLoopPolicy`` is deprecated as of Python 3.14 (removal planned for 3.16), and ``uvloop.EventLoopPolicy`` will be removed alongside it. Prefer the hook approach above. + +For older versions of Python and uvloop, you can override the *event_loop_policy* fixture in your *conftest.py:* .. code-block:: python diff --git a/docs/reference/hooks.rst b/docs/reference/hooks.rst new file mode 100644 index 00000000..ed025b6f --- /dev/null +++ b/docs/reference/hooks.rst @@ -0,0 +1,16 @@ +===== +Hooks +===== + +``pytest_asyncio_loop_factories`` +================================= + +This hook returns a mapping from factory name strings to event loop factory callables for the current test item. + +By default, each pytest-asyncio test is run once per configured factory. Tests managed by other async plugins are unaffected. Synchronous tests are not parametrized. The configured loop scope still determines how long each event loop instance is kept alive. + +Factories should be callables without required parameters and should return an ``asyncio.AbstractEventLoop`` instance. The effective hook result must be a non-empty mapping of non-empty string names to callables. + +When multiple ``pytest_asyncio_loop_factories`` implementations are present, pytest-asyncio uses the first non-``None`` result in pytest's hook dispatch order. + +When the hook is defined, async tests are parametrized via ``pytest.metafunc.parametrize``, and mapping keys are used as test IDs. For example, a test ``test_example`` with an event loop factory key ``foo`` will appear as ``test_example[foo]`` in test output. diff --git a/docs/reference/index.rst b/docs/reference/index.rst index b24c6e9c..5c3095f7 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -8,6 +8,7 @@ Reference configuration fixtures/index functions + hooks markers/index decorators/index changelog diff --git a/docs/reference/markers/index.rst b/docs/reference/markers/index.rst index 7715077b..761196a8 100644 --- a/docs/reference/markers/index.rst +++ b/docs/reference/markers/index.rst @@ -36,6 +36,8 @@ Subpackages do not share the loop with their parent package. Tests marked with *session* scope share the same event loop, even if the tests exist in different packages. +The ``pytest.mark.asyncio`` marker also accepts a ``loop_factories`` keyword argument to select a subset of configured event loop factories for a test. If ``loop_factories`` contains unknown names, pytest-asyncio raises a ``pytest.UsageError`` during collection. + .. |auto mode| replace:: *auto mode* .. _auto mode: ../../concepts.html#auto-mode .. |pytestmark| replace:: ``pytestmark`` diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 28c97cc9..a69350bd 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -20,6 +20,7 @@ Generator, Iterable, Iterator, + Mapping, Sequence, ) from types import AsyncGeneratorType, CoroutineType @@ -27,6 +28,7 @@ Any, Literal, ParamSpec, + TypeAlias, TypeVar, overload, ) @@ -63,6 +65,7 @@ _R = TypeVar("_R", bound=Awaitable[Any] | AsyncIterator[Any]) _P = ParamSpec("_P") FixtureFunction = Callable[_P, _R] +LoopFactory: TypeAlias = Callable[[], AbstractEventLoop] class PytestAsyncioError(Exception): @@ -74,6 +77,19 @@ class Mode(str, enum.Enum): STRICT = "strict" +hookspec = pluggy.HookspecMarker("pytest") + + +class PytestAsyncioSpecs: + @hookspec(firstresult=True) + def pytest_asyncio_loop_factories( + self, + config: Config, + item: Item, + ) -> Mapping[str, LoopFactory] | None: + raise NotImplementedError # pragma: no cover + + ASYNCIO_MODE_HELP = """\ 'auto' - for automatically handling all async functions by the plugin 'strict' - for autoprocessing disabling (useful if different async frameworks \ @@ -83,6 +99,7 @@ class Mode(str, enum.Enum): def pytest_addoption(parser: Parser, pluginmanager: PytestPluginManager) -> None: + pluginmanager.add_hookspecs(PytestAsyncioSpecs) group = parser.getgroup("asyncio") group.addoption( "--asyncio-mode", @@ -219,6 +236,34 @@ def _get_asyncio_debug(config: Config) -> bool: return val == "true" +_INVALID_LOOP_FACTORIES = """\ +pytest_asyncio_loop_factories must return a non-empty mapping of \ +factory names to callables. +""" + + +def _collect_hook_loop_factories( + config: Config, + item: Item, +) -> dict[str, LoopFactory] | None: + hook_caller = item.ihook.pytest_asyncio_loop_factories + if not hook_caller.get_hookimpls(): + return None + + result = hook_caller(config=config, item=item) + if result is None or not isinstance(result, Mapping): + raise pytest.UsageError(_INVALID_LOOP_FACTORIES) + # Copy into an isolated snapshot so later mutations of the hook's + # original container do not affect parametrization. + factories = dict(result) + if not factories or any( + not isinstance(name, str) or not name or not callable(factory) + for name, factory in factories.items() + ): + raise pytest.UsageError(_INVALID_LOOP_FACTORIES) + return factories + + _DEFAULT_FIXTURE_LOOP_SCOPE_UNSET = """\ The configuration option "asyncio_default_fixture_loop_scope" is unset. The event loop scope for asynchronous fixtures will default to the "fixture" caching \ @@ -481,7 +526,11 @@ def _loop_scope(self) -> _ScopeName: marker = self.get_closest_marker("asyncio") assert marker is not None default_loop_scope = _get_default_test_loop_scope(self.config) - return _get_marked_loop_scope(marker, default_loop_scope) + loop_scope = marker.kwargs.get("loop_scope") or marker.kwargs.get("scope") + if loop_scope is None: + return default_loop_scope + else: + return loop_scope @property def _synchronization_target_attr(self) -> tuple[object, str]: @@ -570,6 +619,16 @@ def _synchronization_target_attr(self) -> tuple[object, str]: return self.obj.hypothesis, "inner_test" +def _resolve_asyncio_marker(item: Function) -> Mark | None: + marker = item.get_closest_marker("asyncio") + if marker is not None: + return marker + if _get_asyncio_mode(item.config) == Mode.AUTO: + item.add_marker("asyncio") + return item.get_closest_marker("asyncio") + 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) @@ -600,17 +659,68 @@ def pytest_pycollect_makeitem_convert_async_functions_to_subclass( updated_item = node if isinstance(node, Function): specialized_item_class = PytestAsyncioFunction.item_subclass_for(node) - if specialized_item_class: - if _get_asyncio_mode( - node.config - ) == Mode.AUTO and not node.get_closest_marker("asyncio"): - node.add_marker("asyncio") - if node.get_closest_marker("asyncio"): - updated_item = specialized_item_class._from_function(node) + if ( + specialized_item_class is not None + and _resolve_asyncio_marker(node) is not None + ): + updated_item = specialized_item_class._from_function(node) updated_node_collection.append(updated_item) hook_result.force_result(updated_node_collection) +@pytest.hookimpl(tryfirst=True) +def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: + specialized_item_class = PytestAsyncioFunction.item_subclass_for( + metafunc.definition + ) + if specialized_item_class is None: + return + + asyncio_marker = _resolve_asyncio_marker(metafunc.definition) + if asyncio_marker is None: + return + marker_loop_scope, marker_selected_factory_names = _parse_asyncio_marker( + asyncio_marker + ) + + hook_factories = _collect_hook_loop_factories(metafunc.config, metafunc.definition) + if hook_factories is None: + if marker_selected_factory_names is not None: + raise pytest.UsageError( + "mark.asyncio 'loop_factories' requires at least one " + "pytest_asyncio_loop_factories hook implementation." + ) + return + + if marker_selected_factory_names is None: + effective_factories = hook_factories + else: + missing_factory_names = tuple( + name for name in marker_selected_factory_names if name not in hook_factories + ) + if missing_factory_names: + msg = ( + f"Unknown factory name(s) {missing_factory_names}." + f" Available names: {', '.join(hook_factories)}." + ) + raise pytest.UsageError(msg) + # Build the mapping in marker order to preserve explicit user + # selection order in parametrization. + effective_factories = { + name: hook_factories[name] for name in marker_selected_factory_names + } + metafunc.fixturenames.append(_asyncio_loop_factory.__name__) + default_loop_scope = _get_default_test_loop_scope(metafunc.config) + loop_scope = marker_loop_scope or default_loop_scope + metafunc.parametrize( + _asyncio_loop_factory.__name__, + effective_factories.values(), + ids=effective_factories.keys(), + indirect=True, + scope=loop_scope, + ) + + @contextlib.contextmanager def _temporary_event_loop_policy(policy: AbstractEventLoopPolicy) -> Iterator[None]: old_loop_policy = _get_event_loop_policy() @@ -754,15 +864,16 @@ def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: Please use the "loop_scope" argument instead. """ +_INVALID_LOOP_FACTORIES_KWARG = """\ +mark.asyncio 'loop_factories' must be a non-empty sequence of strings. +""" + -def _get_marked_loop_scope( - asyncio_marker: Mark, default_loop_scope: _ScopeName -) -> _ScopeName: +def _parse_asyncio_marker( + asyncio_marker: Mark, +) -> tuple[_ScopeName | None, Sequence[str] | None]: assert asyncio_marker.name == "asyncio" - if asyncio_marker.args or ( - asyncio_marker.kwargs and set(asyncio_marker.kwargs) - {"loop_scope", "scope"} - ): - raise ValueError("mark.asyncio accepts only a keyword argument 'loop_scope'.") + _validate_asyncio_marker(asyncio_marker) if "scope" in asyncio_marker.kwargs: if "loop_scope" in asyncio_marker.kwargs: raise pytest.UsageError(_DUPLICATE_LOOP_SCOPE_DEFINITION_ERROR) @@ -770,10 +881,31 @@ def _get_marked_loop_scope( scope = asyncio_marker.kwargs.get("loop_scope") or asyncio_marker.kwargs.get( "scope" ) - if scope is None: - scope = default_loop_scope - assert scope in {"function", "class", "module", "package", "session"} - return scope + if scope is not None: + assert scope in {"function", "class", "module", "package", "session"} + marker_value = asyncio_marker.kwargs.get("loop_factories") + if marker_value is None: + return scope, None + if isinstance(marker_value, str) or not isinstance(marker_value, Sequence): + raise ValueError(_INVALID_LOOP_FACTORIES_KWARG) + if not marker_value or any( + not isinstance(factory_name, str) or not factory_name + for factory_name in marker_value + ): + raise ValueError(_INVALID_LOOP_FACTORIES_KWARG) + return scope, marker_value + + +def _validate_asyncio_marker(asyncio_marker: Mark) -> None: + if asyncio_marker.args or ( + asyncio_marker.kwargs + and set(asyncio_marker.kwargs) - {"loop_scope", "scope", "loop_factories"} + ): + msg = ( + "mark.asyncio accepts only keyword arguments 'loop_scope' and" + " 'loop_factories'." + ) + raise ValueError(msg) def _get_default_test_loop_scope(config: Config) -> Any: @@ -798,12 +930,16 @@ def _create_scoped_runner_fixture(scope: _ScopeName) -> Callable: ) def _scoped_runner( event_loop_policy, + _asyncio_loop_factory, request: FixtureRequest, ) -> Iterator[Runner]: new_loop_policy = event_loop_policy debug_mode = _get_asyncio_debug(request.config) with _temporary_event_loop_policy(new_loop_policy): - runner = Runner(debug=debug_mode).__enter__() + runner = Runner( + debug=debug_mode, + loop_factory=_asyncio_loop_factory, + ).__enter__() try: yield runner except Exception as e: @@ -830,6 +966,11 @@ def _scoped_runner( ) +@pytest.fixture(scope="session") +def _asyncio_loop_factory(request: FixtureRequest) -> LoopFactory | None: + return getattr(request, "param", None) + + @pytest.fixture(scope="session", autouse=True) def event_loop_policy() -> AbstractEventLoopPolicy: """Return an instance of the policy used to create asyncio event loops.""" diff --git a/tests/markers/test_invalid_arguments.py b/tests/markers/test_invalid_arguments.py index 19aa3126..df19ecfb 100644 --- a/tests/markers/test_invalid_arguments.py +++ b/tests/markers/test_invalid_arguments.py @@ -35,7 +35,7 @@ async def test_anything(): result = pytester.runpytest("--assert=plain") result.assert_outcomes(errors=1) result.stdout.fnmatch_lines( - ["*ValueError: mark.asyncio accepts only a keyword argument*"] + ["*ValueError: mark.asyncio accepts only keyword arguments*"] ) @@ -53,7 +53,7 @@ async def test_anything(): result = pytester.runpytest("--assert=plain") result.assert_outcomes(errors=1) result.stdout.fnmatch_lines( - ["*ValueError: mark.asyncio accepts only a keyword argument 'loop_scope'*"] + ["*ValueError: mark.asyncio accepts only keyword arguments*"] ) @@ -71,5 +71,38 @@ async def test_anything(): result = pytester.runpytest("--assert=plain") result.assert_outcomes(errors=1) result.stdout.fnmatch_lines( - ["*ValueError: mark.asyncio accepts only a keyword argument*"] + ["*ValueError: mark.asyncio accepts only keyword arguments*"] + ) + + +@pytest.mark.parametrize( + "loop_factories_value", + ('"custom"', "[]", '[""]', "[1]"), +) +def test_error_when_loop_factories_marker_value_is_invalid( + pytester: pytest.Pytester, loop_factories_value: str +): + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + + class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return {"custom": CustomEventLoop} + """)) + pytester.makepyfile(dedent(f"""\ + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio(loop_factories={loop_factories_value}) + async def test_anything(): + pass + """)) + result = pytester.runpytest("--assert=plain") + result.assert_outcomes(errors=1) + result.stdout.fnmatch_lines( + ["*ValueError: mark.asyncio 'loop_factories' must be a non-empty sequence*"] ) diff --git a/tests/test_loop_factory_parametrization.py b/tests/test_loop_factory_parametrization.py new file mode 100644 index 00000000..f6bac235 --- /dev/null +++ b/tests/test_loop_factory_parametrization.py @@ -0,0 +1,573 @@ +from __future__ import annotations + +from textwrap import dedent + +import pytest +from pytest import Pytester + + +def test_named_hook_factories_apply_to_async_tests(pytester: Pytester) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + + class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return {"custom": CustomEventLoop} + """)) + pytester.makepyfile(dedent("""\ + import asyncio + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio + async def test_uses_custom_loop(): + assert type(asyncio.get_running_loop()).__name__ == "CustomEventLoop" + """)) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=1) + + +def test_named_hook_factories_parametrize_async_tests(pytester: Pytester) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + + class CustomEventLoopA(asyncio.SelectorEventLoop): + pass + + class CustomEventLoopB(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return { + "factory_a": CustomEventLoopA, + "factory_b": CustomEventLoopB, + } + """)) + pytester.makepyfile(dedent("""\ + import asyncio + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio + async def test_runs_once_per_factory(): + loop_name = type(asyncio.get_running_loop()).__name__ + assert loop_name in ("CustomEventLoopA", "CustomEventLoopB") + """)) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=2) + + +def test_named_hook_factories_use_mapping_keys_as_test_ids( + pytester: Pytester, +) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + + def pytest_asyncio_loop_factories(config, item): + return { + "factory_a": asyncio.new_event_loop, + "factory_b": asyncio.new_event_loop, + } + """)) + pytester.makepyfile(dedent("""\ + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio + async def test_runs_once_per_factory(): + assert True + """)) + result = pytester.runpytest("--asyncio-mode=strict", "--collect-only", "-q") + result.stdout.fnmatch_lines( + [ + "*test_runs_once_per_factory[[]factory_a[]]", + "*test_runs_once_per_factory[[]factory_b[]]", + ] + ) + + +def test_named_hook_factories_apply_to_async_fixtures(pytester: Pytester) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + + class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return {"custom": CustomEventLoop} + """)) + pytester.makepyfile(dedent("""\ + import asyncio + import pytest + import pytest_asyncio + + pytest_plugins = "pytest_asyncio" + + @pytest_asyncio.fixture + async def loop_fixture(): + return asyncio.get_running_loop() + + @pytest.mark.asyncio + async def test_fixture_uses_custom_loop(loop_fixture): + assert type(loop_fixture).__name__ == "CustomEventLoop" + assert type(asyncio.get_running_loop()).__name__ == "CustomEventLoop" + """)) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=1) + + +def test_sync_tests_are_not_parametrized_by_hook_factories(pytester: Pytester) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + + class CustomEventLoopA(asyncio.SelectorEventLoop): + pass + + class CustomEventLoopB(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return { + "factory_a": CustomEventLoopA, + "factory_b": CustomEventLoopB, + } + """)) + pytester.makepyfile(dedent("""\ + import asyncio + import pytest + + pytest_plugins = "pytest_asyncio" + + def test_sync(request): + assert True + + @pytest.mark.asyncio + async def test_async(request): + assert "_asyncio_loop_factory" in request.fixturenames + loop_name = type(asyncio.get_running_loop()).__name__ + assert loop_name in ("CustomEventLoopA", "CustomEventLoopB") + """)) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=3) + + +@pytest.mark.parametrize( + "hook_body", + ( + "return None", + "return {}", + "return [CustomEventLoop]", + "return {'': CustomEventLoop}", + "return {'default': 1}", + ), +) +def test_hook_requires_non_empty_mapping_of_named_callables( + pytester: Pytester, + hook_body: str, +) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent(f"""\ + import asyncio + + class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + {hook_body} + """)) + pytester.makepyfile(dedent("""\ + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio + async def test_async(): + assert True + """)) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(errors=1) + result.stdout.fnmatch_lines( + [ + "*pytest_asyncio_loop_factories must return a non-empty mapping of " + "factory*" + ] + ) + + +def test_hook_factories_use_first_non_none_result(pytester: Pytester) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makepyfile( + plugin_none=dedent("""\ + import pytest + + @pytest.hookimpl(tryfirst=True) + def pytest_asyncio_loop_factories(config, item): + return None + """), + plugin_loop=dedent("""\ + import asyncio + import pytest + + class SecondaryCustomEventLoop(asyncio.SelectorEventLoop): + pass + + @pytest.hookimpl(trylast=True) + def pytest_asyncio_loop_factories(config, item): + return {"secondary": SecondaryCustomEventLoop} + """), + test_sample=dedent("""\ + import asyncio + import pytest + + pytest_plugins = ("pytest_asyncio", "plugin_none", "plugin_loop") + + @pytest.mark.asyncio + async def test_uses_secondary_loop(): + assert ( + type(asyncio.get_running_loop()).__name__ + == "SecondaryCustomEventLoop" + ) + """), + ) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=1) + + +def test_hook_factories_error_when_all_implementations_return_none( + pytester: Pytester, +) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makepyfile( + plugin_none_a=dedent("""\ + import pytest + + @pytest.hookimpl(tryfirst=True) + def pytest_asyncio_loop_factories(config, item): + return None + """), + plugin_none_b=dedent("""\ + import pytest + + @pytest.hookimpl(trylast=True) + def pytest_asyncio_loop_factories(config, item): + return None + """), + test_sample=dedent("""\ + import pytest + + pytest_plugins = ("pytest_asyncio", "plugin_none_a", "plugin_none_b") + + @pytest.mark.asyncio + async def test_anything(): + assert True + """), + ) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(errors=1) + result.stdout.fnmatch_lines( + [ + "*pytest_asyncio_loop_factories must return a non-empty mapping of " + "factory*" + ] + ) + + +def test_nested_conftest_hook_respects_conftest_locality( + pytester: Pytester, +) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + + class RootCustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return {"root": RootCustomEventLoop} + """)) + subdir = pytester.mkdir("subdir") + subdir.joinpath("conftest.py").write_text( + dedent("""\ + import asyncio + + class SubCustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return {"sub": SubCustomEventLoop} + """), + ) + pytester.makepyfile( + test_root=dedent("""\ + import asyncio + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio + async def test_root_uses_root_loop(): + assert ( + type(asyncio.get_running_loop()).__name__ == "RootCustomEventLoop" + ) + """), + ) + subdir.joinpath("test_sub.py").write_text( + dedent("""\ + import asyncio + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio + async def test_sub_uses_sub_loop(): + assert type(asyncio.get_running_loop()).__name__ == "SubCustomEventLoop" + """), + ) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=2) + + +def test_asyncio_marker_loop_factories_select_subset(pytester: Pytester) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + + class MainCustomEventLoop(asyncio.SelectorEventLoop): + pass + + class AlternativeCustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return { + "main": MainCustomEventLoop, + "alternative": AlternativeCustomEventLoop, + } + """)) + pytester.makepyfile(dedent("""\ + import asyncio + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio(loop_factories=["alternative"]) + async def test_runs_only_with_uvloop(): + assert ( + type(asyncio.get_running_loop()).__name__ + == "AlternativeCustomEventLoop" + ) + """)) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=1) + + +def test_asyncio_marker_loop_factories_unknown_name_errors(pytester: Pytester) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + + def pytest_asyncio_loop_factories(config, item): + return {"root": asyncio.new_event_loop} + """)) + pytester.makepyfile(dedent("""\ + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio(loop_factories=["missing"]) + async def test_errors(): + assert True + """)) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(errors=1) + result.stdout.fnmatch_lines( + [ + "*Unknown factory name(s)*Available names:*", + ] + ) + + +def test_asyncio_marker_loop_factories_without_hook_errors( + pytester: Pytester, +) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makepyfile(dedent("""\ + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio(loop_factories=["missing"]) + async def test_errors(): + assert True + """)) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(errors=1) + result.stdout.fnmatch_lines( + [ + "*mark.asyncio 'loop_factories' requires at least one " + "pytest_asyncio_loop_factories hook implementation.*", + ] + ) + + +@pytest.mark.parametrize("default_test_loop_scope", ("function", "module")) +def test_hook_factories_can_vary_per_test_with_default_loop_scope( + pytester: Pytester, + default_test_loop_scope: str, +) -> None: + pytester.makeini( + "[pytest]\nasyncio_default_fixture_loop_scope = function\n" + f"asyncio_default_test_loop_scope = {default_test_loop_scope}" + ) + pytester.makeconftest(dedent("""\ + import asyncio + + class CustomEventLoopA(asyncio.SelectorEventLoop): + pass + + class CustomEventLoopB(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + if item.name.endswith("a"): + return {"factory_a": CustomEventLoopA} + else: + return {"factory_b": CustomEventLoopB} + """)) + pytester.makepyfile(dedent("""\ + import asyncio + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio + async def test_a(): + assert type(asyncio.get_running_loop()).__name__ == "CustomEventLoopA" + + @pytest.mark.asyncio + async def test_b(): + assert type(asyncio.get_running_loop()).__name__ == "CustomEventLoopB" + """)) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=2) + + +def test_hook_factories_can_vary_per_test_with_session_scope_across_modules( + pytester: Pytester, +) -> None: + pytester.makeini( + "[pytest]\nasyncio_default_fixture_loop_scope = function\n" + "asyncio_default_test_loop_scope = session" + ) + pytester.makeconftest(dedent("""\ + import asyncio + + class CustomEventLoopA(asyncio.SelectorEventLoop): + pass + + class CustomEventLoopB(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + if "test_a.py::" in item.nodeid: + return {"factory_a": CustomEventLoopA} + else: + return {"factory_b": CustomEventLoopB} + """)) + pytester.makepyfile( + test_a=dedent("""\ + import asyncio + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio + async def test_a(): + assert type(asyncio.get_running_loop()).__name__ == "CustomEventLoopA" + """), + test_b=dedent("""\ + import asyncio + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio + async def test_b(): + assert type(asyncio.get_running_loop()).__name__ == "CustomEventLoopB" + """), + ) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=2) + + +def test_hook_factories_work_in_auto_mode(pytester: Pytester) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + + class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return {"custom": CustomEventLoop} + """)) + pytester.makepyfile(dedent("""\ + import asyncio + + pytest_plugins = "pytest_asyncio" + + async def test_uses_custom_loop(): + assert type(asyncio.get_running_loop()).__name__ == "CustomEventLoop" + """)) + result = pytester.runpytest("--asyncio-mode=auto") + result.assert_outcomes(passed=1) + + +def test_function_loop_scope_allows_per_test_factories_with_session_default( + pytester: Pytester, +) -> None: + pytester.makeini( + "[pytest]\nasyncio_default_fixture_loop_scope = function\n" + "asyncio_default_test_loop_scope = session" + ) + pytester.makeconftest(dedent("""\ + import asyncio + + class CustomEventLoopA(asyncio.SelectorEventLoop): + pass + + class CustomEventLoopB(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + if item.name.endswith("a"): + return {"factory_a": CustomEventLoopA} + else: + return {"factory_b": CustomEventLoopB} + """)) + pytester.makepyfile(dedent("""\ + import asyncio + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio(loop_scope="function") + async def test_a(): + assert type(asyncio.get_running_loop()).__name__ == "CustomEventLoopA" + + @pytest.mark.asyncio(loop_scope="function") + async def test_b(): + assert type(asyncio.get_running_loop()).__name__ == "CustomEventLoopB" + """)) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=2) From b6f574c2a05f60fec6a3c886faae806066b6c24f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 18:52:34 +0000 Subject: [PATCH 088/168] Build(deps): Bump codecov/codecov-action from 5.5.2 to 5.5.3 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.5.2 to 5.5.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/671740ac38dd9b0130fbe1cec585b89eea48d3de...1af58845a975a7985b0beb0cbe6fbbb71a41dbad) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: 5.5.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 8c9517ce..7695faae 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@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 + uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5.5.3 with: files: coverage.xml fail_ci_if_error: true From 70f1e9561df4c652bd004f7a71744e98fd1ebcaf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 20:11:21 +0000 Subject: [PATCH 089/168] Build(deps): Bump attrs from 25.4.0 to 26.1.0 Bumps [attrs](https://github.com/python-attrs/attrs) from 25.4.0 to 26.1.0. - [Release notes](https://github.com/python-attrs/attrs/releases) - [Changelog](https://github.com/python-attrs/attrs/blob/main/CHANGELOG.md) - [Commits](https://github.com/python-attrs/attrs/compare/25.4.0...26.1.0) --- updated-dependencies: - dependency-name: attrs dependency-version: 26.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 82dc425f..0de0a3ef 100644 --- a/constraints.txt +++ b/constraints.txt @@ -1,6 +1,6 @@ alabaster==1.0.0 annotated-types-0.7.0 -attrs==25.4.0 +attrs==26.1.0 babel==2.18.0 backports.asyncio.runner==1.2.0 backports.tarfile==1.2.0 From b95358a3071d4be54941b1d4ef2c6c48698bd234 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 20:10:51 +0000 Subject: [PATCH 090/168] Build(deps): Bump coverage from 7.13.4 to 7.13.5 Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.13.4 to 7.13.5. - [Release notes](https://github.com/coveragepy/coveragepy/releases) - [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst) - [Commits](https://github.com/coveragepy/coveragepy/compare/7.13.4...7.13.5) --- updated-dependencies: - dependency-name: coverage dependency-version: 7.13.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 0de0a3ef..1630a50e 100644 --- a/constraints.txt +++ b/constraints.txt @@ -9,7 +9,7 @@ charset-normalizer==3.4.6 check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.1 -coverage==7.13.4 +coverage==7.13.5 cryptography==46.0.5 docutils==0.21.2 exceptiongroup==1.3.1 From 991a0a896c786648fd1bf4e17c18430104b652be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 20:10:55 +0000 Subject: [PATCH 091/168] Build(deps): Bump jaraco-context from 6.1.1 to 6.1.2 Bumps [jaraco-context](https://github.com/jaraco/jaraco.context) from 6.1.1 to 6.1.2. - [Release notes](https://github.com/jaraco/jaraco.context/releases) - [Changelog](https://github.com/jaraco/jaraco.context/blob/main/NEWS.rst) - [Commits](https://github.com/jaraco/jaraco.context/compare/v6.1.1...v6.1.2) --- updated-dependencies: - dependency-name: jaraco-context dependency-version: 6.1.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 1630a50e..eb104e3b 100644 --- a/constraints.txt +++ b/constraints.txt @@ -21,7 +21,7 @@ imagesize==2.0.0 importlib_metadata==8.7.1 iniconfig==2.3.0 jaraco.classes==3.4.0 -jaraco.context==6.1.1 +jaraco.context==6.1.2 jaraco.functools==4.4.0 jeepney==0.9.0 Jinja2==3.1.6 From 6a53907a31a73d509ba00585a4ff67e8b2a97c3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 20:10:46 +0000 Subject: [PATCH 092/168] Build(deps): Bump importlib-metadata from 8.7.1 to 9.0.0 Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 8.7.1 to 9.0.0. - [Release notes](https://github.com/python/importlib_metadata/releases) - [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst) - [Commits](https://github.com/python/importlib_metadata/compare/v8.7.1...v9.0.0) --- updated-dependencies: - dependency-name: importlib-metadata dependency-version: 9.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index eb104e3b..05a6ebf4 100644 --- a/constraints.txt +++ b/constraints.txt @@ -18,7 +18,7 @@ iniconfig==2.3.0 id==1.6.1 idna==3.11 imagesize==2.0.0 -importlib_metadata==8.7.1 +importlib_metadata==9.0.0 iniconfig==2.3.0 jaraco.classes==3.4.0 jaraco.context==6.1.2 From f0928f5b58d1c2c2d8fd579389209d381b17c7bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 18:52:39 +0000 Subject: [PATCH 093/168] Build(deps): Bump codecov/codecov-action from 5.5.3 to 6.0.0 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.5.3 to 6.0.0. - [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/1af58845a975a7985b0beb0cbe6fbbb71a41dbad...57e3a136b779b570ffcdbf80b3bdc90e7fab3de2) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... 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 7695faae..304d44ad 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@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5.5.3 + uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 with: files: coverage.xml fail_ci_if_error: true From 0de370fc79fc50c75b9d5e15480da96039c7a411 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 3 Apr 2026 21:14:04 +0200 Subject: [PATCH 094/168] Import asyncio.AbstractEventLoopPolicy only for type checking --- changelog.d/1394.changed.rst | 2 ++ pytest_asyncio/plugin.py | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 changelog.d/1394.changed.rst diff --git a/changelog.d/1394.changed.rst b/changelog.d/1394.changed.rst new file mode 100644 index 00000000..de757df7 --- /dev/null +++ b/changelog.d/1394.changed.rst @@ -0,0 +1,2 @@ +Only import ``asyncio.AbstractEventLoopPolicy`` for type checking to avoid raising +a DeprecationWarning. diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index a69350bd..5358d476 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -12,7 +12,7 @@ import sys import traceback import warnings -from asyncio import AbstractEventLoop, AbstractEventLoopPolicy +from asyncio import AbstractEventLoop from collections.abc import ( AsyncIterator, Awaitable, @@ -25,6 +25,7 @@ ) from types import AsyncGeneratorType, CoroutineType from typing import ( + TYPE_CHECKING, Any, Literal, ParamSpec, @@ -61,6 +62,11 @@ else: from typing_extensions import TypeIs +if TYPE_CHECKING: + # AbstractEventLoopPolicy is deprecated and scheduled for removal in Python 3.16 + # Import it for type checking only to avoid raising a DeprecationWarning. + from asyncio import AbstractEventLoopPolicy + _ScopeName = Literal["session", "package", "module", "class", "function"] _R = TypeVar("_R", bound=Awaitable[Any] | AsyncIterator[Any]) _P = ParamSpec("_P") From 0d37a665ce6cf06a8f5557350bf30822e3242cd6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 18:57:53 +0000 Subject: [PATCH 095/168] Build(deps): Bump hypothesis from 6.151.9 to 6.151.10 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.151.9 to 6.151.10. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.151.9...hypothesis-python-6.151.10) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.151.10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 05a6ebf4..d12ce51b 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.13.5 cryptography==46.0.5 docutils==0.21.2 exceptiongroup==1.3.1 -hypothesis==6.151.9 +hypothesis==6.151.10 iniconfig==2.3.0 id==1.6.1 idna==3.11 From 926ccdc18a1c3bec0711a2c2390cd3cccb0bc629 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 18:58:03 +0000 Subject: [PATCH 096/168] Build(deps): Bump setuptools-scm from 9.2.2 to 10.0.5 Bumps [setuptools-scm](https://github.com/pypa/setuptools-scm) from 9.2.2 to 10.0.5. - [Release notes](https://github.com/pypa/setuptools-scm/releases) - [Changelog](https://github.com/pypa/setuptools-scm/blob/main/RELEASE_SYSTEM.md) - [Commits](https://github.com/pypa/setuptools-scm/compare/v9.2.2...setuptools-scm-v10.0.5) --- updated-dependencies: - dependency-name: setuptools-scm dependency-version: 10.0.5 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index d12ce51b..8fc12141 100644 --- a/constraints.txt +++ b/constraints.txt @@ -45,7 +45,7 @@ rfc3986==2.0.0 rich==14.3.3 SecretStorage==3.5.0 setuptools==82.0.1 -setuptools-scm==9.2.2 +setuptools-scm==10.0.5 snowballstemmer==3.0.1 sortedcontainers==2.4.0 Sphinx==8.1.3 From 222a5fb6dc09b7c1bac10841243fcce432e56893 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 18:57:40 +0000 Subject: [PATCH 097/168] Build(deps): Bump cryptography from 46.0.5 to 46.0.6 Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.5 to 46.0.6. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/46.0.5...46.0.6) --- updated-dependencies: - dependency-name: cryptography dependency-version: 46.0.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 8fc12141..bed50dc0 100644 --- a/constraints.txt +++ b/constraints.txt @@ -10,7 +10,7 @@ check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.1 coverage==7.13.5 -cryptography==46.0.5 +cryptography==46.0.6 docutils==0.21.2 exceptiongroup==1.3.1 hypothesis==6.151.10 From 04c7b5946698bb09df5fa1cea8e0a9bacf53f29f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 18:57:34 +0000 Subject: [PATCH 098/168] Build(deps): Bump requests from 2.32.5 to 2.33.1 Bumps [requests](https://github.com/psf/requests) from 2.32.5 to 2.33.1. - [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.5...v2.33.1) --- updated-dependencies: - dependency-name: requests dependency-version: 2.33.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index bed50dc0..e4175ede 100644 --- a/constraints.txt +++ b/constraints.txt @@ -39,7 +39,7 @@ pydantic==2.12.5 pydantic-core==2.41.5 pytest==9.0.2 readme-renderer==44.0 -requests==2.32.5 +requests==2.33.1 requests-toolbelt==1.0.0 rfc3986==2.0.0 rich==14.3.3 From 2c4e88fa53da0bdf350cc367fdb2ead7e5c94842 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 18:57:23 +0000 Subject: [PATCH 099/168] Build(deps): Bump pygments from 2.19.2 to 2.20.0 Bumps [pygments](https://github.com/pygments/pygments) from 2.19.2 to 2.20.0. - [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.2...2.20.0) --- updated-dependencies: - dependency-name: pygments dependency-version: 2.20.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index e4175ede..a19a9369 100644 --- a/constraints.txt +++ b/constraints.txt @@ -33,7 +33,7 @@ more-itertools==10.8.0 nh3==0.3.3 packaging==26.0 pluggy==1.6.0 -Pygments==2.19.2 +Pygments==2.20.0 pycparser==3.0 pydantic==2.12.5 pydantic-core==2.41.5 From 6ee36c0b26e5fa84cfdc5774a46ac7ed1e7ba61d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 18:57:17 +0000 Subject: [PATCH 100/168] Build(deps): Bump tomli from 2.4.0 to 2.4.1 Bumps [tomli](https://github.com/hukkin/tomli) from 2.4.0 to 2.4.1. - [Changelog](https://github.com/hukkin/tomli/blob/master/CHANGELOG.md) - [Commits](https://github.com/hukkin/tomli/compare/2.4.0...2.4.1) --- updated-dependencies: - dependency-name: tomli dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index a19a9369..2c03f11c 100644 --- a/constraints.txt +++ b/constraints.txt @@ -57,7 +57,7 @@ sphinxcontrib-jquery==4.1 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==2.0.0 sphinxcontrib-serializinghtml==2.0.0 -tomli==2.4.0 +tomli==2.4.1 twine==6.2.0 typing_extensions==4.15.0 typing-inspection==0.4.2 From 1c888abcbd0b114e692c565ee6f5b9899d092942 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 18:57:09 +0000 Subject: [PATCH 101/168] Build(deps): Bump nh3 from 0.3.3 to 0.3.4 Bumps [nh3](https://github.com/messense/nh3) from 0.3.3 to 0.3.4. - [Release notes](https://github.com/messense/nh3/releases) - [Commits](https://github.com/messense/nh3/compare/v0.3.3...v0.3.4) --- updated-dependencies: - dependency-name: nh3 dependency-version: 0.3.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 2c03f11c..641147c3 100644 --- a/constraints.txt +++ b/constraints.txt @@ -30,7 +30,7 @@ markdown-it-py==4.0.0 MarkupSafe==3.0.3 mdurl==0.1.2 more-itertools==10.8.0 -nh3==0.3.3 +nh3==0.3.4 packaging==26.0 pluggy==1.6.0 Pygments==2.20.0 From 89fc1f32e920225b2ac087a9acfcdd2f672b707f Mon Sep 17 00:00:00 2001 From: dromanov Date: Sun, 5 Apr 2026 22:44:40 +0300 Subject: [PATCH 102/168] chore: add sphinx-tabs to dependency --- constraints.txt | 1 + pyproject.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/constraints.txt b/constraints.txt index 641147c3..f59d34c6 100644 --- a/constraints.txt +++ b/constraints.txt @@ -49,6 +49,7 @@ setuptools-scm==10.0.5 snowballstemmer==3.0.1 sortedcontainers==2.4.0 Sphinx==8.1.3 +sphinx-tabs==3.5.0 sphinx-rtd-theme==3.1.0 sphinxcontrib-applehelp==2.0.0 sphinxcontrib-devhelp==2.0.0 diff --git a/pyproject.toml b/pyproject.toml index 2d629589..f52bc823 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,7 @@ dependencies = [ optional-dependencies.docs = [ "sphinx>=5.3", "sphinx-rtd-theme>=1", + "sphinx-tabs>=3.5", ] optional-dependencies.testing = [ "coverage>=6.2", From 1d63a2eea4050ab5c1098afadb6adba26a2decb3 Mon Sep 17 00:00:00 2001 From: dromanov Date: Sun, 5 Apr 2026 22:44:59 +0300 Subject: [PATCH 103/168] docs: add changelog file --- changelog.d/641.added.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/641.added.rst diff --git a/changelog.d/641.added.rst b/changelog.d/641.added.rst new file mode 100644 index 00000000..c14a381e --- /dev/null +++ b/changelog.d/641.added.rst @@ -0,0 +1 @@ +Added ``sphinx-tabs`` to organize documentation examples into tabs. From fbfde93fcf073454ca73084e4951cea2c479c3a0 Mon Sep 17 00:00:00 2001 From: dromanov Date: Sun, 5 Apr 2026 22:46:30 +0300 Subject: [PATCH 104/168] feat(docs): add tabs and group tabs to improve documentation examples --- docs/concepts.rst | 48 +++++++++++++------ docs/conf.py | 2 +- .../change_default_fixture_loop.rst | 29 ++++++----- .../change_default_test_loop.rst | 32 ++++++++----- docs/reference/markers/index.rst | 25 ++++++---- 5 files changed, 87 insertions(+), 49 deletions(-) diff --git a/docs/concepts.rst b/docs/concepts.rst index 591059a8..3ac57069 100644 --- a/docs/concepts.rst +++ b/docs/concepts.rst @@ -47,30 +47,50 @@ Assigning neighboring tests to different event loop scopes is discouraged as it Test discovery modes ==================== -Pytest-asyncio provides two modes for test discovery, *strict* and *auto*. This can be set through Pytest's ``--asyncio-mode`` command line flag, or through the configuration file: +Pytest-asyncio provides two modes for test discovery, *strict* and *auto*. +This can be set through Pytest's ``--asyncio-mode`` command line flag, +or through the configuration file. -.. code-block:: toml +.. tabs:: - [tool.pytest.ini_options] - asyncio_mode = "auto" # or "strict" + .. group-tab:: Strict mode -Strict mode ------------ + .. code-block:: toml -In strict mode pytest-asyncio will only run tests that have the *asyncio* marker and will only evaluate async fixtures decorated with ``@pytest_asyncio.fixture``. Test functions and fixtures without these markers and decorators will not be handled by pytest-asyncio. + [tool.pytest.ini_options] + asyncio_mode = "strict" -This mode is intended for projects that want so support multiple asynchronous programming libraries as it allows pytest-asyncio to coexist with other async testing plugins in the same codebase. + In strict mode pytest-asyncio will only run tests that have the *asyncio* marker + and will only evaluate async fixtures decorated with ``@pytest_asyncio.fixture``. + Test functions and fixtures without these markers and decorators will not be + handled by pytest-asyncio. -Pytest automatically enables installed plugins. As a result pytest plugins need to coexist peacefully in their default configuration. This is why strict mode is the default mode. + This mode is intended for projects that want to support multiple asynchronous + programming libraries as it allows pytest-asyncio to coexist with other async + testing plugins in the same codebase. -Auto mode ---------- + Pytest automatically enables installed plugins. As a result pytest plugins + need to coexist peacefully in their default configuration. This is why strict + mode is the default mode. -In *auto* mode pytest-asyncio automatically adds the *asyncio* marker to all asynchronous test functions. It will also take ownership of all async fixtures, regardless of whether they are decorated with ``@pytest.fixture`` or ``@pytest_asyncio.fixture``. + .. group-tab:: Auto mode -This mode is intended for projects that use *asyncio* as their only asynchronous programming library. Auto mode makes for the simplest test and fixture configuration and is the recommended default. + .. code-block:: toml -If you intend to support multiple asynchronous programming libraries, e.g. *asyncio* and *trio*, strict mode will be the preferred option. + [tool.pytest.ini_options] + asyncio_mode = "auto" + + In *auto* mode pytest-asyncio automatically adds the *asyncio* marker to all + asynchronous test functions. It will also take ownership of all async fixtures, + regardless of whether they are decorated with ``@pytest.fixture`` or + ``@pytest_asyncio.fixture``. + + This mode is intended for projects that use *asyncio* as their only asynchronous + programming library. Auto mode makes for the simplest test and fixture + configuration and is the recommended default. + + If you intend to support multiple asynchronous programming libraries, + e.g. *asyncio* and *trio*, strict mode will be the preferred option. .. _concepts/concurrent_execution: diff --git a/docs/conf.py b/docs/conf.py index 62a48a45..4718d217 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,7 +16,7 @@ # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -extensions = [] +extensions = ["sphinx_tabs.tabs"] templates_path = ["_templates"] exclude_patterns = [] diff --git a/docs/how-to-guides/change_default_fixture_loop.rst b/docs/how-to-guides/change_default_fixture_loop.rst index b54fef8e..0917abc7 100644 --- a/docs/how-to-guides/change_default_fixture_loop.rst +++ b/docs/how-to-guides/change_default_fixture_loop.rst @@ -3,22 +3,27 @@ How to change the default event loop scope of all fixtures ========================================================== The :ref:`configuration/asyncio_default_fixture_loop_scope` configuration option sets the default event loop scope for asynchronous fixtures. The following code snippets configure all fixtures to run in a session-scoped loop by default: -.. code-block:: ini - :caption: pytest.ini +.. tabs:: - [pytest] - asyncio_default_fixture_loop_scope = session + .. tab:: pytest.ini -.. code-block:: toml - :caption: pyproject.toml + .. code-block:: ini - [tool.pytest.ini_options] - asyncio_default_fixture_loop_scope = "session" + [pytest] + asyncio_default_fixture_loop_scope = session -.. code-block:: ini - :caption: setup.cfg + .. tab:: pyproject.toml - [tool:pytest] - asyncio_default_fixture_loop_scope = session + .. code-block:: toml + + [tool.pytest.ini_options] + asyncio_default_fixture_loop_scope = "session" + + .. tab:: setup.cfg + + .. code-block:: ini + + [tool:pytest] + asyncio_default_fixture_loop_scope = session Please refer to :ref:`configuration/asyncio_default_fixture_loop_scope` for other valid scopes. diff --git a/docs/how-to-guides/change_default_test_loop.rst b/docs/how-to-guides/change_default_test_loop.rst index c5b625d1..d008a753 100644 --- a/docs/how-to-guides/change_default_test_loop.rst +++ b/docs/how-to-guides/change_default_test_loop.rst @@ -3,22 +3,28 @@ How to change the default event loop scope of all tests ======================================================= The :ref:`configuration/asyncio_default_test_loop_scope` configuration option sets the default event loop scope for asynchronous tests. The following code snippets configure all tests to run in a session-scoped loop by default: -.. code-block:: ini - :caption: pytest.ini +.. tabs:: - [pytest] - asyncio_default_test_loop_scope = session + .. tab:: pytest.ini -.. code-block:: toml - :caption: pyproject.toml + .. code-block:: ini - [tool.pytest.ini_options] - asyncio_default_test_loop_scope = "session" + [pytest] + asyncio_default_test_loop_scope = session -.. code-block:: ini - :caption: setup.cfg + .. tab:: pyproject.toml - [tool:pytest] - asyncio_default_test_loop_scope = session + .. code-block:: toml -Please refer to :ref:`configuration/asyncio_default_test_loop_scope` for other valid scopes. + [tool.pytest.ini_options] + asyncio_default_test_loop_scope = "session" + + .. tab:: setup.cfg + + .. code-block:: ini + + [tool:pytest] + asyncio_default_test_loop_scope = session + +Please refer to :ref:`configuration/asyncio_default_test_loop_scope` +for other valid scopes. diff --git a/docs/reference/markers/index.rst b/docs/reference/markers/index.rst index 761196a8..bc0a584a 100644 --- a/docs/reference/markers/index.rst +++ b/docs/reference/markers/index.rst @@ -9,25 +9,32 @@ Markers A coroutine or async generator with this marker is treated as a test function by pytest. The marked function is executed as an asyncio task in the event loop provided by pytest-asyncio. -.. include:: function_scoped_loop_strict_mode_example.py - :code: python +Multiple async tests in a single class or module can be marked in different ways: -Multiple async tests in a single class or module can be marked using |pytestmark|_. +.. tabs:: -.. include:: function_scoped_loop_pytestmark_strict_mode_example.py - :code: python + .. tab:: decorator + + .. include:: function_scoped_loop_strict_mode_example.py + :code: python + + .. tab:: pytestmark + + .. include:: function_scoped_loop_pytestmark_strict_mode_example.py + :code: python The ``pytest.mark.asyncio`` marker can be omitted entirely in |auto mode|_ where the *asyncio* marker is added automatically to *async* test functions. -By default, each test runs in it's own asyncio event loop. +By default, each test runs in its own asyncio event loop. Multiple tests can share the same event loop by providing a *loop_scope* keyword argument to the *asyncio* mark. -The supported scopes are *function,* *class,* and *module,* *package,* and *session*. -The following code example provides a shared event loop for all tests in `TestClassScopedLoop`: +The supported scopes are *function*, *class*, *module*, *package*, and *session*. + +The following code example provides a shared event loop for all tests in ``TestClassScopedLoop``: .. include:: class_scoped_loop_strict_mode_example.py :code: python -Similar to class-scoped event loops, a module-scoped loop is provided when setting mark's scope to *module:* +Similar to class-scoped event loops, a module-scoped loop is provided when setting mark's scope to *module*: .. include:: module_scoped_loop_strict_mode_example.py :code: python From df9239ba458616111f88dd0662083f6e93430163 Mon Sep 17 00:00:00 2001 From: David Dzhalaev <72649244+dzhalaevd@users.noreply.github.com> Date: Tue, 7 Apr 2026 00:32:53 +0300 Subject: [PATCH 105/168] refactor: apply suggestions from code review - change news fragment to the correct category - add more context in news fragment Co-authored-by: Michael Seifert --- changelog.d/1395.downstream.rst | 1 + changelog.d/641.added.rst | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 changelog.d/1395.downstream.rst delete mode 100644 changelog.d/641.added.rst diff --git a/changelog.d/1395.downstream.rst b/changelog.d/1395.downstream.rst new file mode 100644 index 00000000..ff7cc818 --- /dev/null +++ b/changelog.d/1395.downstream.rst @@ -0,0 +1 @@ +Added dependency on ``sphinx-tabs >= 3.5`` to organize documentation examples into tabs. diff --git a/changelog.d/641.added.rst b/changelog.d/641.added.rst deleted file mode 100644 index c14a381e..00000000 --- a/changelog.d/641.added.rst +++ /dev/null @@ -1 +0,0 @@ -Added ``sphinx-tabs`` to organize documentation examples into tabs. From 15a9fefc9cd95a7350c4f16f03d2f21b68c595ee Mon Sep 17 00:00:00 2001 From: dromanov Date: Sun, 29 Mar 2026 22:49:12 +0300 Subject: [PATCH 106/168] feat(workflows): add gh release instead of ncipollo/release-action --- .github/workflows/main.yml | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 304d44ad..00ccce06 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -193,16 +193,32 @@ jobs: path: dist - name: Create GitHub Release if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0 - with: - name: pytest-asyncio ${{ needs.build.outputs.version }} - artifacts: dist/* - bodyFile: release-notes.md - prerelease: ${{ needs.build.outputs.prerelease }} - token: ${{ secrets.GITHUB_TOKEN }} - allowUpdates: true - draft: true - skipIfReleaseExists: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ needs.build.outputs.version }} + PRERELEASE: ${{ needs.build.outputs.prerelease }} + run: | + TAG_NAME="${GITHUB_REF#refs/tags/}" + + IS_DRAFT=$(gh release view "${TAG_NAME}" --json isDraft --jq '.isDraft' 2>/dev/null || echo "") + if [ -n "${IS_DRAFT}" ]; then + if [ "${IS_DRAFT}" = "false" ]; then + exit 0 + fi + gh release edit "${TAG_NAME}" \ + --title "pytest-asyncio ${VERSION}" \ + --notes-file release-notes.md \ + --draft + + gh release upload "${TAG_NAME}" dist/* --clobber + else + gh release create "${TAG_NAME}" \ + --title "pytest-asyncio ${VERSION}" \ + --notes-file release-notes.md \ + --draft \ + $( [ "${PRERELEASE}" = "true" ] && echo "--prerelease" ) \ + dist/* + fi publish-test-pypi: name: Publish packages to test.pypi.org From 452129d966fa4608d720ca67e54d88e942a6b46f Mon Sep 17 00:00:00 2001 From: dromanov Date: Tue, 31 Mar 2026 02:40:12 +0300 Subject: [PATCH 107/168] docs: add newsfragment --- changelog.d/1392.changed.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/1392.changed.rst diff --git a/changelog.d/1392.changed.rst b/changelog.d/1392.changed.rst new file mode 100644 index 00000000..691964e8 --- /dev/null +++ b/changelog.d/1392.changed.rst @@ -0,0 +1 @@ +Change ``ncipollo/release-action`` on ``gh release`` From c2e0cdd371c43e6e35fdd07e2534ebb3779363a1 Mon Sep 17 00:00:00 2001 From: dromanov Date: Wed, 1 Apr 2026 11:24:27 +0300 Subject: [PATCH 108/168] fix: make linters happy --- .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 00ccce06..e646771e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -209,7 +209,7 @@ jobs: --title "pytest-asyncio ${VERSION}" \ --notes-file release-notes.md \ --draft - + gh release upload "${TAG_NAME}" dist/* --clobber else gh release create "${TAG_NAME}" \ From bad34150347599330ddd2cef91aef964ab5eab2f Mon Sep 17 00:00:00 2001 From: dromanov Date: Mon, 6 Apr 2026 23:49:33 +0300 Subject: [PATCH 109/168] refactor: apply suggestions from code review * remove `changed` file * simplify main.yaml * replace VERSION to TAG_NAME in title Co-authored-by: Michael Seifert --- .github/workflows/main.yml | 18 ++++-------------- changelog.d/1392.changed.rst | 1 - 2 files changed, 4 insertions(+), 15 deletions(-) delete mode 100644 changelog.d/1392.changed.rst diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e646771e..f1d74553 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -195,25 +195,15 @@ jobs: if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - VERSION: ${{ needs.build.outputs.version }} PRERELEASE: ${{ needs.build.outputs.prerelease }} run: | TAG_NAME="${GITHUB_REF#refs/tags/}" - IS_DRAFT=$(gh release view "${TAG_NAME}" --json isDraft --jq '.isDraft' 2>/dev/null || echo "") - if [ -n "${IS_DRAFT}" ]; then - if [ "${IS_DRAFT}" = "false" ]; then - exit 0 - fi - gh release edit "${TAG_NAME}" \ - --title "pytest-asyncio ${VERSION}" \ - --notes-file release-notes.md \ - --draft - - gh release upload "${TAG_NAME}" dist/* --clobber - else + gh release view "${TAG_NAME}" >/dev/null + RELEASE_EXISTS=$? + if [ $RELEASE_EXISTS -ne 0 ]; then gh release create "${TAG_NAME}" \ - --title "pytest-asyncio ${VERSION}" \ + --title "pytest-asyncio ${TAG_NAME}" \ --notes-file release-notes.md \ --draft \ $( [ "${PRERELEASE}" = "true" ] && echo "--prerelease" ) \ diff --git a/changelog.d/1392.changed.rst b/changelog.d/1392.changed.rst deleted file mode 100644 index 691964e8..00000000 --- a/changelog.d/1392.changed.rst +++ /dev/null @@ -1 +0,0 @@ -Change ``ncipollo/release-action`` on ``gh release`` From d2e38b0ec2d934ef5135e6e239bdca7dcb9fed0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 20:02:12 +0000 Subject: [PATCH 110/168] Build(deps): Bump click from 8.3.1 to 8.3.2 Bumps [click](https://github.com/pallets/click) from 8.3.1 to 8.3.2. - [Release notes](https://github.com/pallets/click/releases) - [Changelog](https://github.com/pallets/click/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/click/compare/8.3.1...8.3.2) --- updated-dependencies: - dependency-name: click dependency-version: 8.3.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index f59d34c6..26fc7e85 100644 --- a/constraints.txt +++ b/constraints.txt @@ -8,7 +8,7 @@ certifi==2026.2.25 charset-normalizer==3.4.6 check-wheel-contents==0.6.3 cffi==2.0.0 -click==8.3.1 +click==8.3.2 coverage==7.13.5 cryptography==46.0.6 docutils==0.21.2 From a82b3f17ca63c99d5a1c4f5e018fd8446b64bd30 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2026 18:52:48 +0000 Subject: [PATCH 111/168] Build(deps): Bump pypa/gh-action-pypi-publish from 1.13.0 to 1.14.0 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.13.0 to 1.14.0. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e...cef221092ed1bacb1cc03d23a2d87d1d172e277b) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-version: 1.14.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f1d74553..4bba3e13 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -224,7 +224,7 @@ jobs: name: dist path: dist - name: Upload to test.pypi.org - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 with: repository-url: https://test.pypi.org/legacy/ @@ -246,4 +246,4 @@ jobs: run: | tree dist - name: PyPI upload - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 From c374f924d039500591e99ac70d1df601eb7d2db5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 20:01:41 +0000 Subject: [PATCH 112/168] Build(deps): Bump hypothesis from 6.151.10 to 6.151.11 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.151.10 to 6.151.11. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.151.10...hypothesis-python-6.151.11) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.151.11 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 26fc7e85..40ca25ca 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.13.5 cryptography==46.0.6 docutils==0.21.2 exceptiongroup==1.3.1 -hypothesis==6.151.10 +hypothesis==6.151.11 iniconfig==2.3.0 id==1.6.1 idna==3.11 From 69a35419b904a6937bc866507063020abacba09b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 21:51:00 +0000 Subject: [PATCH 113/168] Build(deps): Bump charset-normalizer from 3.4.6 to 3.4.7 Bumps [charset-normalizer](https://github.com/jawah/charset_normalizer) from 3.4.6 to 3.4.7. - [Release notes](https://github.com/jawah/charset_normalizer/releases) - [Changelog](https://github.com/jawah/charset_normalizer/blob/master/CHANGELOG.md) - [Commits](https://github.com/jawah/charset_normalizer/compare/3.4.6...3.4.7) --- updated-dependencies: - dependency-name: charset-normalizer dependency-version: 3.4.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 40ca25ca..811fa26a 100644 --- a/constraints.txt +++ b/constraints.txt @@ -5,7 +5,7 @@ babel==2.18.0 backports.asyncio.runner==1.2.0 backports.tarfile==1.2.0 certifi==2026.2.25 -charset-normalizer==3.4.6 +charset-normalizer==3.4.7 check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.2 From c6ce43cb4a1bd5374f825ecb37adf2399443bac8 Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Sun, 29 Mar 2026 03:19:00 +0100 Subject: [PATCH 114/168] Fix loop factory lifecycle --- changelog.d/1373.fixed.rst | 1 + pytest_asyncio/plugin.py | 17 +++- tests/test_loop_factory_parametrization.py | 107 +++++++++++++++++++++ 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 changelog.d/1373.fixed.rst diff --git a/changelog.d/1373.fixed.rst b/changelog.d/1373.fixed.rst new file mode 100644 index 00000000..0e99d371 --- /dev/null +++ b/changelog.d/1373.fixed.rst @@ -0,0 +1 @@ +Fixed ``pytest_asyncio_loop_factories`` not installing the custom event loop as the current loop, and async fixture teardown/cache invalidation not being tied to the runner lifecycle. diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 5358d476..428751f0 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -504,7 +504,15 @@ def _can_substitute(item: Function) -> bool: def setup(self) -> None: runner_fixture_id = f"_{self._loop_scope}_scoped_runner" - if runner_fixture_id not in self.fixturenames: + if runner_fixture_id in self.fixturenames: + return super().setup() + # The runner must be resolved before async fixtures when loop + # factories are configured. Otherwise, the async fixtures see a + # stale loop from the previous factory. + hook_caller = self.config.hook.pytest_asyncio_loop_factories + if hook_caller.get_hookimpls(): + self.fixturenames.insert(0, runner_fixture_id) + else: self.fixturenames.append(runner_fixture_id) return super().setup() @@ -852,6 +860,11 @@ def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: ) runner_fixture_id = f"_{loop_scope}_scoped_runner" runner = request.getfixturevalue(runner_fixture_id) + # Prevent the runner closing before the fixture's async teardown. + runner_fixturedef = request._get_active_fixturedef(runner_fixture_id) + runner_fixturedef.addfinalizer( + functools.partial(fixturedef.finish, request=request) + ) synchronizer = _fixture_synchronizer(fixturedef, runner, request) _make_asyncio_fixture_function(synchronizer, loop_scope) with MonkeyPatch.context() as c: @@ -946,6 +959,8 @@ def _scoped_runner( debug=debug_mode, loop_factory=_asyncio_loop_factory, ).__enter__() + if _asyncio_loop_factory is not None: + _set_event_loop(runner.get_loop()) try: yield runner except Exception as e: diff --git a/tests/test_loop_factory_parametrization.py b/tests/test_loop_factory_parametrization.py index f6bac235..6d016338 100644 --- a/tests/test_loop_factory_parametrization.py +++ b/tests/test_loop_factory_parametrization.py @@ -571,3 +571,110 @@ async def test_b(): """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=2) + + +def test_sync_fixture_sees_same_loop_as_async_test_under_custom_factory( + pytester: Pytester, +) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + + class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return {"custom": CustomEventLoop} + """)) + pytester.makepyfile(dedent("""\ + import asyncio + import pytest + import pytest_asyncio + + pytest_plugins = "pytest_asyncio" + + @pytest_asyncio.fixture(autouse=True) + def enable_debug_on_event_loop(): + asyncio.get_event_loop().set_debug(True) + + @pytest.mark.asyncio + async def test_debug_mode_visible(): + assert asyncio.get_running_loop().get_debug() + """)) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=1) + + +@pytest.mark.parametrize("loop_scope", ("module", "package", "session")) +def test_async_generator_fixture_teardown_runs_under_custom_factory( + pytester: Pytester, + loop_scope: str, +) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent(f"""\ + import asyncio + import pytest_asyncio + + class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return {{"custom": CustomEventLoop}} + + @pytest_asyncio.fixture( + autouse=True, scope="{loop_scope}", loop_scope="{loop_scope}" + ) + async def fixture_with_teardown(): + yield + print("TEARDOWN_EXECUTED") + """)) + pytester.makepyfile(dedent(f"""\ + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio(loop_scope="{loop_scope}") + async def test_passes(): + assert True + """)) + result = pytester.runpytest("--asyncio-mode=strict", "-s") + result.assert_outcomes(passed=1) + result.stdout.fnmatch_lines(["*TEARDOWN_EXECUTED*"]) + + +@pytest.mark.parametrize("loop_scope", ("module", "package", "session")) +def test_async_fixture_recreated_per_loop_factory_variant( + pytester: Pytester, + loop_scope: str, +) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent(f"""\ + import asyncio + import pytest_asyncio + + class CustomLoopA(asyncio.SelectorEventLoop): + pass + + class CustomLoopB(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return {{"loop_a": CustomLoopA, "loop_b": CustomLoopB}} + + @pytest_asyncio.fixture(scope="{loop_scope}", loop_scope="{loop_scope}") + async def fixture_loop_type(): + return type(asyncio.get_running_loop()).__name__ + """)) + pytester.makepyfile(dedent(f"""\ + import asyncio + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio(loop_scope="{loop_scope}") + async def test_fixture_matches_running_loop(fixture_loop_type): + running_loop_type = type(asyncio.get_running_loop()).__name__ + assert fixture_loop_type == running_loop_type + """)) + result = pytester.runpytest("--asyncio-mode=strict", "-v") + result.assert_outcomes(passed=2) From 91b428edfa57cde03e546c211fcab6bb18cb2007 Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Sun, 29 Mar 2026 15:47:20 +0100 Subject: [PATCH 115/168] Fix fixtures seeing the wrong event loop --- changelog.d/1373.fixed.rst | 2 +- pytest_asyncio/plugin.py | 60 ++++++++++- tests/test_loop_factory_parametrization.py | 113 +++++++++++++++++++++ tests/test_set_event_loop.py | 57 +++++++++++ 4 files changed, 230 insertions(+), 2 deletions(-) diff --git a/changelog.d/1373.fixed.rst b/changelog.d/1373.fixed.rst index 0e99d371..8858da3b 100644 --- a/changelog.d/1373.fixed.rst +++ b/changelog.d/1373.fixed.rst @@ -1 +1 @@ -Fixed ``pytest_asyncio_loop_factories`` not installing the custom event loop as the current loop, and async fixture teardown/cache invalidation not being tied to the runner lifecycle. +Fixed ``pytest_asyncio_loop_factories`` not installing the custom event loop as the current loop, and async fixture teardown/cache invalidation not being tied to the runner lifecycle, and sync ``@pytest_asyncio.fixture`` seeing the wrong event loop when multiple loop scopes are active. diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 428751f0..c740001e 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -334,8 +334,50 @@ def _fixture_synchronizer( return _wrap_asyncgen_fixture(fixture_function, runner, request) # type: ignore[arg-type] elif inspect.iscoroutinefunction(fixturedef.func): return _wrap_async_fixture(fixture_function, runner, request) # type: ignore[arg-type] + elif inspect.isgeneratorfunction(fixturedef.func): + return _wrap_syncgen_fixture(fixture_function, runner) # type: ignore[arg-type] else: - return fixturedef.func + return _wrap_sync_fixture(fixture_function, runner) # type: ignore[arg-type] + + +SyncGenFixtureParams = ParamSpec("SyncGenFixtureParams") +SyncGenFixtureYieldType = TypeVar("SyncGenFixtureYieldType") + + +def _wrap_syncgen_fixture( + fixture_function: Callable[ + SyncGenFixtureParams, Generator[SyncGenFixtureYieldType] + ], + runner: Runner, +) -> Callable[SyncGenFixtureParams, Generator[SyncGenFixtureYieldType]]: + @functools.wraps(fixture_function) + def _syncgen_fixture_wrapper( + *args: SyncGenFixtureParams.args, + **kwargs: SyncGenFixtureParams.kwargs, + ) -> Generator[SyncGenFixtureYieldType]: + with _temporary_event_loop(runner.get_loop()): + yield from fixture_function(*args, **kwargs) + + return _syncgen_fixture_wrapper + + +SyncFixtureParams = ParamSpec("SyncFixtureParams") +SyncFixtureReturnType = TypeVar("SyncFixtureReturnType") + + +def _wrap_sync_fixture( + fixture_function: Callable[SyncFixtureParams, SyncFixtureReturnType], + runner: Runner, +) -> Callable[SyncFixtureParams, SyncFixtureReturnType]: + @functools.wraps(fixture_function) + def _sync_fixture_wrapper( + *args: SyncFixtureParams.args, + **kwargs: SyncFixtureParams.kwargs, + ) -> SyncFixtureReturnType: + with _temporary_event_loop(runner.get_loop()): + return fixture_function(*args, **kwargs) + + return _sync_fixture_wrapper AsyncGenFixtureParams = ParamSpec("AsyncGenFixtureParams") @@ -735,6 +777,22 @@ def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: ) +@contextlib.contextmanager +def _temporary_event_loop(loop: AbstractEventLoop) -> Iterator[None]: + try: + old_loop = _get_event_loop_no_warn() + except RuntimeError: + old_loop = None + if old_loop is loop: + yield + return + _set_event_loop(loop) + try: + yield + finally: + _set_event_loop(old_loop) + + @contextlib.contextmanager def _temporary_event_loop_policy(policy: AbstractEventLoopPolicy) -> Iterator[None]: old_loop_policy = _get_event_loop_policy() diff --git a/tests/test_loop_factory_parametrization.py b/tests/test_loop_factory_parametrization.py index 6d016338..c6aec796 100644 --- a/tests/test_loop_factory_parametrization.py +++ b/tests/test_loop_factory_parametrization.py @@ -605,6 +605,119 @@ async def test_debug_mode_visible(): result.assert_outcomes(passed=1) +@pytest.mark.parametrize( + ("fixture_scope", "wider_scope"), + [ + ("function", "module"), + ("function", "package"), + ("function", "session"), + ("module", "session"), + ("package", "session"), + ], +) +def test_sync_fixture_sees_its_own_loop_when_wider_scoped_loop_active( + pytester: Pytester, + fixture_scope: str, + wider_scope: str, +) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent(f"""\ + import asyncio + import pytest_asyncio + + class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return {{"custom": CustomEventLoop}} + + @pytest_asyncio.fixture( + autouse=True, + scope="{wider_scope}", + loop_scope="{wider_scope}", + ) + async def wider_scoped_fixture(): + yield + + @pytest_asyncio.fixture( + autouse=True, + scope="{fixture_scope}", + loop_scope="{fixture_scope}", + ) + def sync_fixture_captures_loop(): + return id(asyncio.get_event_loop()) + """)) + pytester.makepyfile(dedent(f"""\ + import asyncio + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio(loop_scope="{fixture_scope}") + async def test_sync_fixture_and_test_see_same_loop(sync_fixture_captures_loop): + assert sync_fixture_captures_loop == id(asyncio.get_running_loop()) + """)) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=1) + + +@pytest.mark.parametrize( + ("fixture_scope", "wider_scope"), + [ + ("function", "module"), + ("function", "session"), + ("module", "session"), + ], +) +def test_sync_generator_fixture_teardown_sees_own_loop( + pytester: Pytester, + fixture_scope: str, + wider_scope: str, +) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent(f"""\ + import asyncio + import pytest_asyncio + + class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return {{"custom": CustomEventLoop}} + + @pytest_asyncio.fixture( + autouse=True, + scope="{wider_scope}", + loop_scope="{wider_scope}", + ) + async def wider_scoped_fixture(): + yield + + @pytest_asyncio.fixture( + autouse=True, + scope="{fixture_scope}", + loop_scope="{fixture_scope}", + ) + def sync_generator_fixture(): + loop_at_setup = id(asyncio.get_event_loop()) + yield loop_at_setup + loop_at_teardown = id(asyncio.get_event_loop()) + assert loop_at_setup == loop_at_teardown + """)) + pytester.makepyfile(dedent(f"""\ + import asyncio + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio(loop_scope="{fixture_scope}") + async def test_generator_fixture_sees_correct_loop(sync_generator_fixture): + assert sync_generator_fixture == id(asyncio.get_running_loop()) + """)) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=1) + + @pytest.mark.parametrize("loop_scope", ("module", "package", "session")) def test_async_generator_fixture_teardown_runs_under_custom_factory( pytester: Pytester, diff --git a/tests/test_set_event_loop.py b/tests/test_set_event_loop.py index 7f0d5dea..3854c04b 100644 --- a/tests/test_set_event_loop.py +++ b/tests/test_set_event_loop.py @@ -329,3 +329,60 @@ async def test_after_second(second_webserver): """)) result = pytester.runpytest("--asyncio-mode=strict") result.assert_outcomes(passed=5) + + +@pytest.mark.parametrize("test_loop_scope", ("module", "package", "session")) +@pytest.mark.parametrize( + "loop_breaking_action", + [ + "asyncio.set_event_loop(None)", + "asyncio.run(asyncio.sleep(0))", + pytest.param( + "with asyncio.Runner(): pass", + marks=pytest.mark.skipif( + sys.version_info < (3, 11), + reason="asyncio.Runner requires Python 3.11+", + ), + ), + ], +) +def test_sync_fixture_sees_correct_loop_after_loop_broken_with_factory( + pytester: Pytester, + test_loop_scope: str, + loop_breaking_action: str, +): + pytester.makeini(dedent(f"""\ + [pytest] + asyncio_default_test_loop_scope = {test_loop_scope} + asyncio_default_fixture_loop_scope = function + """)) + pytester.makepyfile(dedent(f"""\ + import asyncio + import pytest + import pytest_asyncio + + pytest_plugins = "pytest_asyncio" + + class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return {{"custom": CustomEventLoop}} + + @pytest.mark.asyncio + async def test_before(): + pass + + def test_break_event_loop(): + {loop_breaking_action} + + @pytest_asyncio.fixture(loop_scope="{test_loop_scope}") + def sync_fixture_loop_id(): + return id(asyncio.get_event_loop()) + + @pytest.mark.asyncio + async def test_sync_fixture_sees_correct_loop(sync_fixture_loop_id): + assert sync_fixture_loop_id == id(asyncio.get_running_loop()) + """)) + result = pytester.runpytest_subprocess() + result.assert_outcomes(passed=3) From 323fadfea7810b9c08cc92a5a161ced682d2f90d Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Tue, 31 Mar 2026 19:30:47 +0100 Subject: [PATCH 116/168] Fix event loop leak on Python <3.14 --- changelog.d/1373.fixed.rst | 2 +- pytest_asyncio/plugin.py | 20 +++++++++--- tests/test_loop_factory_parametrization.py | 36 ++++++++++++++++++++++ 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/changelog.d/1373.fixed.rst b/changelog.d/1373.fixed.rst index 8858da3b..793b3b06 100644 --- a/changelog.d/1373.fixed.rst +++ b/changelog.d/1373.fixed.rst @@ -1 +1 @@ -Fixed ``pytest_asyncio_loop_factories`` not installing the custom event loop as the current loop, and async fixture teardown/cache invalidation not being tied to the runner lifecycle, and sync ``@pytest_asyncio.fixture`` seeing the wrong event loop when multiple loop scopes are active. +Fixed ``pytest_asyncio_loop_factories`` not installing the custom event loop as the current loop, async fixture teardown/cache invalidation not being tied to the runner lifecycle, sync ``@pytest_asyncio.fixture`` seeing the wrong event loop when multiple loop scopes are active, and an event loop leak on Python 3.10-3.13. diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index c740001e..1f46a228 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -794,12 +794,19 @@ def _temporary_event_loop(loop: AbstractEventLoop) -> Iterator[None]: @contextlib.contextmanager -def _temporary_event_loop_policy(policy: AbstractEventLoopPolicy) -> Iterator[None]: +def _temporary_event_loop_policy( + policy: AbstractEventLoopPolicy, + *, + has_custom_factory: bool, +) -> Iterator[None]: old_loop_policy = _get_event_loop_policy() - try: - old_loop = _get_event_loop_no_warn() - except RuntimeError: + if has_custom_factory: old_loop = None + else: + try: + old_loop = _get_event_loop_no_warn() + except RuntimeError: + old_loop = None _set_event_loop_policy(policy) try: yield @@ -1012,7 +1019,10 @@ def _scoped_runner( ) -> Iterator[Runner]: new_loop_policy = event_loop_policy debug_mode = _get_asyncio_debug(request.config) - with _temporary_event_loop_policy(new_loop_policy): + with _temporary_event_loop_policy( + new_loop_policy, + has_custom_factory=_asyncio_loop_factory is not None, + ): runner = Runner( debug=debug_mode, loop_factory=_asyncio_loop_factory, diff --git a/tests/test_loop_factory_parametrization.py b/tests/test_loop_factory_parametrization.py index c6aec796..7cc629ef 100644 --- a/tests/test_loop_factory_parametrization.py +++ b/tests/test_loop_factory_parametrization.py @@ -533,6 +533,42 @@ async def test_uses_custom_loop(): result.assert_outcomes(passed=1) +def test_no_event_loop_leak_with_custom_factory(pytester: Pytester) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + import pytest_asyncio + + class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return {"custom": CustomEventLoop} + + @pytest_asyncio.fixture(autouse=True, scope="session", loop_scope="session") + async def session_fixture(): + yield + + @pytest_asyncio.fixture(autouse=True) + def sync_fixture(): + asyncio.get_event_loop() + """)) + pytester.makepyfile(dedent("""\ + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio + async def test_passes(): + assert True + """)) + result = pytester.runpytest_subprocess( + "--asyncio-mode=auto", "-W", "error::ResourceWarning" + ) + result.assert_outcomes(passed=1) + result.stderr.no_fnmatch_line("*unclosed event loop*") + + def test_function_loop_scope_allows_per_test_factories_with_session_default( pytester: Pytester, ) -> None: From f7de4d85ce50a6719c364578fd43be7e03334c37 Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Thu, 2 Apr 2026 01:08:22 +0100 Subject: [PATCH 117/168] Resolve fixture early --- pytest_asyncio/plugin.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 1f46a228..47827afb 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -546,16 +546,14 @@ def _can_substitute(item: Function) -> bool: def setup(self) -> None: runner_fixture_id = f"_{self._loop_scope}_scoped_runner" - if runner_fixture_id in self.fixturenames: - return super().setup() - # The runner must be resolved before async fixtures when loop - # factories are configured. Otherwise, the async fixtures see a - # stale loop from the previous factory. + if runner_fixture_id not in self.fixturenames: + self.fixturenames.append(runner_fixture_id) + # When loop factories are configured, resolve the loop factory + # fixture early so that a factory variant change cascades cache + # invalidation before any async fixture checks its cache. hook_caller = self.config.hook.pytest_asyncio_loop_factories if hook_caller.get_hookimpls(): - self.fixturenames.insert(0, runner_fixture_id) - else: - self.fixturenames.append(runner_fixture_id) + _ = self._request.getfixturevalue(_asyncio_loop_factory.__name__) return super().setup() def runtest(self) -> None: From d7a2d64fc9f37843b3ffce3e516f46aad76733f7 Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Fri, 3 Apr 2026 12:34:13 +0200 Subject: [PATCH 118/168] Skip ID for single factories --- pytest_asyncio/plugin.py | 4 ++- tests/test_loop_factory_parametrization.py | 29 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 47827afb..5b9a239b 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -766,10 +766,12 @@ def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: metafunc.fixturenames.append(_asyncio_loop_factory.__name__) default_loop_scope = _get_default_test_loop_scope(metafunc.config) loop_scope = marker_loop_scope or default_loop_scope + # pytest.HIDDEN_PARAM was added in pytest 8.4 + hide_id = len(effective_factories) == 1 and hasattr(pytest, "HIDDEN_PARAM") metafunc.parametrize( _asyncio_loop_factory.__name__, effective_factories.values(), - ids=effective_factories.keys(), + ids=(pytest.HIDDEN_PARAM,) if hide_id else effective_factories.keys(), indirect=True, scope=loop_scope, ) diff --git a/tests/test_loop_factory_parametrization.py b/tests/test_loop_factory_parametrization.py index 7cc629ef..8221d135 100644 --- a/tests/test_loop_factory_parametrization.py +++ b/tests/test_loop_factory_parametrization.py @@ -6,6 +6,35 @@ from pytest import Pytester +@pytest.mark.skipif( + not hasattr(pytest, "HIDDEN_PARAM"), + reason="pytest.HIDDEN_PARAM requires pytest 9.0+", +) +def test_single_factory_does_not_add_suffix_to_test_name( + pytester: Pytester, +) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + + def pytest_asyncio_loop_factories(config, item): + return {"asyncio": asyncio.new_event_loop} + """)) + pytester.makepyfile(dedent("""\ + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio + async def test_example(): + assert True + """)) + result = pytester.runpytest("--asyncio-mode=strict", "--collect-only", "-q") + result.stdout.fnmatch_lines( + ["test_single_factory_does_not_add_suffix_to_test_name.py::test_example"] + ) + + def test_named_hook_factories_apply_to_async_tests(pytester: Pytester) -> None: pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makeconftest(dedent("""\ From 07c12b13d8f35a5962651a0f9b9a2125f415bebd Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Thu, 9 Apr 2026 23:13:23 +0100 Subject: [PATCH 119/168] Fix skipif reason typo Co-authored-by: Michael Seifert --- tests/test_loop_factory_parametrization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_loop_factory_parametrization.py b/tests/test_loop_factory_parametrization.py index 8221d135..cc71bf20 100644 --- a/tests/test_loop_factory_parametrization.py +++ b/tests/test_loop_factory_parametrization.py @@ -8,7 +8,7 @@ @pytest.mark.skipif( not hasattr(pytest, "HIDDEN_PARAM"), - reason="pytest.HIDDEN_PARAM requires pytest 9.0+", + reason="pytest.HIDDEN_PARAM requires pytest 8.4+", ) def test_single_factory_does_not_add_suffix_to_test_name( pytester: Pytester, From 9f9da02c7d1ddae17a64e2391d1504a90caee319 Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Thu, 9 Apr 2026 23:16:34 +0100 Subject: [PATCH 120/168] Remove fragment --- changelog.d/1373.fixed.rst | 1 - 1 file changed, 1 deletion(-) delete mode 100644 changelog.d/1373.fixed.rst diff --git a/changelog.d/1373.fixed.rst b/changelog.d/1373.fixed.rst deleted file mode 100644 index 793b3b06..00000000 --- a/changelog.d/1373.fixed.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed ``pytest_asyncio_loop_factories`` not installing the custom event loop as the current loop, async fixture teardown/cache invalidation not being tied to the runner lifecycle, sync ``@pytest_asyncio.fixture`` seeing the wrong event loop when multiple loop scopes are active, and an event loop leak on Python 3.10-3.13. From 91edbbe80d50b8c7be2de7373369373582cb6a4d Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Thu, 9 Apr 2026 23:25:51 +0100 Subject: [PATCH 121/168] Update new fragment --- changelog.d/1164.added.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/changelog.d/1164.added.rst b/changelog.d/1164.added.rst index ccf2988a..c68d22dd 100644 --- a/changelog.d/1164.added.rst +++ b/changelog.d/1164.added.rst @@ -1,3 +1,5 @@ Added the ``pytest_asyncio_loop_factories`` hook to parametrize asyncio tests with custom event loop factories. -The hook now returns a mapping of factory names to loop factories, and ``pytest.mark.asyncio(loop_factories=[...])`` can be used to select a subset of configured factories per test. +The hook returns a mapping of factory names to loop factories, and ``pytest.mark.asyncio(loop_factories=[...])`` selects a subset of configured factories per test. When a single factory is configured, test names are unchanged on pytest 8.4+. + +Synchronous ``@pytest_asyncio.fixture`` functions now see the correct event loop when custom loop factories are configured, even when test code disrupts the current event loop (e.g., via ``asyncio.run()`` or ``asyncio.set_event_loop(None)``). From 7dbad55ce03533bd10b8afd15460b3e965f28532 Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Fri, 10 Apr 2026 14:09:06 +0100 Subject: [PATCH 122/168] Skip unavailable requested loop factories --- .../run_test_with_specific_loop_factories.rst | 2 + docs/reference/markers/index.rst | 2 +- pytest_asyncio/plugin.py | 44 +++++++---- tests/test_loop_factory_parametrization.py | 79 ++++++++++++++++--- 4 files changed, 100 insertions(+), 27 deletions(-) diff --git a/docs/how-to-guides/run_test_with_specific_loop_factories.rst b/docs/how-to-guides/run_test_with_specific_loop_factories.rst index 338d28ab..5be0333a 100644 --- a/docs/how-to-guides/run_test_with_specific_loop_factories.rst +++ b/docs/how-to-guides/run_test_with_specific_loop_factories.rst @@ -12,3 +12,5 @@ To run a test with only a subset of configured factories, use the ``loop_factori @pytest.mark.asyncio(loop_factories=["custom"]) async def test_only_with_custom_event_loop(): pass + +If a requested factory name is not available from the hook, the test variant for that factory is skipped. diff --git a/docs/reference/markers/index.rst b/docs/reference/markers/index.rst index bc0a584a..80275f53 100644 --- a/docs/reference/markers/index.rst +++ b/docs/reference/markers/index.rst @@ -43,7 +43,7 @@ Subpackages do not share the loop with their parent package. Tests marked with *session* scope share the same event loop, even if the tests exist in different packages. -The ``pytest.mark.asyncio`` marker also accepts a ``loop_factories`` keyword argument to select a subset of configured event loop factories for a test. If ``loop_factories`` contains unknown names, pytest-asyncio raises a ``pytest.UsageError`` during collection. +The ``pytest.mark.asyncio`` marker also accepts a ``loop_factories`` keyword argument to select a subset of configured event loop factories for a test. If ``loop_factories`` contains names not available from the hook, those test variants are skipped. .. |auto mode| replace:: *auto mode* .. _auto mode: ../../concepts.html#auto-mode diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 5b9a239b..18d84181 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -17,6 +17,7 @@ AsyncIterator, Awaitable, Callable, + Collection, Generator, Iterable, Iterator, @@ -746,32 +747,41 @@ def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: ) return + factory_params: Collection[object] + factory_ids: Collection[str] if marker_selected_factory_names is None: - effective_factories = hook_factories + factory_params = hook_factories.values() + factory_ids = hook_factories.keys() else: - missing_factory_names = tuple( - name for name in marker_selected_factory_names if name not in hook_factories - ) - if missing_factory_names: - msg = ( - f"Unknown factory name(s) {missing_factory_names}." - f" Available names: {', '.join(hook_factories)}." + # Iterate in marker order to preserve explicit user selection + # order. + factory_ids = marker_selected_factory_names + factory_params = [ + ( + hook_factories[name] + if name in hook_factories + else pytest.param( + None, + marks=pytest.mark.skip( + reason=( + f"Loop factory {name!r} is not available." + f" Available factories:" + f" {', '.join(hook_factories)}." + ), + ), + ) ) - raise pytest.UsageError(msg) - # Build the mapping in marker order to preserve explicit user - # selection order in parametrization. - effective_factories = { - name: hook_factories[name] for name in marker_selected_factory_names - } + for name in marker_selected_factory_names + ] metafunc.fixturenames.append(_asyncio_loop_factory.__name__) default_loop_scope = _get_default_test_loop_scope(metafunc.config) loop_scope = marker_loop_scope or default_loop_scope # pytest.HIDDEN_PARAM was added in pytest 8.4 - hide_id = len(effective_factories) == 1 and hasattr(pytest, "HIDDEN_PARAM") + hide_id = len(factory_ids) == 1 and hasattr(pytest, "HIDDEN_PARAM") metafunc.parametrize( _asyncio_loop_factory.__name__, - effective_factories.values(), - ids=(pytest.HIDDEN_PARAM,) if hide_id else effective_factories.keys(), + factory_params, + ids=(pytest.HIDDEN_PARAM,) if hide_id else factory_ids, indirect=True, scope=loop_scope, ) diff --git a/tests/test_loop_factory_parametrization.py b/tests/test_loop_factory_parametrization.py index cc71bf20..224c9141 100644 --- a/tests/test_loop_factory_parametrization.py +++ b/tests/test_loop_factory_parametrization.py @@ -400,7 +400,7 @@ async def test_runs_only_with_uvloop(): result.assert_outcomes(passed=1) -def test_asyncio_marker_loop_factories_unknown_name_errors(pytester: Pytester) -> None: +def test_unavailable_factory_skips_with_reason(pytester: Pytester) -> None: pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makeconftest(dedent("""\ import asyncio @@ -414,16 +414,77 @@ def pytest_asyncio_loop_factories(config, item): pytest_plugins = "pytest_asyncio" @pytest.mark.asyncio(loop_factories=["missing"]) - async def test_errors(): + async def test_skipped(): assert True """)) - result = pytester.runpytest("--asyncio-mode=strict") - result.assert_outcomes(errors=1) - result.stdout.fnmatch_lines( - [ - "*Unknown factory name(s)*Available names:*", - ] - ) + result = pytester.runpytest("--asyncio-mode=strict", "-rs") + result.assert_outcomes(skipped=1) + result.stdout.fnmatch_lines(["*SKIPPED*Loop factory 'missing' is not available*"]) + + +def test_partial_intersection_runs_available_and_skips_missing( + pytester: Pytester, +) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + + class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + def pytest_asyncio_loop_factories(config, item): + return { + "available": CustomEventLoop, + "other": asyncio.new_event_loop, + } + """)) + pytester.makepyfile(dedent("""\ + import asyncio + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio(loop_factories=["available", "missing"]) + async def test_runs_with_available(): + assert type(asyncio.get_running_loop()).__name__ == "CustomEventLoop" + """)) + result = pytester.runpytest("--asyncio-mode=strict", "-rs") + result.assert_outcomes(passed=1, skipped=1) + result.stdout.fnmatch_lines(["*SKIPPED*Loop factory 'missing' is not available*"]) + + +def test_platform_conditional_factories(pytester: Pytester) -> None: + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest(dedent("""\ + import asyncio + import sys + + def pytest_asyncio_loop_factories(config, item): + factories = {"default": asyncio.new_event_loop} + if sys.platform == "a_platform_that_does_not_exist": + factories["exotic"] = asyncio.new_event_loop + return factories + """)) + pytester.makepyfile(dedent("""\ + import pytest + + pytest_plugins = "pytest_asyncio" + + @pytest.mark.asyncio(loop_factories=["exotic"]) + async def test_exotic_only(): + assert True + + @pytest.mark.asyncio(loop_factories=["default"]) + async def test_default_only(): + assert True + + @pytest.mark.asyncio(loop_factories=["default", "exotic"]) + async def test_both(): + assert True + """)) + result = pytester.runpytest("--asyncio-mode=strict", "-rs") + result.assert_outcomes(passed=2, skipped=2) + result.stdout.fnmatch_lines(["*SKIPPED*Loop factory 'exotic' is not available*"]) def test_asyncio_marker_loop_factories_without_hook_errors( From ad9ee631f31899d0f79f59cf5f637e45931c44b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 20:01:59 +0000 Subject: [PATCH 123/168] Build(deps): Bump more-itertools from 10.8.0 to 11.0.1 Bumps [more-itertools](https://github.com/more-itertools/more-itertools) from 10.8.0 to 11.0.1. - [Release notes](https://github.com/more-itertools/more-itertools/releases) - [Commits](https://github.com/more-itertools/more-itertools/compare/v10.8.0...v11.0.1) --- updated-dependencies: - dependency-name: more-itertools dependency-version: 11.0.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 811fa26a..89fe918b 100644 --- a/constraints.txt +++ b/constraints.txt @@ -29,7 +29,7 @@ keyring==25.7.0 markdown-it-py==4.0.0 MarkupSafe==3.0.3 mdurl==0.1.2 -more-itertools==10.8.0 +more-itertools==11.0.1 nh3==0.3.4 packaging==26.0 pluggy==1.6.0 From 249bc8d3312466d3357b7c8fbf2a3e63b859dd61 Mon Sep 17 00:00:00 2001 From: dromanov Date: Fri, 10 Apr 2026 12:54:44 +0300 Subject: [PATCH 124/168] ci: add chronographer config --- .github/chronographer.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/chronographer.yml diff --git a/.github/chronographer.yml b/.github/chronographer.yml new file mode 100644 index 00000000..e60e1284 --- /dev/null +++ b/.github/chronographer.yml @@ -0,0 +1,14 @@ +--- +branch-protection-check-name: Timeline protection +action-hints: + inline-markdown: | + This PR is missing news fragments. + + Add a file under `changelog.d/`, like: + `123.changed.rst`. You can use the Towncrier CLI to create it. Make sure to match the writing style with the existing change log content. + + If this change isn't user-facing, ask the maintainers to apply the `skip-changelog` label. Please, include a justification. +enforce-name: + suffix: .rst +labels: + skip-changelog: skip-changelog From f8b1c75916f0025743466474675b1435eda62dc0 Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Fri, 10 Apr 2026 00:09:31 +0100 Subject: [PATCH 125/168] Raise minimum pytest version to v8.4 --- changelog.d/1397.changed.rst | 1 + dependencies/pytest-min/constraints.txt | 2 +- dependencies/pytest-min/requirements.txt | 2 +- pyproject.toml | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 changelog.d/1397.changed.rst diff --git a/changelog.d/1397.changed.rst b/changelog.d/1397.changed.rst new file mode 100644 index 00000000..40cd7f1a --- /dev/null +++ b/changelog.d/1397.changed.rst @@ -0,0 +1 @@ +Updated minimum supported pytest version to v8.4.0. diff --git a/dependencies/pytest-min/constraints.txt b/dependencies/pytest-min/constraints.txt index f01a0eb7..dcc6b925 100644 --- a/dependencies/pytest-min/constraints.txt +++ b/dependencies/pytest-min/constraints.txt @@ -14,7 +14,7 @@ packaging==23.2 pluggy==1.5.0 py==1.11.0 Pygments==2.16.1 -pytest==8.2.0 +pytest==8.4.0 requests==2.31.0 sortedcontainers==2.4.0 tomli==2.0.1 diff --git a/dependencies/pytest-min/requirements.txt b/dependencies/pytest-min/requirements.txt index 918abfd5..c1eaa33c 100644 --- a/dependencies/pytest-min/requirements.txt +++ b/dependencies/pytest-min/requirements.txt @@ -1,3 +1,3 @@ # Always adjust install_requires in setup.cfg and requirements.txt # when changing minimum version dependencies -pytest[testing] == 8.2.0 +pytest[testing] == 8.4.0 diff --git a/pyproject.toml b/pyproject.toml index f52bc823..10637e76 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ dynamic = [ dependencies = [ "backports-asyncio-runner>=1.1,<2; python_version<'3.11'", - "pytest>=8.2,<10", + "pytest>=8.4,<10", "typing-extensions>=4.12; python_version<'3.13'", ] optional-dependencies.docs = [ From 4919983a16b1f65ee34be58980e02c2dc47a3c07 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 15 Apr 2026 15:58:55 +0200 Subject: [PATCH 126/168] ci: Fix issue that cause the step for creating a GitHub release to exit prematurely. --- .github/workflows/main.yml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4bba3e13..f43e7e0b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -197,18 +197,15 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PRERELEASE: ${{ needs.build.outputs.prerelease }} run: | + set +e TAG_NAME="${GITHUB_REF#refs/tags/}" - - gh release view "${TAG_NAME}" >/dev/null - RELEASE_EXISTS=$? - if [ $RELEASE_EXISTS -ne 0 ]; then - gh release create "${TAG_NAME}" \ - --title "pytest-asyncio ${TAG_NAME}" \ - --notes-file release-notes.md \ - --draft \ - $( [ "${PRERELEASE}" = "true" ] && echo "--prerelease" ) \ - dist/* - fi + gh release view "${TAG_NAME}" >/dev/null && echo "Release already exists. Exiting." && exit 0 + gh release create "${TAG_NAME}" \ + --title "pytest-asyncio ${TAG_NAME}" \ + --notes-file release-notes.md \ + --draft \ + $( [ "${PRERELEASE}" = "true" ] && echo "--prerelease" ) \ + dist/* publish-test-pypi: name: Publish packages to test.pypi.org From 78f56e695d3e5353055735eee0a97896004a0ba5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 18:52:34 +0000 Subject: [PATCH 127/168] Build(deps): Bump actions/upload-artifact from 7.0.0 to 7.0.1 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 7.0.0 to 7.0.1. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/bbbca2ddaa5d8feaa63e36b76fdaad77386f024f...043fb46d1a93c77aae656e7c1c64a875d1fc6a0a) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f43e7e0b..44a7e561 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -42,7 +42,7 @@ jobs: id: version run: python ./tools/get-version.py >> $GITHUB_OUTPUT - name: Upload artifacts - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with: name: dist path: dist @@ -93,7 +93,7 @@ jobs: run: python -m tox - name: Store coverage data - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a if: "!endsWith(matrix.os, 'windows')" with: name: coverage-python-${{ matrix.python-version }} @@ -182,7 +182,7 @@ jobs: run: | pandoc --wrap=preserve -o release-notes.md release-notes.rst - name: Upload artifacts - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with: name: release-notes.md path: release-notes.md From 861bcb2f9344eae5575940f7baad6299d157021e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 21:17:56 +0000 Subject: [PATCH 128/168] Build(deps): Bump rich from 14.3.3 to 15.0.0 Bumps [rich](https://github.com/Textualize/rich) from 14.3.3 to 15.0.0. - [Release notes](https://github.com/Textualize/rich/releases) - [Changelog](https://github.com/Textualize/rich/blob/master/CHANGELOG.md) - [Commits](https://github.com/Textualize/rich/compare/v14.3.3...v15.0.0) --- updated-dependencies: - dependency-name: rich dependency-version: 15.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 89fe918b..255bbc60 100644 --- a/constraints.txt +++ b/constraints.txt @@ -42,7 +42,7 @@ readme-renderer==44.0 requests==2.33.1 requests-toolbelt==1.0.0 rfc3986==2.0.0 -rich==14.3.3 +rich==15.0.0 SecretStorage==3.5.0 setuptools==82.0.1 setuptools-scm==10.0.5 From 51b6dfc6ad961fc90d880fd5910cfde3c750284e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 21:18:21 +0000 Subject: [PATCH 129/168] Build(deps): Bump hypothesis from 6.151.11 to 6.151.13 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.151.11 to 6.151.13. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.151.11...hypothesis-python-6.151.13) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.151.13 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 255bbc60..29e91c89 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.13.5 cryptography==46.0.6 docutils==0.21.2 exceptiongroup==1.3.1 -hypothesis==6.151.11 +hypothesis==6.151.13 iniconfig==2.3.0 id==1.6.1 idna==3.11 From e1daf451d3d68100e31930a01b2c5a2747a07ce4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 21:18:27 +0000 Subject: [PATCH 130/168] Build(deps): Bump pytest from 9.0.2 to 9.0.3 Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to 9.0.3. - [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/9.0.2...9.0.3) --- updated-dependencies: - dependency-name: pytest dependency-version: 9.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 29e91c89..7cf4e874 100644 --- a/constraints.txt +++ b/constraints.txt @@ -37,7 +37,7 @@ Pygments==2.20.0 pycparser==3.0 pydantic==2.12.5 pydantic-core==2.41.5 -pytest==9.0.2 +pytest==9.0.3 readme-renderer==44.0 requests==2.33.1 requests-toolbelt==1.0.0 From c23aa2757de892efb90a29ecc5eae8e7a377aef8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 21:17:50 +0000 Subject: [PATCH 131/168] Build(deps): Bump more-itertools from 11.0.1 to 11.0.2 Bumps [more-itertools](https://github.com/more-itertools/more-itertools) from 11.0.1 to 11.0.2. - [Release notes](https://github.com/more-itertools/more-itertools/releases) - [Commits](https://github.com/more-itertools/more-itertools/compare/v11.0.1...v11.0.2) --- updated-dependencies: - dependency-name: more-itertools dependency-version: 11.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 7cf4e874..bd9d8d40 100644 --- a/constraints.txt +++ b/constraints.txt @@ -29,7 +29,7 @@ keyring==25.7.0 markdown-it-py==4.0.0 MarkupSafe==3.0.3 mdurl==0.1.2 -more-itertools==11.0.1 +more-itertools==11.0.2 nh3==0.3.4 packaging==26.0 pluggy==1.6.0 From 0fe529a7b178fd87fbb217fba49b6f29fa69f57c Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 15 Apr 2026 16:59:46 +0200 Subject: [PATCH 132/168] docs: Point to *stable* documentation version, instead of *latest*. --- README.rst | 4 ++-- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index e056a880..9d501a5b 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,7 @@ pytest-asyncio :alt: Matrix chat room: #pytest-asyncio :target: https://matrix.to/#/#pytest-asyncio:matrix.org -`pytest-asyncio `_ is a `pytest `_ plugin. It facilitates testing of code that uses the `asyncio `_ library. +`pytest-asyncio `_ is a `pytest `_ plugin. It facilitates testing of code that uses the `asyncio `_ library. Specifically, pytest-asyncio provides support for coroutines as test functions. This allows users to *await* code inside their tests. For example, the following code is executed as a test item by pytest: @@ -25,7 +25,7 @@ Specifically, pytest-asyncio provides support for coroutines as test functions. res = await library.do_something() assert b"expected result" == res -More details can be found in the `documentation `_. +More details can be found in the `documentation `_. Note that test classes subclassing the standard `unittest `__ library are not supported. Users are advised to use `unittest.IsolatedAsyncioTestCase `__ diff --git a/pyproject.toml b/pyproject.toml index 10637e76..7580def3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ optional-dependencies.testing = [ "hypothesis>=5.7.1", ] urls."Bug Tracker" = "https://github.com/pytest-dev/pytest-asyncio/issues" -urls.Changelog = "https://pytest-asyncio.readthedocs.io/en/latest/reference/changelog.html" +urls.Changelog = "https://pytest-asyncio.readthedocs.io/en/stable/reference/changelog.html" urls.Documentation = "https://pytest-asyncio.readthedocs.io" urls.Homepage = "https://github.com/pytest-dev/pytest-asyncio" urls."Source Code" = "https://github.com/pytest-dev/pytest-asyncio" From 31137d07ec6fe9cb509e3fd0549dcfb32f84c752 Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Sat, 18 Apr 2026 21:39:40 +0100 Subject: [PATCH 133/168] Delete unused code --- ...oop_custom_policies_strict_mode_example.py | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 docs/reference/markers/class_scoped_loop_custom_policies_strict_mode_example.py diff --git a/docs/reference/markers/class_scoped_loop_custom_policies_strict_mode_example.py b/docs/reference/markers/class_scoped_loop_custom_policies_strict_mode_example.py deleted file mode 100644 index 5bb26247..00000000 --- a/docs/reference/markers/class_scoped_loop_custom_policies_strict_mode_example.py +++ /dev/null @@ -1,23 +0,0 @@ -import warnings - -with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - from asyncio import DefaultEventLoopPolicy - -import pytest - - -@pytest.fixture( - params=[ - DefaultEventLoopPolicy(), - DefaultEventLoopPolicy(), - ] -) -def event_loop_policy(request): - return request.param - - -class TestWithDifferentLoopPolicies: - @pytest.mark.asyncio - async def test_parametrized_loop(self): - pass From ee0a6be81f5645e812db4f31559a235d574cefb3 Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Sat, 18 Apr 2026 21:40:27 +0100 Subject: [PATCH 134/168] Deprecate event_loop_policy hook --- changelog.d/1419.deprecated.rst | 1 + docs/how-to-guides/multiple_loops.rst | 4 +++ docs/how-to-guides/uvloop.rst | 2 +- docs/reference/fixtures/index.rst | 5 +++ pytest_asyncio/plugin.py | 13 +++++++ tests/markers/test_class_scope.py | 4 +-- tests/markers/test_function_scope.py | 49 +++++++++++++++++++++++++-- tests/markers/test_module_scope.py | 4 +-- tests/markers/test_package_scope.py | 4 +-- tests/markers/test_session_scope.py | 4 +-- 10 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 changelog.d/1419.deprecated.rst diff --git a/changelog.d/1419.deprecated.rst b/changelog.d/1419.deprecated.rst new file mode 100644 index 00000000..2e4f39e2 --- /dev/null +++ b/changelog.d/1419.deprecated.rst @@ -0,0 +1 @@ +Overriding the *event_loop_policy* fixture is deprecated. Use the ``pytest_asyncio_loop_factories`` hook instead. diff --git a/docs/how-to-guides/multiple_loops.rst b/docs/how-to-guides/multiple_loops.rst index 3453c49f..7ebf0616 100644 --- a/docs/how-to-guides/multiple_loops.rst +++ b/docs/how-to-guides/multiple_loops.rst @@ -2,6 +2,10 @@ How to test with different event loops ====================================== +.. warning:: + + Overriding the *event_loop_policy* fixture is deprecated and will be removed in a future version of pytest-asyncio. Use the ``pytest_asyncio_loop_factories`` hook instead. See :doc:`custom_loop_factory` for details. + Parametrizing the *event_loop_policy* fixture parametrizes all async tests. The following example causes all async tests to run multiple times, once for each event loop in the fixture parameters: .. include:: multiple_loops_example.py diff --git a/docs/how-to-guides/uvloop.rst b/docs/how-to-guides/uvloop.rst index 99afd238..03143be5 100644 --- a/docs/how-to-guides/uvloop.rst +++ b/docs/how-to-guides/uvloop.rst @@ -24,7 +24,7 @@ Using the event_loop_policy fixture .. note:: - ``asyncio.AbstractEventLoopPolicy`` is deprecated as of Python 3.14 (removal planned for 3.16), and ``uvloop.EventLoopPolicy`` will be removed alongside it. Prefer the hook approach above. + ``asyncio.AbstractEventLoopPolicy`` is deprecated as of Python 3.14 (removal planned for 3.16), and ``uvloop.EventLoopPolicy`` will be removed alongside it. Overriding the *event_loop_policy* fixture is also deprecated in pytest-asyncio. Prefer the hook approach above. For older versions of Python and uvloop, you can override the *event_loop_policy* fixture in your *conftest.py:* diff --git a/docs/reference/fixtures/index.rst b/docs/reference/fixtures/index.rst index 3d151dcb..2791a484 100644 --- a/docs/reference/fixtures/index.rst +++ b/docs/reference/fixtures/index.rst @@ -4,6 +4,11 @@ Fixtures event_loop_policy ================= + +.. warning:: + + Overriding the *event_loop_policy* fixture is deprecated and will be removed in a future version of pytest-asyncio. Use the ``pytest_asyncio_loop_factories`` hook instead. See :doc:`../hooks` for details. + Returns the event loop policy used to create asyncio event loops. The default return value is *asyncio.get_event_loop_policy().* diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 18d84181..3810f775 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -919,6 +919,13 @@ def inner(*args, **kwargs): @pytest.hookimpl(wrapper=True) def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: + if ( + fixturedef.argname == "event_loop_policy" + and fixturedef.func.__module__ != __name__ + ): + warnings.warn( + PytestDeprecationWarning(_EVENT_LOOP_POLICY_FIXTURE_DEPRECATION_WARNING), + ) asyncio_mode = _get_asyncio_mode(request.config) if not _is_asyncio_fixture_function(fixturedef.func): if asyncio_mode == Mode.STRICT: @@ -962,6 +969,12 @@ def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: mark.asyncio 'loop_factories' must be a non-empty sequence of strings. """ +_EVENT_LOOP_POLICY_FIXTURE_DEPRECATION_WARNING = """\ +Overriding the "event_loop_policy" fixture is deprecated \ +and will be removed in a future version of pytest-asyncio. \ +Use the "pytest_asyncio_loop_factories" hook to customize event loop creation.\ +""" + def _parse_asyncio_marker( asyncio_marker: Mark, diff --git a/tests/markers/test_class_scope.py b/tests/markers/test_class_scope.py index 5394c2e9..28f7187c 100644 --- a/tests/markers/test_class_scope.py +++ b/tests/markers/test_class_scope.py @@ -133,7 +133,7 @@ async def test_does_not_use_custom_event_loop_policy(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=3) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) @@ -168,7 +168,7 @@ async def test_parametrized_loop(self, request): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=2) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) diff --git a/tests/markers/test_function_scope.py b/tests/markers/test_function_scope.py index 5005a69a..180d5c71 100644 --- a/tests/markers/test_function_scope.py +++ b/tests/markers/test_function_scope.py @@ -107,7 +107,7 @@ async def test_uses_custom_event_loop_policy(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=1, warnings=2) + result.assert_outcomes(passed=1, warnings=3) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=1) @@ -148,12 +148,57 @@ async def test_parametrized_loop(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=3) + result.assert_outcomes(passed=2, warnings=5) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) +def test_event_loop_policy_fixture_override_emits_deprecation_warning( + pytester: Pytester, +): + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makepyfile( + dedent("""\ + import asyncio + import pytest + + pytestmark = pytest.mark.asyncio + + @pytest.fixture + def event_loop_policy(): + return asyncio.DefaultEventLoopPolicy() + + async def test_anything(): + pass + """), + ) + result = pytester.runpytest("--asyncio-mode=strict", "-W", "default") + result.assert_outcomes(passed=1) + result.stdout.fnmatch_lines( + "*PytestDeprecationWarning*event_loop_policy*deprecated*" + ) + + +def test_default_event_loop_policy_fixture_does_not_warn( + pytester: Pytester, +): + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makepyfile( + dedent("""\ + import pytest + + pytestmark = pytest.mark.asyncio + + async def test_anything(): + pass + """), + ) + result = pytester.runpytest("--asyncio-mode=strict", "-W", "default") + result.assert_outcomes(passed=1) + result.stdout.no_fnmatch_line("*PytestDeprecationWarning*event_loop_policy*") + + def test_asyncio_mark_provides_function_scoped_loop_to_fixtures( pytester: Pytester, ): diff --git a/tests/markers/test_module_scope.py b/tests/markers/test_module_scope.py index 3f7ee3e9..a2662812 100644 --- a/tests/markers/test_module_scope.py +++ b/tests/markers/test_module_scope.py @@ -83,7 +83,7 @@ async def test_does_not_use_custom_event_loop_policy(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=3) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) @@ -118,7 +118,7 @@ async def test_parametrized_loop(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=2) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) diff --git a/tests/markers/test_package_scope.py b/tests/markers/test_package_scope.py index 0783eb9c..ff6aba4a 100644 --- a/tests/markers/test_package_scope.py +++ b/tests/markers/test_package_scope.py @@ -115,7 +115,7 @@ async def test_also_uses_custom_event_loop_policy(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=3) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) @@ -153,7 +153,7 @@ async def test_parametrized_loop(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=2) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) diff --git a/tests/markers/test_session_scope.py b/tests/markers/test_session_scope.py index 66ea5ed8..24566c5e 100644 --- a/tests/markers/test_session_scope.py +++ b/tests/markers/test_session_scope.py @@ -116,7 +116,7 @@ async def test_also_uses_custom_event_loop_policy(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=3) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) @@ -154,7 +154,7 @@ async def test_parametrized_loop(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=2) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) From a8758f5fc0b965d050ae00c8558b9bd26ca1bf64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 21:52:49 +0000 Subject: [PATCH 135/168] Build(deps): Bump cryptography from 46.0.6 to 46.0.7 Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.6 to 46.0.7. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/46.0.6...46.0.7) --- updated-dependencies: - dependency-name: cryptography dependency-version: 46.0.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index bd9d8d40..777d8a39 100644 --- a/constraints.txt +++ b/constraints.txt @@ -10,7 +10,7 @@ check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.2 coverage==7.13.5 -cryptography==46.0.6 +cryptography==46.0.7 docutils==0.21.2 exceptiongroup==1.3.1 hypothesis==6.151.13 From af62924bd1ae2840be1466a349331fdc584b924a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 21:54:15 +0000 Subject: [PATCH 136/168] Build(deps): Bump pydantic from 2.12.5 to 2.13.0 Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.12.5 to 2.13.0. - [Release notes](https://github.com/pydantic/pydantic/releases) - [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md) - [Commits](https://github.com/pydantic/pydantic/compare/v2.12.5...v2.13.0) --- updated-dependencies: - dependency-name: pydantic dependency-version: 2.13.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Build(deps): Bump pydantic-core from 2.41.5 to 2.46.0 Bumps [pydantic-core](https://github.com/pydantic/pydantic) from 2.41.5 to 2.46.0. - [Release notes](https://github.com/pydantic/pydantic/releases) - [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md) - [Commits](https://github.com/pydantic/pydantic/commits) --- updated-dependencies: - dependency-name: pydantic-core dependency-version: 2.46.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/constraints.txt b/constraints.txt index 777d8a39..9157271f 100644 --- a/constraints.txt +++ b/constraints.txt @@ -35,8 +35,8 @@ packaging==26.0 pluggy==1.6.0 Pygments==2.20.0 pycparser==3.0 -pydantic==2.12.5 -pydantic-core==2.41.5 +pydantic==2.13.0 +pydantic-core==2.46.0 pytest==9.0.3 readme-renderer==44.0 requests==2.33.1 From 31772d220191ec3c347cd7f8ea8298c5d40c5041 Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Thu, 30 Apr 2026 01:21:13 +0100 Subject: [PATCH 137/168] Fix unclosed event loop --- changelog.d/724.fixed.rst | 1 + pytest_asyncio/plugin.py | 18 ++++-------------- tests/test_set_event_loop.py | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 changelog.d/724.fixed.rst diff --git a/changelog.d/724.fixed.rst b/changelog.d/724.fixed.rst new file mode 100644 index 00000000..6e68478a --- /dev/null +++ b/changelog.d/724.fixed.rst @@ -0,0 +1 @@ +Fixed a ``ResourceWarning: unclosed event loop`` warning that could occur when a synchronous test called ``asyncio.run()`` or otherwise unset the current event loop after pytest-asyncio had run an async test or fixture. diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 3810f775..2fe8db12 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -806,23 +806,13 @@ def _temporary_event_loop(loop: AbstractEventLoop) -> Iterator[None]: @contextlib.contextmanager def _temporary_event_loop_policy( policy: AbstractEventLoopPolicy, - *, - has_custom_factory: bool, ) -> Iterator[None]: old_loop_policy = _get_event_loop_policy() - if has_custom_factory: - old_loop = None - else: - try: - old_loop = _get_event_loop_no_warn() - except RuntimeError: - old_loop = None _set_event_loop_policy(policy) try: yield finally: _set_event_loop_policy(old_loop_policy) - _set_event_loop(old_loop) def _get_event_loop_policy() -> AbstractEventLoopPolicy: @@ -1042,10 +1032,7 @@ def _scoped_runner( ) -> Iterator[Runner]: new_loop_policy = event_loop_policy debug_mode = _get_asyncio_debug(request.config) - with _temporary_event_loop_policy( - new_loop_policy, - has_custom_factory=_asyncio_loop_factory is not None, - ): + with _temporary_event_loop_policy(new_loop_policy): runner = Runner( debug=debug_mode, loop_factory=_asyncio_loop_factory, @@ -1068,6 +1055,9 @@ def _scoped_runner( _RUNNER_TEARDOWN_WARNING % traceback.format_exc(), RuntimeWarning, ) + finally: + if _asyncio_loop_factory is not None: + _set_event_loop(None) return _scoped_runner diff --git a/tests/test_set_event_loop.py b/tests/test_set_event_loop.py index 3854c04b..52dfc7af 100644 --- a/tests/test_set_event_loop.py +++ b/tests/test_set_event_loop.py @@ -99,6 +99,40 @@ async def test_after(self): result.assert_outcomes(passed=3) +def test_asyncio_run_after_async_fixture_does_not_leak_loop( + pytester: Pytester, +): + pytester.makeini(dedent("""\ + [pytest] + asyncio_default_fixture_loop_scope = function + """)) + pytester.makepyfile(dedent("""\ + import asyncio + import gc + import pytest + import pytest_asyncio + + pytest_plugins = "pytest_asyncio" + + @pytest_asyncio.fixture + async def async_fixture(): + yield + + @pytest.mark.asyncio + async def test_async_function_uses_async_fixture(async_fixture): + pass + + def test_collect_unclosed_loops(): + async def amain(): + pass + + asyncio.run(amain()) + gc.collect() + """)) + result = pytester.runpytest_subprocess("-W", "error") + result.assert_outcomes(passed=2) + + @pytest.mark.parametrize("test_loop_scope", ("module", "package", "session")) @pytest.mark.parametrize( "loop_breaking_action", From 61cde33fb356c5c2f95b34f31c6812b835b8b6af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 01:51:07 +0000 Subject: [PATCH 138/168] Build(deps): Bump nh3 from 0.3.4 to 0.3.5 Bumps [nh3](https://github.com/messense/nh3) from 0.3.4 to 0.3.5. - [Release notes](https://github.com/messense/nh3/releases) - [Commits](https://github.com/messense/nh3/compare/v0.3.4...v0.3.5) --- updated-dependencies: - dependency-name: nh3 dependency-version: 0.3.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 9157271f..7335592f 100644 --- a/constraints.txt +++ b/constraints.txt @@ -30,7 +30,7 @@ markdown-it-py==4.0.0 MarkupSafe==3.0.3 mdurl==0.1.2 more-itertools==11.0.2 -nh3==0.3.4 +nh3==0.3.5 packaging==26.0 pluggy==1.6.0 Pygments==2.20.0 From 46450ab044266a5073b30aac162e24446bfde92a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 08:46:03 +0000 Subject: [PATCH 139/168] Build(deps): Bump packaging from 26.0 to 26.2 Bumps [packaging](https://github.com/pypa/packaging) from 26.0 to 26.2. - [Release notes](https://github.com/pypa/packaging/releases) - [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pypa/packaging/compare/26.0...26.2) --- updated-dependencies: - dependency-name: packaging dependency-version: '26.2' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 7335592f..766eff1c 100644 --- a/constraints.txt +++ b/constraints.txt @@ -31,7 +31,7 @@ MarkupSafe==3.0.3 mdurl==0.1.2 more-itertools==11.0.2 nh3==0.3.5 -packaging==26.0 +packaging==26.2 pluggy==1.6.0 Pygments==2.20.0 pycparser==3.0 From fce75dedeb168147082c0e50cf0e3fd40c998021 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 01:51:12 +0000 Subject: [PATCH 140/168] Build(deps): Bump click from 8.3.2 to 8.3.3 Bumps [click](https://github.com/pallets/click) from 8.3.2 to 8.3.3. - [Release notes](https://github.com/pallets/click/releases) - [Changelog](https://github.com/pallets/click/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/click/compare/8.3.2...8.3.3) --- updated-dependencies: - dependency-name: click dependency-version: 8.3.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 766eff1c..4208e36b 100644 --- a/constraints.txt +++ b/constraints.txt @@ -8,7 +8,7 @@ certifi==2026.2.25 charset-normalizer==3.4.7 check-wheel-contents==0.6.3 cffi==2.0.0 -click==8.3.2 +click==8.3.3 coverage==7.13.5 cryptography==46.0.7 docutils==0.21.2 From 4df3efae639a95688fc06602198c6a175b09de15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 09:04:17 +0000 Subject: [PATCH 141/168] Build(deps): Bump certifi from 2026.2.25 to 2026.4.22 Bumps [certifi](https://github.com/certifi/python-certifi) from 2026.2.25 to 2026.4.22. - [Commits](https://github.com/certifi/python-certifi/compare/2026.02.25...2026.04.22) --- updated-dependencies: - dependency-name: certifi dependency-version: 2026.4.22 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 4208e36b..ddd20399 100644 --- a/constraints.txt +++ b/constraints.txt @@ -4,7 +4,7 @@ attrs==26.1.0 babel==2.18.0 backports.asyncio.runner==1.2.0 backports.tarfile==1.2.0 -certifi==2026.2.25 +certifi==2026.4.22 charset-normalizer==3.4.7 check-wheel-contents==0.6.3 cffi==2.0.0 From 758277424a3a905176f4c5ff31a8519ae93b2141 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 09:04:00 +0000 Subject: [PATCH 142/168] Build(deps): Bump cryptography from 46.0.7 to 48.0.0 Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.7 to 48.0.0. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/46.0.7...48.0.0) --- updated-dependencies: - dependency-name: cryptography dependency-version: 48.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index ddd20399..81e32961 100644 --- a/constraints.txt +++ b/constraints.txt @@ -10,7 +10,7 @@ check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.3 coverage==7.13.5 -cryptography==46.0.7 +cryptography==48.0.0 docutils==0.21.2 exceptiongroup==1.3.1 hypothesis==6.151.13 From f1c23b92eddaf1062cb80ee518297e6832959372 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 09:34:51 +0000 Subject: [PATCH 143/168] Build(deps): Bump hypothesis from 6.151.13 to 6.152.4 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.151.13 to 6.152.4. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.151.13...hypothesis-python-6.152.4) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.152.4 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 81e32961..753197bd 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.13.5 cryptography==48.0.0 docutils==0.21.2 exceptiongroup==1.3.1 -hypothesis==6.151.13 +hypothesis==6.152.4 iniconfig==2.3.0 id==1.6.1 idna==3.11 From 82325198f6f0def916c522c70e0cdcca6d2a03c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 01:51:24 +0000 Subject: [PATCH 144/168] Build(deps): Bump zipp from 3.23.0 to 3.23.1 Bumps [zipp](https://github.com/jaraco/zipp) from 3.23.0 to 3.23.1. - [Release notes](https://github.com/jaraco/zipp/releases) - [Changelog](https://github.com/jaraco/zipp/blob/main/NEWS.rst) - [Commits](https://github.com/jaraco/zipp/compare/v3.23.0...v3.23.1) --- updated-dependencies: - dependency-name: zipp dependency-version: 3.23.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 753197bd..5764b92b 100644 --- a/constraints.txt +++ b/constraints.txt @@ -64,4 +64,4 @@ typing_extensions==4.15.0 typing-inspection==0.4.2 urllib3==2.6.3 wheel-filename==1.4.2 -zipp==3.23.0 +zipp==3.23.1 From 74a5557bcb6ba971523fb56c8427660cdc3b36c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 01:51:20 +0000 Subject: [PATCH 145/168] Build(deps): Bump idna from 3.11 to 3.13 Bumps [idna](https://github.com/kjd/idna) from 3.11 to 3.13. - [Release notes](https://github.com/kjd/idna/releases) - [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst) - [Commits](https://github.com/kjd/idna/compare/v3.11...v3.13) --- updated-dependencies: - dependency-name: idna dependency-version: '3.13' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 5764b92b..84f8182b 100644 --- a/constraints.txt +++ b/constraints.txt @@ -16,7 +16,7 @@ exceptiongroup==1.3.1 hypothesis==6.152.4 iniconfig==2.3.0 id==1.6.1 -idna==3.11 +idna==3.13 imagesize==2.0.0 importlib_metadata==9.0.0 iniconfig==2.3.0 From fae1e67818d990787248c7d0b9cb1a7f0bd723a4 Mon Sep 17 00:00:00 2001 From: Herrtian <70463940+Herrtian@users.noreply.github.com> Date: Fri, 1 May 2026 17:19:41 +0200 Subject: [PATCH 146/168] Add pytest main CI smoke test --- .github/workflows/main.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 44a7e561..506e474f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -100,6 +100,28 @@ jobs: path: coverage/coverage.* if-no-files-found: error + test-pytest-dev: + name: ubuntu - Python ${{ env.PYTHON_LATEST }} - pytest main + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + persist-credentials: false + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 + with: + python-version: ${{ env.PYTHON_LATEST }} + - name: Install dependencies + run: | + python -VV + python -m site + python -m pip install --upgrade pip + python -m pip install --upgrade \ + ".[testing]" \ + "pytest @ git+https://github.com/pytest-dev/pytest.git" + - name: Run tests against pytest main + run: make test + lint-github-actions: name: Lint GitHub Actions permissions: From 81e6dba36db3089f6c02bf19eff87e7eddf32024 Mon Sep 17 00:00:00 2001 From: Herrtian <70463940+Herrtian@users.noreply.github.com> Date: Sat, 2 May 2026 17:21:07 +0200 Subject: [PATCH 147/168] Add local pytest dev tox env --- .github/workflows/main.yml | 8 +++----- tox.ini | 6 ++++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 506e474f..d0704b05 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -101,7 +101,7 @@ jobs: if-no-files-found: error test-pytest-dev: - name: ubuntu - Python ${{ env.PYTHON_LATEST }} - pytest main + name: ubuntu - Python latest - pytest main runs-on: ubuntu-latest continue-on-error: true steps: @@ -116,11 +116,9 @@ jobs: python -VV python -m site python -m pip install --upgrade pip - python -m pip install --upgrade \ - ".[testing]" \ - "pytest @ git+https://github.com/pytest-dev/pytest.git" + python -m pip install --upgrade tox - name: Run tests against pytest main - run: make test + run: python -m tox run -e pytest-dev lint-github-actions: name: Lint GitHub Actions diff --git a/tox.ini b/tox.ini index c37ee977..099c4edb 100644 --- a/tox.ini +++ b/tox.ini @@ -40,6 +40,12 @@ commands = make test allowlist_externals = make +[testenv:pytest-dev] +description = Run tests against pytest main +constraints = +deps = + pytest @ git+https://github.com/pytest-dev/pytest.git + [testenv:docs] allowlist_externals = git From a06249a26fe3a994e9f661ae23a4a96f1cbee201 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 13 May 2026 19:16:19 +0200 Subject: [PATCH 148/168] test: Prepare tests for unhandled async fixture errors in pytest >=9.1. --- tests/modes/test_strict_mode.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/modes/test_strict_mode.py b/tests/modes/test_strict_mode.py index be4d8d99..86cc34ad 100644 --- a/tests/modes/test_strict_mode.py +++ b/tests/modes/test_strict_mode.py @@ -2,6 +2,7 @@ from textwrap import dedent +import pytest from pytest import Pytester, version_tuple as pytest_version @@ -82,6 +83,10 @@ async def test_anything(): result.stdout.fnmatch_lines(["*async def functions are not natively supported*"]) +@pytest.mark.skipif( + pytest_version >= (9, 1, 0), + reason="pytest >=9.1 converts unhandled async fixtures to errors", +) def test_strict_mode_ignores_unmarked_fixture(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile(dedent("""\ @@ -109,6 +114,10 @@ async def test_anything(any_fixture): ) +@pytest.mark.skipif( + pytest_version >= (9, 1, 0), + reason="pytest >=9.1 converts unhandled async fixtures to errors", +) def test_strict_mode_marked_test_unmarked_fixture_warning(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile(dedent("""\ @@ -152,6 +161,10 @@ async def test_anything(any_fixture): # autouse is not handled in any special way currently +@pytest.mark.skipif( + pytest_version >= (9, 1, 0), + reason="pytest >=9.1 converts unhandled async fixtures to errors", +) def test_strict_mode_marked_test_unmarked_autouse_fixture_warning(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile(dedent("""\ From b7311834ec5eef323208929185c94e57acaed923 Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Mon, 4 May 2026 11:28:24 +0200 Subject: [PATCH 149/168] Document selecting loop factories per test Split guide Tweak wording --- .../configure_loop_factories_per_test.rst | 29 +++++++++++++++++++ .../conftest.py | 18 ++++++++++++ .../test_extra_loop_factories.py | 11 +++++++ docs/how-to-guides/custom_loop_factory.rst | 6 ++-- docs/how-to-guides/index.rst | 1 + .../run_test_with_specific_loop_factories.rst | 19 ++++++++---- .../conftest.py | 12 ++++++++ .../test_loop_factories_subset.py | 11 +++++++ 8 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 docs/how-to-guides/configure_loop_factories_per_test.rst create mode 100644 docs/how-to-guides/configure_loop_factories_per_test/conftest.py create mode 100644 docs/how-to-guides/configure_loop_factories_per_test/test_extra_loop_factories.py create mode 100644 docs/how-to-guides/run_test_with_specific_loop_factories/conftest.py create mode 100644 docs/how-to-guides/run_test_with_specific_loop_factories/test_loop_factories_subset.py diff --git a/docs/how-to-guides/configure_loop_factories_per_test.rst b/docs/how-to-guides/configure_loop_factories_per_test.rst new file mode 100644 index 00000000..f7f6d05c --- /dev/null +++ b/docs/how-to-guides/configure_loop_factories_per_test.rst @@ -0,0 +1,29 @@ +======================================================== +How to configure event loop factories from the test item +======================================================== + +``pytest_asyncio_loop_factories`` is called with the current pytest ``item``. +Use that item to decide which named event loop factories are available for the test being collected. + +For example, a hook can inspect the test's fixtures and return a different factory mapping for tests that request a particular fixture. +In ``conftest.py``, check the current item's fixture names and build the factory mapping for that item: + +.. include:: configure_loop_factories_per_test/conftest.py + :code: python + +Then request the fixture from tests that should use the custom factory: + +.. include:: configure_loop_factories_per_test/test_extra_loop_factories.py + :code: python + +In this example, ``test_runs_with_default_factory_only`` is parametrized only over ``default``, while ``test_runs_with_custom_factory_only`` is parametrized only over ``custom``. + +The same pattern works with any information available from the current pytest item, such as fixture names, markers, node IDs, or file paths. + +Because this is a standard pytest hook, its placement also matters. +An implementation in a nested ``conftest.py`` applies to tests collected under that directory. +Use this when a whole package or directory should share the same factory set. + +For declaring factories without item-specific logic, see :doc:`custom_loop_factory`. + +For selecting a subset of available factories from a test, see :doc:`run_test_with_specific_loop_factories`. diff --git a/docs/how-to-guides/configure_loop_factories_per_test/conftest.py b/docs/how-to-guides/configure_loop_factories_per_test/conftest.py new file mode 100644 index 00000000..ba2af819 --- /dev/null +++ b/docs/how-to-guides/configure_loop_factories_per_test/conftest.py @@ -0,0 +1,18 @@ +import asyncio + +import pytest + + +class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + +@pytest.fixture +def requires_custom_loop(): + pass + + +def pytest_asyncio_loop_factories(config, item): + if "requires_custom_loop" in item.fixturenames: + return {"custom": CustomEventLoop} + return {"default": asyncio.new_event_loop} diff --git a/docs/how-to-guides/configure_loop_factories_per_test/test_extra_loop_factories.py b/docs/how-to-guides/configure_loop_factories_per_test/test_extra_loop_factories.py new file mode 100644 index 00000000..55eafe67 --- /dev/null +++ b/docs/how-to-guides/configure_loop_factories_per_test/test_extra_loop_factories.py @@ -0,0 +1,11 @@ +import pytest + + +@pytest.mark.asyncio +async def test_runs_with_default_factory_only(): + pass + + +@pytest.mark.asyncio +async def test_runs_with_custom_factory_only(requires_custom_loop): + pass diff --git a/docs/how-to-guides/custom_loop_factory.rst b/docs/how-to-guides/custom_loop_factory.rst index 6e176d95..fb4d9f9c 100644 --- a/docs/how-to-guides/custom_loop_factory.rst +++ b/docs/how-to-guides/custom_loop_factory.rst @@ -2,7 +2,7 @@ How to use custom event loop factories for tests ================================================ -pytest-asyncio can run asynchronous tests with custom event loop factories by implementing ``pytest_asyncio_loop_factories`` in ``conftest.py``. The hook returns a mapping from factory names to loop factory callables: +pytest-asyncio can run asynchronous tests with custom event loop factories by implementing ``pytest_asyncio_loop_factories`` in ``conftest.py``. The hook provides the named event loop factories that are available for a test item by returning a mapping from factory names to loop factory callables: .. code-block:: python @@ -21,6 +21,6 @@ pytest-asyncio can run asynchronous tests with custom event loop factories by im "custom": CustomEventLoop, } -See :doc:`run_test_with_specific_loop_factories` for running tests with only a subset of configured factories. +The hook receives the current pytest ``item``, so it can return different factory mappings for different tests. See :doc:`configure_loop_factories_per_test` for item-based factory configuration. -See :doc:`../reference/hooks` and :doc:`../reference/markers/index` for the hook and marker reference. +To run a test with only some configured factories, see :doc:`run_test_with_specific_loop_factories`. diff --git a/docs/how-to-guides/index.rst b/docs/how-to-guides/index.rst index acf0d177..b67a4125 100644 --- a/docs/how-to-guides/index.rst +++ b/docs/how-to-guides/index.rst @@ -11,6 +11,7 @@ How-To Guides change_default_fixture_loop change_default_test_loop custom_loop_factory + configure_loop_factories_per_test run_test_with_specific_loop_factories run_class_tests_in_same_loop run_module_tests_in_same_loop diff --git a/docs/how-to-guides/run_test_with_specific_loop_factories.rst b/docs/how-to-guides/run_test_with_specific_loop_factories.rst index 5be0333a..1a322fed 100644 --- a/docs/how-to-guides/run_test_with_specific_loop_factories.rst +++ b/docs/how-to-guides/run_test_with_specific_loop_factories.rst @@ -2,15 +2,22 @@ How to run a test with specific event loop factories only ========================================================= -To run a test with only a subset of configured factories, use the ``loop_factories`` argument of ``pytest.mark.asyncio``: +``pytest_asyncio_loop_factories`` determines which named event loop factories are available for each test item. +By default, pytest-asyncio parametrizes a test with every factory returned for that item. +Use ``loop_factories`` to select a subset of the factory names returned by the hook. -.. code-block:: python +Assume ``conftest.py`` provides two named factories: - import pytest +.. include:: run_test_with_specific_loop_factories/conftest.py + :code: python +Then use ``loop_factories`` to select which available factory names a test should run with: - @pytest.mark.asyncio(loop_factories=["custom"]) - async def test_only_with_custom_event_loop(): - pass +.. include:: run_test_with_specific_loop_factories/test_loop_factories_subset.py + :code: python If a requested factory name is not available from the hook, the test variant for that factory is skipped. + +For declaring the factories themselves, see :doc:`custom_loop_factory`. + +For choosing the available factories from the pytest item, see :doc:`configure_loop_factories_per_test`. diff --git a/docs/how-to-guides/run_test_with_specific_loop_factories/conftest.py b/docs/how-to-guides/run_test_with_specific_loop_factories/conftest.py new file mode 100644 index 00000000..631ca247 --- /dev/null +++ b/docs/how-to-guides/run_test_with_specific_loop_factories/conftest.py @@ -0,0 +1,12 @@ +import asyncio + + +class CustomEventLoop(asyncio.SelectorEventLoop): + pass + + +def pytest_asyncio_loop_factories(config, item): + return { + "default": asyncio.new_event_loop, + "custom": CustomEventLoop, + } diff --git a/docs/how-to-guides/run_test_with_specific_loop_factories/test_loop_factories_subset.py b/docs/how-to-guides/run_test_with_specific_loop_factories/test_loop_factories_subset.py new file mode 100644 index 00000000..c8eaf4c6 --- /dev/null +++ b/docs/how-to-guides/run_test_with_specific_loop_factories/test_loop_factories_subset.py @@ -0,0 +1,11 @@ +import pytest + + +@pytest.mark.asyncio +async def test_runs_with_every_configured_factory(): + pass + + +@pytest.mark.asyncio(loop_factories=["custom"]) +async def test_runs_with_only_custom_factory(): + pass From 589fe4c3e79ff5e3f14293eaee779592f4467d3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 01:50:31 +0000 Subject: [PATCH 150/168] Build(deps): Bump pydantic from 2.13.0 to 2.13.3 Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.13.0 to 2.13.3. - [Release notes](https://github.com/pydantic/pydantic/releases) - [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md) - [Commits](https://github.com/pydantic/pydantic/compare/v2.13.0...v2.13.3) --- updated-dependencies: - dependency-name: pydantic dependency-version: 2.13.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Build(deps): Bump pydantic-core from 2.46.0 to 2.46.3 Bumps [pydantic-core](https://github.com/pydantic/pydantic) from 2.46.0 to 2.46.3. - [Release notes](https://github.com/pydantic/pydantic/releases) - [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md) - [Commits](https://github.com/pydantic/pydantic/compare/core-v2.46.0...core-v2.46.3) --- updated-dependencies: - dependency-name: pydantic-core dependency-version: 2.46.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/constraints.txt b/constraints.txt index 84f8182b..38c95f9d 100644 --- a/constraints.txt +++ b/constraints.txt @@ -35,8 +35,8 @@ packaging==26.2 pluggy==1.6.0 Pygments==2.20.0 pycparser==3.0 -pydantic==2.13.0 -pydantic-core==2.46.0 +pydantic==2.13.3 +pydantic-core==2.46.3 pytest==9.0.3 readme-renderer==44.0 requests==2.33.1 From 725f4430f10b50279196fa06b7789ec61a1c195f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 04:11:24 +0000 Subject: [PATCH 151/168] Build(deps): Bump idna from 3.13 to 3.14 Bumps [idna](https://github.com/kjd/idna) from 3.13 to 3.14. - [Release notes](https://github.com/kjd/idna/releases) - [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.md) - [Commits](https://github.com/kjd/idna/compare/v3.13...v3.14) --- updated-dependencies: - dependency-name: idna dependency-version: '3.14' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 38c95f9d..854dc4b1 100644 --- a/constraints.txt +++ b/constraints.txt @@ -16,7 +16,7 @@ exceptiongroup==1.3.1 hypothesis==6.152.4 iniconfig==2.3.0 id==1.6.1 -idna==3.13 +idna==3.14 imagesize==2.0.0 importlib_metadata==9.0.0 iniconfig==2.3.0 From 9a7332838405e8cef1d195d4c314f3c83785ac44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 04:11:09 +0000 Subject: [PATCH 152/168] Build(deps): Bump requests from 2.33.1 to 2.34.0 Bumps [requests](https://github.com/psf/requests) from 2.33.1 to 2.34.0. - [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.33.1...v2.34.0) --- updated-dependencies: - dependency-name: requests dependency-version: 2.34.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 854dc4b1..ff1c2539 100644 --- a/constraints.txt +++ b/constraints.txt @@ -39,7 +39,7 @@ pydantic==2.13.3 pydantic-core==2.46.3 pytest==9.0.3 readme-renderer==44.0 -requests==2.33.1 +requests==2.34.0 requests-toolbelt==1.0.0 rfc3986==2.0.0 rich==15.0.0 From ce6610bdb8a1b19946748b9220f08a686469b21a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 04:11:14 +0000 Subject: [PATCH 153/168] Build(deps): Bump urllib3 from 2.6.3 to 2.7.0 Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.3 to 2.7.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.6.3...2.7.0) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index ff1c2539..e1d02bbe 100644 --- a/constraints.txt +++ b/constraints.txt @@ -62,6 +62,6 @@ tomli==2.4.1 twine==6.2.0 typing_extensions==4.15.0 typing-inspection==0.4.2 -urllib3==2.6.3 +urllib3==2.7.0 wheel-filename==1.4.2 zipp==3.23.1 From 99a4a9832e2d72c6587f7159127c36dc6c853a04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 04:11:03 +0000 Subject: [PATCH 154/168] Build(deps): Bump markdown-it-py from 4.0.0 to 4.2.0 Bumps [markdown-it-py](https://github.com/executablebooks/markdown-it-py) from 4.0.0 to 4.2.0. - [Release notes](https://github.com/executablebooks/markdown-it-py/releases) - [Changelog](https://github.com/executablebooks/markdown-it-py/blob/master/CHANGELOG.md) - [Commits](https://github.com/executablebooks/markdown-it-py/compare/v4.0.0...v4.2.0) --- updated-dependencies: - dependency-name: markdown-it-py dependency-version: 4.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index e1d02bbe..ab44fdb1 100644 --- a/constraints.txt +++ b/constraints.txt @@ -26,7 +26,7 @@ jaraco.functools==4.4.0 jeepney==0.9.0 Jinja2==3.1.6 keyring==25.7.0 -markdown-it-py==4.0.0 +markdown-it-py==4.2.0 MarkupSafe==3.0.3 mdurl==0.1.2 more-itertools==11.0.2 From 8c4da88f6f05aa723cabd367a04901f6c6ab28fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 04:10:42 +0000 Subject: [PATCH 155/168] Build(deps): Bump coverage from 7.13.5 to 7.14.0 Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.13.5 to 7.14.0. - [Release notes](https://github.com/coveragepy/coveragepy/releases) - [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst) - [Commits](https://github.com/coveragepy/coveragepy/compare/7.13.5...7.14.0) --- updated-dependencies: - dependency-name: coverage dependency-version: 7.14.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index ab44fdb1..06faf565 100644 --- a/constraints.txt +++ b/constraints.txt @@ -9,7 +9,7 @@ charset-normalizer==3.4.7 check-wheel-contents==0.6.3 cffi==2.0.0 click==8.3.3 -coverage==7.13.5 +coverage==7.14.0 cryptography==48.0.0 docutils==0.21.2 exceptiongroup==1.3.1 From 8a2f148313175a2ccca6725abad75cbe199922e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 05:50:39 +0000 Subject: [PATCH 156/168] Build(deps): Bump hypothesis from 6.152.4 to 6.152.6 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.152.4 to 6.152.6. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.152.4...hypothesis-python-6.152.6) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.152.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 06faf565..66b72241 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.14.0 cryptography==48.0.0 docutils==0.21.2 exceptiongroup==1.3.1 -hypothesis==6.152.4 +hypothesis==6.152.6 iniconfig==2.3.0 id==1.6.1 idna==3.14 From 2e8b88f1ad20da4568827b4bbf15ead0b9087321 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 15 May 2026 10:37:45 +0200 Subject: [PATCH 157/168] ci: Publish the GitHub release after uploading the release artifacts to PyPI. --- .github/workflows/main.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d0704b05..2864329c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -252,6 +252,7 @@ jobs: needs: [build, lint, check, create-github-release] runs-on: ubuntu-latest permissions: + contents: write id-token: write steps: - name: Download distributions @@ -264,3 +265,10 @@ jobs: tree dist - name: PyPI upload uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 + - name: Publish GitHub Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set +e + TAG_NAME="${GITHUB_REF#refs/tags/}" + gh release edit "${TAG_NAME}" --draft=false From 5046ba4ee678684bdbe98f0278d9650dae96d2ec Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 15 May 2026 10:38:47 +0200 Subject: [PATCH 158/168] ci: Remove redundant job dependencies. --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2864329c..6a02b2ee 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -165,7 +165,7 @@ jobs: create-github-release: name: Create GitHub release - needs: [build, lint, check] + needs: [check] runs-on: ubuntu-latest permissions: contents: write @@ -230,7 +230,7 @@ jobs: publish-test-pypi: name: Publish packages to test.pypi.org if: github.event_name == 'push' && github.ref == 'refs/heads/main' - needs: [build, lint, check, create-github-release] + needs: [create-github-release] runs-on: ubuntu-latest permissions: id-token: write @@ -249,7 +249,7 @@ jobs: name: Publish packages to pypi.org environment: release if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - needs: [build, lint, check, create-github-release] + needs: [create-github-release] runs-on: ubuntu-latest permissions: contents: write From 82a393c5e31b6ebbbd8ec2a8dafc5f35b9cf1236 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 15 May 2026 10:39:38 +0200 Subject: [PATCH 159/168] ci: Remove unnecessary debug output. Downloaded dists can also be traced via the artifacts in the pipeline. --- .github/workflows/main.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6a02b2ee..20dac6c6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -260,9 +260,6 @@ jobs: with: name: dist path: dist - - name: Collected dists - run: | - tree dist - name: PyPI upload uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 - name: Publish GitHub Release From 919044700627889d25ca63b6e7a3bc785f3137eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 05:51:17 +0000 Subject: [PATCH 160/168] Build(deps): Bump pydantic from 2.13.3 to 2.13.4 Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.13.3 to 2.13.4. - [Release notes](https://github.com/pydantic/pydantic/releases) - [Changelog](https://github.com/pydantic/pydantic/blob/v2.13.4/HISTORY.md) - [Commits](https://github.com/pydantic/pydantic/compare/v2.13.3...v2.13.4) --- updated-dependencies: - dependency-name: pydantic dependency-version: 2.13.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Build(deps): Bump pydantic-core from 2.46.3 to 2.46.4 Bumps [pydantic-core](https://github.com/pydantic/pydantic) from 2.46.3 to 2.46.4. - [Release notes](https://github.com/pydantic/pydantic/releases) - [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md) - [Commits](https://github.com/pydantic/pydantic/compare/core-v2.46.3...core-v2.46.4) --- updated-dependencies: - dependency-name: pydantic-core dependency-version: 2.46.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/constraints.txt b/constraints.txt index 66b72241..a7c1f011 100644 --- a/constraints.txt +++ b/constraints.txt @@ -35,8 +35,8 @@ packaging==26.2 pluggy==1.6.0 Pygments==2.20.0 pycparser==3.0 -pydantic==2.13.3 -pydantic-core==2.46.3 +pydantic==2.13.4 +pydantic-core==2.46.4 pytest==9.0.3 readme-renderer==44.0 requests==2.34.0 From b62e2228c80070977baf6b77ba89d5c148af920f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 00:36:34 +0000 Subject: [PATCH 161/168] Build(deps): Bump click from 8.3.3 to 8.4.0 Bumps [click](https://github.com/pallets/click) from 8.3.3 to 8.4.0. - [Release notes](https://github.com/pallets/click/releases) - [Changelog](https://github.com/pallets/click/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/click/compare/8.3.3...8.4.0) --- updated-dependencies: - dependency-name: click dependency-version: 8.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index a7c1f011..a7b80d93 100644 --- a/constraints.txt +++ b/constraints.txt @@ -8,7 +8,7 @@ certifi==2026.4.22 charset-normalizer==3.4.7 check-wheel-contents==0.6.3 cffi==2.0.0 -click==8.3.3 +click==8.4.0 coverage==7.14.0 cryptography==48.0.0 docutils==0.21.2 From 762eaf5033b798b965c92afdbb2cebefa8fc3a8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 00:36:27 +0000 Subject: [PATCH 162/168] Build(deps): Bump jaraco-functools from 4.4.0 to 4.5.0 Bumps [jaraco-functools](https://github.com/jaraco/jaraco.functools) from 4.4.0 to 4.5.0. - [Release notes](https://github.com/jaraco/jaraco.functools/releases) - [Changelog](https://github.com/jaraco/jaraco.functools/blob/main/NEWS.rst) - [Commits](https://github.com/jaraco/jaraco.functools/compare/v4.4.0...v4.5.0) --- updated-dependencies: - dependency-name: jaraco-functools dependency-version: 4.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index a7b80d93..d1b049db 100644 --- a/constraints.txt +++ b/constraints.txt @@ -22,7 +22,7 @@ importlib_metadata==9.0.0 iniconfig==2.3.0 jaraco.classes==3.4.0 jaraco.context==6.1.2 -jaraco.functools==4.4.0 +jaraco.functools==4.5.0 jeepney==0.9.0 Jinja2==3.1.6 keyring==25.7.0 From fc433402c570fd36a7a227ef4bc3abd4579299de Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 00:36:18 +0000 Subject: [PATCH 163/168] Build(deps): Bump idna from 3.14 to 3.15 Bumps [idna](https://github.com/kjd/idna) from 3.14 to 3.15. - [Release notes](https://github.com/kjd/idna/releases) - [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.md) - [Commits](https://github.com/kjd/idna/compare/v3.14...v3.15) --- updated-dependencies: - dependency-name: idna dependency-version: '3.15' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index d1b049db..6d068c50 100644 --- a/constraints.txt +++ b/constraints.txt @@ -16,7 +16,7 @@ exceptiongroup==1.3.1 hypothesis==6.152.6 iniconfig==2.3.0 id==1.6.1 -idna==3.14 +idna==3.15 imagesize==2.0.0 importlib_metadata==9.0.0 iniconfig==2.3.0 From e8bae9bc1f197731fc1a210c0da557af7b698e6d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 00:36:11 +0000 Subject: [PATCH 164/168] Build(deps): Bump requests from 2.34.0 to 2.34.2 Bumps [requests](https://github.com/psf/requests) from 2.34.0 to 2.34.2. - [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.34.0...v2.34.2) --- updated-dependencies: - dependency-name: requests dependency-version: 2.34.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 6d068c50..c1a05f9a 100644 --- a/constraints.txt +++ b/constraints.txt @@ -39,7 +39,7 @@ pydantic==2.13.4 pydantic-core==2.46.4 pytest==9.0.3 readme-renderer==44.0 -requests==2.34.0 +requests==2.34.2 requests-toolbelt==1.0.0 rfc3986==2.0.0 rich==15.0.0 From a56fc77ecd59f781d8471b0f6a82bf58e08c95fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 00:36:06 +0000 Subject: [PATCH 165/168] Build(deps): Bump hypothesis from 6.152.6 to 6.152.8 Bumps [hypothesis](https://github.com/HypothesisWorks/hypothesis) from 6.152.6 to 6.152.8. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-python-6.152.6...hypothesis-python-6.152.8) --- updated-dependencies: - dependency-name: hypothesis dependency-version: 6.152.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index c1a05f9a..6b136156 100644 --- a/constraints.txt +++ b/constraints.txt @@ -13,7 +13,7 @@ coverage==7.14.0 cryptography==48.0.0 docutils==0.21.2 exceptiongroup==1.3.1 -hypothesis==6.152.6 +hypothesis==6.152.8 iniconfig==2.3.0 id==1.6.1 idna==3.15 From ab9f63245094865c42c940a34af724b0dec1debf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 00:35:41 +0000 Subject: [PATCH 166/168] Build(deps): Bump zipp from 3.23.1 to 4.1.0 Bumps [zipp](https://github.com/jaraco/zipp) from 3.23.1 to 4.1.0. - [Release notes](https://github.com/jaraco/zipp/releases) - [Changelog](https://github.com/jaraco/zipp/blob/main/NEWS.rst) - [Commits](https://github.com/jaraco/zipp/compare/v3.23.1...v4.1.0) --- updated-dependencies: - dependency-name: zipp dependency-version: 4.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- constraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constraints.txt b/constraints.txt index 6b136156..ca52c8f5 100644 --- a/constraints.txt +++ b/constraints.txt @@ -64,4 +64,4 @@ typing_extensions==4.15.0 typing-inspection==0.4.2 urllib3==2.7.0 wheel-filename==1.4.2 -zipp==3.23.1 +zipp==4.1.0 From 4b900fb5d0c30949c574e55dd904ee179f858a5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 00:31:53 +0000 Subject: [PATCH 167/168] Build(deps): Bump codecov/codecov-action from 6.0.0 to 6.0.1 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 6.0.0 to 6.0.1. - [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/57e3a136b779b570ffcdbf80b3bdc90e7fab3de2...e79a6962e0d4c0c17b229090214935d2e33f8354) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: 6.0.1 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 20dac6c6..c80e4b82 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -157,7 +157,7 @@ jobs: coverage combine coverage xml - name: Upload coverage report - uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 + uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 with: files: coverage.xml fail_ci_if_error: true From 6e14cd2af9292dca1fa2b027a06bbc40b0e0e425 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Tue, 26 May 2026 11:44:26 +0200 Subject: [PATCH 168/168] chore: Prepare release of v1.4.0. --- changelog.d/1164.added.rst | 5 ----- changelog.d/1298.changed.rst | 1 - changelog.d/1394.changed.rst | 2 -- changelog.d/1395.downstream.rst | 1 - changelog.d/1397.changed.rst | 1 - changelog.d/1419.deprecated.rst | 1 - changelog.d/724.fixed.rst | 1 - docs/reference/changelog.rst | 40 +++++++++++++++++++++++++++++++++ 8 files changed, 40 insertions(+), 12 deletions(-) delete mode 100644 changelog.d/1164.added.rst delete mode 100644 changelog.d/1298.changed.rst delete mode 100644 changelog.d/1394.changed.rst delete mode 100644 changelog.d/1395.downstream.rst delete mode 100644 changelog.d/1397.changed.rst delete mode 100644 changelog.d/1419.deprecated.rst delete mode 100644 changelog.d/724.fixed.rst diff --git a/changelog.d/1164.added.rst b/changelog.d/1164.added.rst deleted file mode 100644 index c68d22dd..00000000 --- a/changelog.d/1164.added.rst +++ /dev/null @@ -1,5 +0,0 @@ -Added the ``pytest_asyncio_loop_factories`` hook to parametrize asyncio tests with custom event loop factories. - -The hook returns a mapping of factory names to loop factories, and ``pytest.mark.asyncio(loop_factories=[...])`` selects a subset of configured factories per test. When a single factory is configured, test names are unchanged on pytest 8.4+. - -Synchronous ``@pytest_asyncio.fixture`` functions now see the correct event loop when custom loop factories are configured, even when test code disrupts the current event loop (e.g., via ``asyncio.run()`` or ``asyncio.set_event_loop(None)``). diff --git a/changelog.d/1298.changed.rst b/changelog.d/1298.changed.rst deleted file mode 100644 index 49f83c83..00000000 --- a/changelog.d/1298.changed.rst +++ /dev/null @@ -1 +0,0 @@ -Improved the readability of the warning message that is displayed when ``asyncio_default_fixture_loop_scope`` is unset diff --git a/changelog.d/1394.changed.rst b/changelog.d/1394.changed.rst deleted file mode 100644 index de757df7..00000000 --- a/changelog.d/1394.changed.rst +++ /dev/null @@ -1,2 +0,0 @@ -Only import ``asyncio.AbstractEventLoopPolicy`` for type checking to avoid raising -a DeprecationWarning. diff --git a/changelog.d/1395.downstream.rst b/changelog.d/1395.downstream.rst deleted file mode 100644 index ff7cc818..00000000 --- a/changelog.d/1395.downstream.rst +++ /dev/null @@ -1 +0,0 @@ -Added dependency on ``sphinx-tabs >= 3.5`` to organize documentation examples into tabs. diff --git a/changelog.d/1397.changed.rst b/changelog.d/1397.changed.rst deleted file mode 100644 index 40cd7f1a..00000000 --- a/changelog.d/1397.changed.rst +++ /dev/null @@ -1 +0,0 @@ -Updated minimum supported pytest version to v8.4.0. diff --git a/changelog.d/1419.deprecated.rst b/changelog.d/1419.deprecated.rst deleted file mode 100644 index 2e4f39e2..00000000 --- a/changelog.d/1419.deprecated.rst +++ /dev/null @@ -1 +0,0 @@ -Overriding the *event_loop_policy* fixture is deprecated. Use the ``pytest_asyncio_loop_factories`` hook instead. diff --git a/changelog.d/724.fixed.rst b/changelog.d/724.fixed.rst deleted file mode 100644 index 6e68478a..00000000 --- a/changelog.d/724.fixed.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed a ``ResourceWarning: unclosed event loop`` warning that could occur when a synchronous test called ``asyncio.run()`` or otherwise unset the current event loop after pytest-asyncio had run an async test or fixture. diff --git a/docs/reference/changelog.rst b/docs/reference/changelog.rst index e6bd7b86..2d9e3ab3 100644 --- a/docs/reference/changelog.rst +++ b/docs/reference/changelog.rst @@ -10,6 +10,46 @@ This project uses `towncrier `__ for changelo .. towncrier release notes start +`1.4.0 `_ - 2026-05-26 +=============================================================================== + +Deprecated +---------- + +- Overriding the *event_loop_policy* fixture is deprecated. Use the ``pytest_asyncio_loop_factories`` hook instead. (`#1419 `_) + + +Added +----- + +- Added the ``pytest_asyncio_loop_factories`` hook to parametrize asyncio tests with custom event loop factories. + + The hook returns a mapping of factory names to loop factories, and ``pytest.mark.asyncio(loop_factories=[...])`` selects a subset of configured factories per test. When a single factory is configured, test names are unchanged. + + Synchronous ``@pytest_asyncio.fixture`` functions now see the correct event loop when custom loop factories are configured, even when test code disrupts the current event loop (e.g., via ``asyncio.run()`` or ``asyncio.set_event_loop(None)``). (`#1164 `_) + + +Changed +------- + +- Improved the readability of the warning message that is displayed when ``asyncio_default_fixture_loop_scope`` is unset (`#1298 `_) +- Only import ``asyncio.AbstractEventLoopPolicy`` for type checking to avoid raising + a DeprecationWarning. (`#1394 `_) +- Updated minimum supported pytest version to v8.4.0. (`#1397 `_) + + +Fixed +----- + +- Fixed a ``ResourceWarning: unclosed event loop`` warning that could occur when a synchronous test called ``asyncio.run()`` or otherwise unset the current event loop after pytest-asyncio had run an async test or fixture. (`#724 `_) + + +Notes for Downstream Packagers +------------------------------ + +- Added dependency on ``sphinx-tabs >= 3.5`` to organize documentation examples into tabs. (`#1395 `_) + + `1.3.0 `_ - 2025-11-10 ===============================================================================