From 57ac04ed8c0bfb1a16b9ba4a46374c465e579a2a Mon Sep 17 00:00:00 2001 From: A5rocks Date: Fri, 31 Oct 2025 16:18:13 +0900 Subject: [PATCH 01/36] Start new cycle --- src/trio/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/trio/_version.py b/src/trio/_version.py index 9f941c270b..659188b5e0 100644 --- a/src/trio/_version.py +++ b/src/trio/_version.py @@ -1,3 +1,3 @@ # This file is imported from __init__.py and parsed by setuptools -__version__ = "0.32.0" +__version__ = "0.32.0+dev" From 5926cd8e302ed5fdbf905c5b9eb0062c5e1082cc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 1 Nov 2025 01:15:58 +0000 Subject: [PATCH 02/36] Dependency updates (#3350) --- .pre-commit-config.yaml | 8 ++++---- test-requirements.txt | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 46cad85615..d7a89bb87a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.2 + rev: v0.14.3 hooks: - id: ruff-check types: [file] @@ -42,11 +42,11 @@ repos: hooks: - id: typos - repo: https://github.com/sphinx-contrib/sphinx-lint - rev: v1.0.0 + rev: v1.0.1 hooks: - id: sphinx-lint - repo: https://github.com/woodruffw/zizmor-pre-commit - rev: v1.16.0 + rev: v1.16.1 hooks: - id: zizmor - repo: local @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.5 + rev: 0.9.7 hooks: # Compile requirements - id: pip-compile diff --git a/test-requirements.txt b/test-requirements.txt index 9717b1646c..952a219608 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -120,7 +120,7 @@ pylint==4.0.2 # via -r test-requirements.in pyopenssl==25.3.0 # via -r test-requirements.in -pyright==1.1.406 +pyright==1.1.407 # via -r test-requirements.in pytest==8.4.2 # via -r test-requirements.in @@ -132,7 +132,7 @@ requests==2.32.5 # via sphinx roman-numerals-py==3.1.0 ; python_full_version >= '3.11' # via sphinx -ruff==0.14.2 +ruff==0.14.3 # via -r test-requirements.in sniffio==1.3.1 # via -r test-requirements.in @@ -192,7 +192,7 @@ typing-extensions==4.15.0 # virtualenv urllib3==2.5.0 # via requests -uv==0.9.5 +uv==0.9.7 # via -r test-requirements.in -virtualenv==20.35.3 +virtualenv==20.35.4 # via pre-commit From 300adeec95e2cd92d2665d74456e360d12e32a81 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 18:11:39 -0500 Subject: [PATCH 03/36] [pre-commit.ci] pre-commit autoupdate (#3351) --- .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 d7a89bb87a..d3f4e44306 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,7 +38,7 @@ repos: # tomli needed on 3.10. tomllib is available in stdlib on 3.11+ - tomli - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.38.1 + rev: v1.39.0 hooks: - id: typos - repo: https://github.com/sphinx-contrib/sphinx-lint @@ -46,7 +46,7 @@ repos: hooks: - id: sphinx-lint - repo: https://github.com/woodruffw/zizmor-pre-commit - rev: v1.16.1 + rev: v1.16.2 hooks: - id: zizmor - repo: local From 107048dc592434c608bf77dd40eecc9aeac24b7f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 03:51:30 +0000 Subject: [PATCH 04/36] Dependency updates (#3354) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 8 ++++---- test-requirements.txt | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d3f4e44306..b1fa725056 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,11 +20,11 @@ repos: - id: sort-simple-yaml files: .pre-commit-config.yaml - repo: https://github.com/psf/black-pre-commit-mirror - rev: 25.9.0 + rev: 25.11.0 hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.3 + rev: v0.14.4 hooks: - id: ruff-check types: [file] @@ -46,7 +46,7 @@ repos: hooks: - id: sphinx-lint - repo: https://github.com/woodruffw/zizmor-pre-commit - rev: v1.16.2 + rev: v1.16.3 hooks: - id: zizmor - repo: local @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.7 + rev: 0.9.8 hooks: # Compile requirements - id: pip-compile diff --git a/test-requirements.txt b/test-requirements.txt index 952a219608..9c00ee385c 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,7 +4,7 @@ alabaster==1.0.0 # via sphinx astor==0.8.1 # via -r test-requirements.in -astroid==4.0.1 +astroid==4.0.2 # via pylint async-generator==1.10 # via -r test-requirements.in @@ -14,7 +14,7 @@ attrs==25.4.0 # outcome babel==2.17.0 # via sphinx -black==25.9.0 ; implementation_name == 'cpython' +black==25.11.0 ; implementation_name == 'cpython' # via -r test-requirements.in certifi==2025.10.5 # via requests @@ -36,7 +36,7 @@ colorama==0.4.6 ; sys_platform == 'win32' # pylint # pytest # sphinx -coverage==7.11.0 +coverage==7.11.3 # via -r test-requirements.in cryptography==46.0.3 # via @@ -108,7 +108,7 @@ platformdirs==4.5.0 # virtualenv pluggy==1.6.0 # via pytest -pre-commit==4.3.0 +pre-commit==4.4.0 # via -r test-requirements.in pycparser==2.23 ; (implementation_name != 'PyPy' and os_name == 'nt') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi @@ -122,9 +122,9 @@ pyopenssl==25.3.0 # via -r test-requirements.in pyright==1.1.407 # via -r test-requirements.in -pytest==8.4.2 +pytest==9.0.0 # via -r test-requirements.in -pytokens==0.2.0 ; implementation_name == 'cpython' +pytokens==0.3.0 ; implementation_name == 'cpython' # via black pyyaml==6.0.3 # via pre-commit @@ -132,7 +132,7 @@ requests==2.32.5 # via sphinx roman-numerals-py==3.1.0 ; python_full_version >= '3.11' # via sphinx -ruff==0.14.3 +ruff==0.14.4 # via -r test-requirements.in sniffio==1.3.1 # via -r test-requirements.in @@ -192,7 +192,7 @@ typing-extensions==4.15.0 # virtualenv urllib3==2.5.0 # via requests -uv==0.9.7 +uv==0.9.8 # via -r test-requirements.in virtualenv==20.35.4 # via pre-commit From 63ce96808bca270c8e58601c21c4d194d6249b56 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 16:56:00 -0500 Subject: [PATCH 05/36] [pre-commit.ci] pre-commit autoupdate (#3356) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate 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/adhtruong/mirrors-typos: v1.39.0 → v1.39.2](https://github.com/adhtruong/mirrors-typos/compare/v1.39.0...v1.39.2) - [github.com/astral-sh/uv-pre-commit: 0.9.8 → 0.9.10](https://github.com/astral-sh/uv-pre-commit/compare/0.9.8...0.9.10) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- test-requirements.txt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b1fa725056..c042990630 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.4 + rev: v0.14.5 hooks: - id: ruff-check types: [file] @@ -38,7 +38,7 @@ repos: # tomli needed on 3.10. tomllib is available in stdlib on 3.11+ - tomli - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.39.0 + rev: v1.39.2 hooks: - id: typos - repo: https://github.com/sphinx-contrib/sphinx-lint @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.8 + rev: 0.9.10 hooks: # Compile requirements - id: pip-compile diff --git a/test-requirements.txt b/test-requirements.txt index 9c00ee385c..8a2bbe2015 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -132,7 +132,7 @@ requests==2.32.5 # via sphinx roman-numerals-py==3.1.0 ; python_full_version >= '3.11' # via sphinx -ruff==0.14.4 +ruff==0.14.5 # via -r test-requirements.in sniffio==1.3.1 # via -r test-requirements.in @@ -192,7 +192,7 @@ typing-extensions==4.15.0 # virtualenv urllib3==2.5.0 # via requests -uv==0.9.8 +uv==0.9.10 # via -r test-requirements.in virtualenv==20.35.4 # via pre-commit From c4017e4cd590acee1d835257ce5cab8cae6723ee Mon Sep 17 00:00:00 2001 From: Ripan Roy <88824889+Ripan-Roy@users.noreply.github.com> Date: Mon, 24 Nov 2025 16:23:29 +0530 Subject: [PATCH 06/36] Add smoke tests to CI workflow for verifying build artifacts (#3352) --- .github/workflows/ci.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9daca186a9..1e84e47c07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,12 +86,25 @@ jobs: dist/${{ steps.artifact-name.outputs.wheel }} retention-days: 5 + smoke-tests: + name: Smoke tests + needs: + - build + + runs-on: ubuntu-latest + + steps: + - name: Switch to using Python 3.11 + uses: actions/setup-python@v6 + with: + python-version: 3.11 + - name: >- Smoke-test: retrieve the project source from an sdist inside the GHA artifact uses: re-actors/checkout-python-sdist@release/v2 with: - source-tarball-name: ${{ steps.artifact-name.outputs.sdist }} + source-tarball-name: ${{ needs.build.outputs.sdist-artifact-name }} workflow-artifact-name: ${{ env.dists-artifact-name }} - name: >- @@ -456,6 +469,7 @@ jobs: if: always() needs: + - smoke-tests - Windows - Ubuntu - macOS From fe49a53454161e1e1bfaf15de9b324f58bd8d5ac 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:55:51 +0000 Subject: [PATCH 07/36] [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/sphinx-contrib/sphinx-lint: v1.0.1 → v1.0.2](https://github.com/sphinx-contrib/sphinx-lint/compare/v1.0.1...v1.0.2) - [github.com/astral-sh/uv-pre-commit: 0.9.10 → 0.9.11](https://github.com/astral-sh/uv-pre-commit/compare/0.9.10...0.9.11) --- .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 c042990630..6887e0cca4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.5 + rev: v0.14.6 hooks: - id: ruff-check types: [file] @@ -42,7 +42,7 @@ repos: hooks: - id: typos - repo: https://github.com/sphinx-contrib/sphinx-lint - rev: v1.0.1 + rev: v1.0.2 hooks: - id: sphinx-lint - repo: https://github.com/woodruffw/zizmor-pre-commit @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.10 + rev: 0.9.11 hooks: # Compile requirements - id: pip-compile From 5c07f6f7e7b40308e54dbdc6c03ce5bb1f211226 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:56:19 +0000 Subject: [PATCH 08/36] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- test-requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index 8a2bbe2015..4c56a29c71 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -132,7 +132,7 @@ requests==2.32.5 # via sphinx roman-numerals-py==3.1.0 ; python_full_version >= '3.11' # via sphinx -ruff==0.14.5 +ruff==0.14.6 # via -r test-requirements.in sniffio==1.3.1 # via -r test-requirements.in @@ -192,7 +192,7 @@ typing-extensions==4.15.0 # virtualenv urllib3==2.5.0 # via requests -uv==0.9.10 +uv==0.9.11 # via -r test-requirements.in virtualenv==20.35.4 # via pre-commit From a73a028f380b8edeca8186ba50860a98ee5cae5f Mon Sep 17 00:00:00 2001 From: A5rocks Date: Wed, 26 Nov 2025 09:29:47 -0500 Subject: [PATCH 09/36] Attempt to fix for Android's `sys.platform` changes (#3359) * Attempt to fix for Android * Add a newsfragment --- newsfragments/3357.bugfix.rst | 1 + src/trio/_core/__init__.py | 6 +++--- src/trio/_core/_run.py | 6 +++++- 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 newsfragments/3357.bugfix.rst diff --git a/newsfragments/3357.bugfix.rst b/newsfragments/3357.bugfix.rst new file mode 100644 index 0000000000..d2badc264b --- /dev/null +++ b/newsfragments/3357.bugfix.rst @@ -0,0 +1 @@ +Start supporting Android's new ``"android"`` `sys.platform`. diff --git a/src/trio/_core/__init__.py b/src/trio/_core/__init__.py index f9d8068f0c..c53d9d59a0 100644 --- a/src/trio/_core/__init__.py +++ b/src/trio/_core/__init__.py @@ -86,9 +86,9 @@ write_overlapped, ) # Kqueue imports -if (sys.platform != "linux" and sys.platform != "win32") or ( - not _t.TYPE_CHECKING and "sphinx.ext.autodoc" in sys.modules -): +if ( + sys.platform != "linux" and sys.platform != "win32" and sys.platform != "android" +) or (not _t.TYPE_CHECKING and "sphinx.ext.autodoc" in sys.modules): from ._run import current_kqueue, monitor_kevent, wait_kevent del sys # It would be better to import sys as _sys, but mypy does not understand it diff --git a/src/trio/_core/_run.py b/src/trio/_core/_run.py index 4689dca104..8a7ddc1dc3 100644 --- a/src/trio/_core/_run.py +++ b/src/trio/_core/_run.py @@ -3113,7 +3113,11 @@ def in_trio_task() -> bool: WindowsIOManager as TheIOManager, _WindowsStatistics as IOStatistics, ) -elif sys.platform == "linux" or (not TYPE_CHECKING and hasattr(select, "epoll")): +elif ( + sys.platform == "linux" + or sys.platform == "android" + or (not TYPE_CHECKING and hasattr(select, "epoll")) +): from ._generated_io_epoll import * from ._io_epoll import ( EpollIOManager as TheIOManager, From e242fb6784c9ec7320e89c7d3d34e31abcdef95b Mon Sep 17 00:00:00 2001 From: A5rocks Date: Mon, 1 Dec 2025 17:29:35 -0500 Subject: [PATCH 10/36] Avoid `pre-commit run` failing (#3361) --- .github/workflows/autodeps.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/autodeps.yml b/.github/workflows/autodeps.yml index 8b58f8904f..309dfd373f 100644 --- a/.github/workflows/autodeps.yml +++ b/.github/workflows/autodeps.yml @@ -40,7 +40,7 @@ jobs: run: python -m pip install -r test-requirements.txt - name: Pre-commit fixes - run: pre-commit run -a + run: pre-commit run -a || true - name: Commit changes and create automerge PR env: From 0ae6e622fb618ca833a6106dedb1aed4884ca94a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 16:01:26 -0500 Subject: [PATCH 11/36] Bump dependencies from commit e242fb (#3363) * Dependency updates * Try to address CI failures * Another pass at CI issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update test-requirements.txt * Address mypy changes --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: A5rocks Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 8 +++--- docs-requirements.txt | 8 +++--- src/trio/_core/_tests/test_asyncgen.py | 6 +++-- src/trio/_core/_tests/test_ki.py | 8 +++--- src/trio/_tests/test_exports.py | 8 ++++++ src/trio/_tests/test_path.py | 6 ++--- src/trio/socket.py | 6 ++--- test-requirements.in | 2 +- test-requirements.txt | 36 ++++++++++++++------------ 9 files changed, 51 insertions(+), 37 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6887e0cca4..3029664854 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.6 + rev: v0.14.7 hooks: - id: ruff-check types: [file] @@ -38,7 +38,7 @@ repos: # tomli needed on 3.10. tomllib is available in stdlib on 3.11+ - tomli - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.39.2 + rev: v1.40.0 hooks: - id: typos - repo: https://github.com/sphinx-contrib/sphinx-lint @@ -46,7 +46,7 @@ repos: hooks: - id: sphinx-lint - repo: https://github.com/woodruffw/zizmor-pre-commit - rev: v1.16.3 + rev: v1.18.0 hooks: - id: zizmor - repo: local @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.11 + rev: 0.9.14 hooks: # Compile requirements - id: pip-compile diff --git a/docs-requirements.txt b/docs-requirements.txt index 4fcf0cd18b..cb27b1c262 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -8,9 +8,9 @@ attrs==25.4.0 # outcome babel==2.17.0 # via sphinx -beautifulsoup4==4.14.2 +beautifulsoup4==4.14.3 # via sphinx-codeautolink -certifi==2025.10.5 +certifi==2025.11.12 # via requests cffi==2.0.0 ; os_name == 'nt' or platform_python_implementation != 'PyPy' # via @@ -18,7 +18,7 @@ cffi==2.0.0 ; os_name == 'nt' or platform_python_implementation != 'PyPy' # cryptography charset-normalizer==3.4.4 # via requests -click==8.3.0 +click==8.3.1 # via towncrier colorama==0.4.6 ; sys_platform == 'win32' # via @@ -30,7 +30,7 @@ docutils==0.21.2 # via # sphinx # sphinx-rtd-theme -exceptiongroup==1.3.0 +exceptiongroup==1.3.1 # via -r docs-requirements.in idna==3.11 # via diff --git a/src/trio/_core/_tests/test_asyncgen.py b/src/trio/_core/_tests/test_asyncgen.py index 8147a0e57b..91ca0250aa 100644 --- a/src/trio/_core/_tests/test_asyncgen.py +++ b/src/trio/_core/_tests/test_asyncgen.py @@ -221,13 +221,15 @@ async def async_main() -> None: saved.append(agen()) await saved[-1].asend(None) + ATTEMPT_AMOUNT = 50 + # Actually running into the edge case requires that the run_sync_soon task # execute in between the system nursery's closure and the strong-ification # of runner.asyncgens. There's about a 25% chance that it doesn't # (if the run_sync_soon task runs before init on one tick and after init # on the next tick); if we try enough times, we can make the chance of # failure as small as we want. - for _attempt in range(50): + for _ in range(ATTEMPT_AMOUNT): needs_retry = False record.clear() saved.clear() @@ -240,7 +242,7 @@ async def async_main() -> None: else: # pragma: no cover pytest.fail( "Didn't manage to hit the trailing_finalizer_asyncgens case " - f"despite trying {_attempt} times", + f"despite trying {ATTEMPT_AMOUNT} times", ) diff --git a/src/trio/_core/_tests/test_ki.py b/src/trio/_core/_tests/test_ki.py index a8d81ca020..cf37aae6d4 100644 --- a/src/trio/_core/_tests/test_ki.py +++ b/src/trio/_core/_tests/test_ki.py @@ -164,7 +164,7 @@ def protected_manager() -> Iterator[None]: @pytest.mark.skipif(async_generator is None, reason="async_generator not installed") async def test_async_generator_agen_protection() -> None: @_core.enable_ki_protection - @async_generator # type: ignore[misc] # untyped generator + @async_generator # type: ignore[untyped-decorator] async def agen_protected1() -> None: # type: ignore[misc] # untyped generator assert _core.currently_ki_protected() try: @@ -173,7 +173,7 @@ async def agen_protected1() -> None: # type: ignore[misc] # untyped generator assert _core.currently_ki_protected() @_core.disable_ki_protection - @async_generator # type: ignore[misc] # untyped generator + @async_generator # type: ignore[untyped-decorator] async def agen_unprotected1() -> None: # type: ignore[misc] # untyped generator assert not _core.currently_ki_protected() try: @@ -182,7 +182,7 @@ async def agen_unprotected1() -> None: # type: ignore[misc] # untyped generator assert not _core.currently_ki_protected() # Swap the order of the decorators: - @async_generator # type: ignore[misc] # untyped generator + @async_generator # type: ignore[untyped-decorator] @_core.enable_ki_protection async def agen_protected2() -> None: # type: ignore[misc] # untyped generator assert _core.currently_ki_protected() @@ -191,7 +191,7 @@ async def agen_protected2() -> None: # type: ignore[misc] # untyped generator finally: assert _core.currently_ki_protected() - @async_generator # type: ignore[misc] # untyped generator + @async_generator # type: ignore[untyped-decorator] @_core.disable_ki_protection async def agen_unprotected2() -> None: # type: ignore[misc] # untyped generator assert not _core.currently_ki_protected() diff --git a/src/trio/_tests/test_exports.py b/src/trio/_tests/test_exports.py index 0f8158f377..10d71a9ff7 100644 --- a/src/trio/_tests/test_exports.py +++ b/src/trio/_tests/test_exports.py @@ -175,6 +175,10 @@ def no_underscores(symbols: Iterable[str]) -> set[str]: completions = script.complete() static_names = no_underscores(c.name for c in completions) elif tool == "mypy": + if sys.implementation.name != "cpython": + # https://github.com/python/mypy/issues/20329 + pytest.skip("mypy does not support pypy") + if not RUN_SLOW: # pragma: no cover pytest.skip("use --run-slow to check against mypy") @@ -272,6 +276,10 @@ def no_hidden(symbols: Iterable[str]) -> set[str]: if tool == "jedi" and sys.implementation.name != "cpython": pytest.skip("jedi does not support pypy") + if tool == "mypy" and sys.implementation.name != "cpython": + # https://github.com/python/mypy/issues/20329 + pytest.skip("mypy does not support pypy") + if tool == "mypy": cache = Path.cwd() / ".mypy_cache" diff --git a/src/trio/_tests/test_path.py b/src/trio/_tests/test_path.py index 533ff94c5a..9fe3920df3 100644 --- a/src/trio/_tests/test_path.py +++ b/src/trio/_tests/test_path.py @@ -210,16 +210,16 @@ async def test_globmethods(path: trio.Path) -> None: await (path / "bar.dat").write_bytes(b"") # Path.glob - for _pattern, _results in { + for pattern, results in { "*.txt": {"bar.txt"}, "**/*.txt": {"_bar.txt", "bar.txt"}, }.items(): entries = set() - for entry in await path.glob(_pattern): + for entry in await path.glob(pattern): assert isinstance(entry, trio.Path) entries.add(entry.name) - assert entries == _results + assert entries == results # Path.rglob entries = set() diff --git a/src/trio/socket.py b/src/trio/socket.py index cfcb9943c8..5375a1a675 100644 --- a/src/trio/socket.py +++ b/src/trio/socket.py @@ -26,9 +26,9 @@ # have: globals().update( { - _name: getattr(_stdlib_socket, _name) - for _name in _stdlib_socket.__all__ - if _name.isupper() and _name not in _bad_symbols + name: getattr(_stdlib_socket, name) + for name in _stdlib_socket.__all__ + if name.isupper() and name not in _bad_symbols }, ) diff --git a/test-requirements.in b/test-requirements.in index b16a2b5d6e..272da6a34c 100644 --- a/test-requirements.in +++ b/test-requirements.in @@ -11,7 +11,7 @@ cryptography>=41.0.0 # cryptography<41 segfaults on pypy3.10 # Tools black; implementation_name == "cpython" -mypy +mypy; implementation_name == "cpython" ruff >= 0.8.0 astor # code generation uv >= 0.2.24 diff --git a/test-requirements.txt b/test-requirements.txt index 4c56a29c71..b155926324 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -16,17 +16,17 @@ babel==2.17.0 # via sphinx black==25.11.0 ; implementation_name == 'cpython' # via -r test-requirements.in -certifi==2025.10.5 +certifi==2025.11.12 # via requests cffi==2.0.0 ; os_name == 'nt' or platform_python_implementation != 'PyPy' # via # -r test-requirements.in # cryptography -cfgv==3.4.0 +cfgv==3.5.0 # via pre-commit charset-normalizer==3.4.4 # via requests -click==8.3.0 ; implementation_name == 'cpython' +click==8.3.1 ; implementation_name == 'cpython' # via black codespell==2.4.1 # via -r test-requirements.in @@ -36,7 +36,7 @@ colorama==0.4.6 ; sys_platform == 'win32' # pylint # pytest # sphinx -coverage==7.11.3 +coverage==7.12.0 # via -r test-requirements.in cryptography==46.0.3 # via @@ -48,9 +48,11 @@ dill==0.4.0 # via pylint distlib==0.4.0 # via virtualenv -docutils==0.21.2 +docutils==0.21.2 ; python_full_version < '3.11' # via sphinx -exceptiongroup==1.3.0 ; python_full_version < '3.11' +docutils==0.22.3 ; python_full_version >= '3.11' + # via sphinx +exceptiongroup==1.3.1 ; python_full_version < '3.11' # via # -r test-requirements.in # pytest @@ -73,11 +75,13 @@ jedi==0.19.2 ; implementation_name == 'cpython' # via -r test-requirements.in jinja2==3.1.6 # via sphinx +librt==0.6.3 ; implementation_name == 'cpython' + # via mypy markupsafe==3.0.3 # via jinja2 mccabe==0.7.0 # via pylint -mypy==1.18.2 +mypy==1.19.0 ; implementation_name == 'cpython' # via -r test-requirements.in mypy-extensions==1.1.0 # via @@ -97,7 +101,7 @@ packaging==25.0 # sphinx parso==0.8.5 ; implementation_name == 'cpython' # via jedi -pathspec==0.12.1 +pathspec==0.12.1 ; implementation_name == 'cpython' # via # black # mypy @@ -108,7 +112,7 @@ platformdirs==4.5.0 # virtualenv pluggy==1.6.0 # via pytest -pre-commit==4.4.0 +pre-commit==4.5.0 # via -r test-requirements.in pycparser==2.23 ; (implementation_name != 'PyPy' and os_name == 'nt') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi @@ -116,13 +120,13 @@ pygments==2.19.2 # via # pytest # sphinx -pylint==4.0.2 +pylint==4.0.4 # via -r test-requirements.in pyopenssl==25.3.0 # via -r test-requirements.in pyright==1.1.407 # via -r test-requirements.in -pytest==9.0.0 +pytest==9.0.1 # via -r test-requirements.in pytokens==0.3.0 ; implementation_name == 'cpython' # via black @@ -130,9 +134,9 @@ pyyaml==6.0.3 # via pre-commit requests==2.32.5 # via sphinx -roman-numerals-py==3.1.0 ; python_full_version >= '3.11' +roman-numerals==3.1.0 ; python_full_version >= '3.11' # via sphinx -ruff==0.14.6 +ruff==0.14.7 # via -r test-requirements.in sniffio==1.3.1 # via -r test-requirements.in @@ -142,7 +146,7 @@ sortedcontainers==2.4.0 # via -r test-requirements.in sphinx==8.1.3 ; python_full_version < '3.11' # via -r test-requirements.in -sphinx==8.2.3 ; python_full_version >= '3.11' +sphinx==9.0.1 ; python_full_version >= '3.11' # via -r test-requirements.in sphinxcontrib-applehelp==2.0.0 # via sphinx @@ -171,7 +175,7 @@ types-cffi==1.17.0.20250915 # via # -r test-requirements.in # types-pyopenssl -types-docutils==0.22.2.20251006 +types-docutils==0.22.3.20251115 # via -r test-requirements.in types-pyopenssl==24.1.0.20240722 # via -r test-requirements.in @@ -192,7 +196,7 @@ typing-extensions==4.15.0 # virtualenv urllib3==2.5.0 # via requests -uv==0.9.11 +uv==0.9.14 # via -r test-requirements.in virtualenv==20.35.4 # via pre-commit From 7d87afe6cb21b881d0fd1ad035089ab3b0c86f80 Mon Sep 17 00:00:00 2001 From: A5rocks Date: Fri, 5 Dec 2025 04:31:01 -0500 Subject: [PATCH 12/36] Re-add `tests/` directory for coverage (#3314) --- pyproject.toml | 2 +- src/trio/_core/_tests/test_thread_cache.py | 25 +++++----------------- src/trio/_core/_thread_cache.py | 4 +++- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index aef3c258fc..a68f7d5482 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -311,7 +311,7 @@ omit = [ parallel = true plugins = [] relative_files = true -source = ["trio"] +source = ["trio", "tests/", "_trio_check_attrs_aliases"] [tool.coverage.report] precision = 1 diff --git a/src/trio/_core/_tests/test_thread_cache.py b/src/trio/_core/_tests/test_thread_cache.py index a308befb67..3abdd59e43 100644 --- a/src/trio/_core/_tests/test_thread_cache.py +++ b/src/trio/_core/_tests/test_thread_cache.py @@ -220,23 +220,8 @@ def foo() -> None: if child_pid != 0: # if this test fails, this will hang, triggering a timeout. os.waitpid(child_pid, 0) - else: - # this is necessary because os._exit doesn't unwind the stack, - # so coverage doesn't get to automatically stop and save - # coverage information. - try: - import coverage - - cov = coverage.Coverage.current() - # the following pragmas are necessary because if coverage: - # - isn't running, then it can't record the branch not - # taken - # - isn't installed, then it can't record the ImportError - - if cov: # pragma: no branch - cov.stop() - cov.save() - except ImportError: # pragma: no cover - pass - - os._exit(0) # pragma: no cover # coverage was stopped above. + else: # pragma: no cover # coverage is shut down by os._exit(0) + # we would *want* to allow coverage to take a snapshot here. check + # git blame for how to do that. however, that times out for some + # reason when having `tests/` in `source` for coverage.py. + os._exit(0) diff --git a/src/trio/_core/_thread_cache.py b/src/trio/_core/_thread_cache.py index 44820e7711..c2b2315bd3 100644 --- a/src/trio/_core/_thread_cache.py +++ b/src/trio/_core/_thread_cache.py @@ -303,7 +303,9 @@ def start_thread_soon( THREAD_CACHE.start_thread_soon(fn, deliver, name) -def clear_worker_threads() -> None: +def clear_worker_threads() -> ( + None +): # pragma: no cover # see test_clear_thread_cache_after_fork # This is OK because the child process does not actually have any # worker threads. Additionally, while WorkerThread keeps a strong # reference and so would get affected, the only place those are From d64798f0d1a2ff7eaecb83484d72cb02dd2b4cab Mon Sep 17 00:00:00 2001 From: A5rocks Date: Fri, 5 Dec 2025 14:05:44 -0500 Subject: [PATCH 13/36] Fix CI failing on main (#3365) * We don't care about PyPy + mypy anymore * Avoid segfault * Update src/trio/_core/_tests/test_run.py --- src/trio/_core/_tests/test_run.py | 2 +- src/trio/_tests/test_exports.py | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/trio/_core/_tests/test_run.py b/src/trio/_core/_tests/test_run.py index 9b059a4366..45ea21cdd4 100644 --- a/src/trio/_core/_tests/test_run.py +++ b/src/trio/_core/_tests/test_run.py @@ -1062,7 +1062,7 @@ async def main() -> None: # the upstream issue is resolved. @restore_unraisablehook() @pytest.mark.skipif( - sys.version_info[:3] == (3, 14, 0), + sys.version_info[:2] == (3, 14), reason="https://github.com/python/cpython/issues/133932", ) def test_error_in_run_loop() -> None: diff --git a/src/trio/_tests/test_exports.py b/src/trio/_tests/test_exports.py index 10d71a9ff7..abc50210d3 100644 --- a/src/trio/_tests/test_exports.py +++ b/src/trio/_tests/test_exports.py @@ -365,17 +365,6 @@ def lookup_symbol(symbol: str) -> dict[str, Any]: # type: ignore[misc, explicit ignore_names.add("__firstlineno__") ignore_names.add("__static_attributes__") - # pypy seems to have some additional dunders that differ - if sys.implementation.name == "pypy": - ignore_names |= { - "__basicsize__", - "__dictoffset__", - "__itemsize__", - "__sizeof__", - "__weakrefoffset__", - "__unicode__", - } - # inspect.getmembers sees `name` and `value` in Enums, otherwise # it behaves the same way as `dir` # runtime_names = no_underscores(dir(class_)) From ffe7261e66a3f7af53e9671c9fc06e2342d3bda6 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sat, 6 Dec 2025 02:35:35 +0100 Subject: [PATCH 14/36] Configure coveragepy to separate importables&pths Fixes #3314. --- pyproject.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a68f7d5482..7c0373e35f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -293,6 +293,10 @@ skip_covered = false [tool.coverage.paths] source = ["src", "**/site-packages"] +attrs-plugin = [ + "tests/_trio_check_attrs_aliases.py", + "_trio_check_attrs_aliases.py", +] [tool.coverage.run] branch = true @@ -311,7 +315,8 @@ omit = [ parallel = true plugins = [] relative_files = true -source = ["trio", "tests/", "_trio_check_attrs_aliases"] +source = ["."] +source_pkgs = ["trio", "_trio_check_attrs_aliases"] [tool.coverage.report] precision = 1 From b670a915801dc824a2b73203b874e8dd9d8e6550 Mon Sep 17 00:00:00 2001 From: A5rocks Date: Sun, 7 Dec 2025 21:54:11 -0500 Subject: [PATCH 15/36] Avoid resolving packages unnecessarily (#3366) --- ci.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci.sh b/ci.sh index 07797937e9..9362dbc727 100755 --- a/ci.sh +++ b/ci.sh @@ -54,7 +54,7 @@ if [ "${NO_TEST_REQUIREMENTS-0}" == 1 ]; then python -m uv pip install pytest coverage -c test-requirements.txt flags="--skip-optional-imports" else - python -m uv pip install -r test-requirements.txt + python -m uv pip install -r test-requirements.txt --no-deps flags="" fi From 8bf4cba9b883431b9fd17cca5433749c36527ad4 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 22:04:31 +0000 Subject: [PATCH 16/36] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black-pre-commit-mirror: 25.11.0 → 25.12.0](https://github.com/psf/black-pre-commit-mirror/compare/25.11.0...25.12.0) - [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) - [github.com/astral-sh/uv-pre-commit: 0.9.14 → 0.9.16](https://github.com/astral-sh/uv-pre-commit/compare/0.9.14...0.9.16) --- .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 3029664854..bda4b08a45 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,11 +20,11 @@ repos: - id: sort-simple-yaml files: .pre-commit-config.yaml - repo: https://github.com/psf/black-pre-commit-mirror - rev: 25.11.0 + rev: 25.12.0 hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.7 + rev: v0.14.8 hooks: - id: ruff-check types: [file] @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.14 + rev: 0.9.16 hooks: # Compile requirements - id: pip-compile From 3ced7fc5d333ab685cc01a9fcf538a80eab01cd7 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 22:05:10 +0000 Subject: [PATCH 17/36] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- test-requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index b155926324..26e63e0c85 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -14,7 +14,7 @@ attrs==25.4.0 # outcome babel==2.17.0 # via sphinx -black==25.11.0 ; implementation_name == 'cpython' +black==25.12.0 ; implementation_name == 'cpython' # via -r test-requirements.in certifi==2025.11.12 # via requests @@ -136,7 +136,7 @@ requests==2.32.5 # via sphinx roman-numerals==3.1.0 ; python_full_version >= '3.11' # via sphinx -ruff==0.14.7 +ruff==0.14.8 # via -r test-requirements.in sniffio==1.3.1 # via -r test-requirements.in @@ -196,7 +196,7 @@ typing-extensions==4.15.0 # virtualenv urllib3==2.5.0 # via requests -uv==0.9.14 +uv==0.9.16 # via -r test-requirements.in virtualenv==20.35.4 # via pre-commit From e93fdf187358e9b43e378b9ed9810aafb3d1ef6c Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 8 Dec 2025 16:08:41 -0600 Subject: [PATCH 18/36] Add noqa for new ruff warning --- src/trio/_core/_traps.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/trio/_core/_traps.py b/src/trio/_core/_traps.py index 652cc0b879..14f66af927 100644 --- a/src/trio/_core/_traps.py +++ b/src/trio/_core/_traps.py @@ -66,7 +66,9 @@ class PermanentlyDetachCoroutineObject: def _real_async_yield( obj: MessageType, ) -> Generator[MessageType, None, None]: - return (yield obj) + # "Using `yield` and `return {value}` in a generator function can + # lead to confusing behavior" + return (yield obj) # noqa: B901 # Real yield value is from trio's main loop, but type checkers can't From 5f9c6031ff223a182e93e2f866b4001b8ffad51c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 00:42:37 +0000 Subject: [PATCH 19/36] Dependency updates (#3370) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs-requirements.txt | 2 +- test-requirements.txt | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs-requirements.txt b/docs-requirements.txt index cb27b1c262..b350e9be1d 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -105,5 +105,5 @@ typing-extensions==4.15.0 # beautifulsoup4 # exceptiongroup # pyopenssl -urllib3==2.5.0 +urllib3==2.6.1 # via requests diff --git a/test-requirements.txt b/test-requirements.txt index 26e63e0c85..1884e3b95d 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -36,7 +36,7 @@ colorama==0.4.6 ; sys_platform == 'win32' # pylint # pytest # sphinx -coverage==7.12.0 +coverage==7.13.0 # via -r test-requirements.in cryptography==46.0.3 # via @@ -75,7 +75,7 @@ jedi==0.19.2 ; implementation_name == 'cpython' # via -r test-requirements.in jinja2==3.1.6 # via sphinx -librt==0.6.3 ; implementation_name == 'cpython' +librt==0.7.3 ; implementation_name == 'cpython' # via mypy markupsafe==3.0.3 # via jinja2 @@ -105,7 +105,7 @@ pathspec==0.12.1 ; implementation_name == 'cpython' # via # black # mypy -platformdirs==4.5.0 +platformdirs==4.5.1 # via # black # pylint @@ -126,7 +126,7 @@ pyopenssl==25.3.0 # via -r test-requirements.in pyright==1.1.407 # via -r test-requirements.in -pytest==9.0.1 +pytest==9.0.2 # via -r test-requirements.in pytokens==0.3.0 ; implementation_name == 'cpython' # via black @@ -146,7 +146,7 @@ sortedcontainers==2.4.0 # via -r test-requirements.in sphinx==8.1.3 ; python_full_version < '3.11' # via -r test-requirements.in -sphinx==9.0.1 ; python_full_version >= '3.11' +sphinx==9.0.4 ; python_full_version >= '3.11' # via -r test-requirements.in sphinxcontrib-applehelp==2.0.0 # via sphinx @@ -194,7 +194,7 @@ typing-extensions==4.15.0 # pyopenssl # pyright # virtualenv -urllib3==2.5.0 +urllib3==2.6.1 # via requests uv==0.9.16 # via -r test-requirements.in From 889821ea40df764ced30175a7bb7d55cc6a79e00 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 17:18:31 -0500 Subject: [PATCH 20/36] [pre-commit.ci] pre-commit autoupdate (#3372) --- .pre-commit-config.yaml | 4 ++-- test-requirements.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bda4b08a45..d69c54a7a4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.8 + rev: v0.14.9 hooks: - id: ruff-check types: [file] @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.16 + rev: 0.9.17 hooks: # Compile requirements - id: pip-compile diff --git a/test-requirements.txt b/test-requirements.txt index 1884e3b95d..0b3ca23100 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -136,7 +136,7 @@ requests==2.32.5 # via sphinx roman-numerals==3.1.0 ; python_full_version >= '3.11' # via sphinx -ruff==0.14.8 +ruff==0.14.9 # via -r test-requirements.in sniffio==1.3.1 # via -r test-requirements.in @@ -196,7 +196,7 @@ typing-extensions==4.15.0 # virtualenv urllib3==2.6.1 # via requests -uv==0.9.16 +uv==0.9.17 # via -r test-requirements.in virtualenv==20.35.4 # via pre-commit From 7eac6979fb38100e5ac844af4c460216f3df0629 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 21:23:27 +0000 Subject: [PATCH 21/36] [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/woodruffw/zizmor-pre-commit: v1.18.0 → v1.19.0](https://github.com/woodruffw/zizmor-pre-commit/compare/v1.18.0...v1.19.0) - [github.com/astral-sh/uv-pre-commit: 0.9.17 → 0.9.18](https://github.com/astral-sh/uv-pre-commit/compare/0.9.17...0.9.18) --- .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 d69c54a7a4..3b1369f7dd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.9 + rev: v0.14.10 hooks: - id: ruff-check types: [file] @@ -46,7 +46,7 @@ repos: hooks: - id: sphinx-lint - repo: https://github.com/woodruffw/zizmor-pre-commit - rev: v1.18.0 + rev: v1.19.0 hooks: - id: zizmor - repo: local @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.17 + rev: 0.9.18 hooks: # Compile requirements - id: pip-compile From 2eeec349c81fb403b28b1345df2e65a6fc66dd4f 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 21:23:44 +0000 Subject: [PATCH 22/36] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- test-requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index 0b3ca23100..346de1f3cd 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -136,7 +136,7 @@ requests==2.32.5 # via sphinx roman-numerals==3.1.0 ; python_full_version >= '3.11' # via sphinx -ruff==0.14.9 +ruff==0.14.10 # via -r test-requirements.in sniffio==1.3.1 # via -r test-requirements.in @@ -196,7 +196,7 @@ typing-extensions==4.15.0 # virtualenv urllib3==2.6.1 # via requests -uv==0.9.17 +uv==0.9.18 # via -r test-requirements.in virtualenv==20.35.4 # via pre-commit From 4f905c2d46071f0b3cc687371bdacb2df45e5c37 Mon Sep 17 00:00:00 2001 From: Abduaziz Date: Wed, 31 Dec 2025 04:04:16 +0500 Subject: [PATCH 23/36] Deprecate `testing.RaisesGroup` and `testing.Matcher` (#3337) * chore: deprecate `RaisesGroup` * feat: deprecate `RaisesGroup` using `deprecate_attributes` utility * feat: deprecate `Matcher` * feat: make `DeprecatedAttribute` as `final` * feat: supress warnings about deprecation warning on specific test cases * fix: replace RaisesGroup & Matcher with pytest alternatives across all test cases * docs: add newsfragment * fix: change version number * style: format merged code * fix: adjust `test_static_tool_sees_class_members` for deprecated classes (RaisesGroup, Matcher) * fix: change text formatting, so that build tool won't search for references * fix: add underscore prefix on class names to match exported classes' names * style: correct formatting on newsfragment * fix: find/replace * fix: use `pytest.deprecated_call` context manager * fix: delete type tests, mypy errors * fix: remove deprecated calls * chore: update `_check_type_completeness.json` * test: add test case to test deprecation --- newsfragments/3326.deprecated.rst | 1 + src/trio/_core/_tests/test_cancelled.py | 12 +- src/trio/_core/_tests/test_ki.py | 6 +- src/trio/_core/_tests/test_parking_lot.py | 9 +- src/trio/_core/_tests/test_run.py | 126 +++++----- src/trio/_deprecate.py | 3 + src/trio/_tests/_check_type_completeness.json | 6 +- src/trio/_tests/test_channel.py | 10 +- src/trio/_tests/test_exports.py | 2 +- .../_tests/test_highlevel_open_tcp_stream.py | 5 +- .../_tests/test_highlevel_serve_listeners.py | 9 +- src/trio/_tests/test_signals.py | 3 +- src/trio/_tests/test_ssl.py | 15 +- src/trio/_tests/test_subprocess.py | 9 +- src/trio/_tests/test_sync.py | 6 +- src/trio/_tests/test_testing.py | 6 +- src/trio/_tests/test_testing_raisesgroup.py | 18 +- src/trio/_tests/test_util.py | 2 +- src/trio/_tests/type_tests/raisesgroup.py | 223 ------------------ src/trio/testing/__init__.py | 21 +- 20 files changed, 154 insertions(+), 338 deletions(-) create mode 100644 newsfragments/3326.deprecated.rst delete mode 100644 src/trio/_tests/type_tests/raisesgroup.py diff --git a/newsfragments/3326.deprecated.rst b/newsfragments/3326.deprecated.rst new file mode 100644 index 0000000000..74916bf4e6 --- /dev/null +++ b/newsfragments/3326.deprecated.rst @@ -0,0 +1 @@ +Both :class:`trio.testing.RaisesGroup` and :class:`trio.testing.Matcher` have been deprecated. Pytest alternatives ``pytest.RaisesGroup`` and ``pytest.RaisesExc`` (respectively) are considered correct replacement. diff --git a/src/trio/_core/_tests/test_cancelled.py b/src/trio/_core/_tests/test_cancelled.py index 0c144c37f0..0280b6068b 100644 --- a/src/trio/_core/_tests/test_cancelled.py +++ b/src/trio/_core/_tests/test_cancelled.py @@ -7,7 +7,7 @@ import trio from trio import Cancelled from trio.lowlevel import current_task -from trio.testing import RaisesGroup, wait_all_tasks_blocked +from trio.testing import wait_all_tasks_blocked from .test_ki import ki_self @@ -108,7 +108,7 @@ async def failing_task(task_status: trio.TaskStatus[trio.lowlevel.Task]) -> None task_status.started(current_task()) raise ValueError - with RaisesGroup(ValueError, TypeError): + with pytest.RaisesGroup(ValueError, TypeError): async with trio.open_nursery() as nursery: fail_task = await nursery.start(failing_task) with pytest.raises(Cancelled, match=match_str.format(fail_task)): @@ -123,7 +123,7 @@ async def failing_task(task_status: trio.TaskStatus[trio.lowlevel.Task]) -> None await wait_all_tasks_blocked() raise ValueError - with RaisesGroup(ValueError, TypeError): + with pytest.RaisesGroup(ValueError, TypeError): async with trio.open_nursery() as nursery: fail_task = await nursery.start(failing_task) await nursery.start(cancelled_task, fail_task) @@ -147,7 +147,7 @@ async def cancelled_task() -> None: ): await trio.sleep_forever() - with RaisesGroup(ValueError): + with pytest.RaisesGroup(ValueError): async with trio.open_nursery() as nursery: nursery.start_soon(cancelled_task) await wait_all_tasks_blocked() @@ -192,7 +192,7 @@ async def child() -> None: ): await trio.sleep_forever() - with RaisesGroup(ValueError): + with pytest.RaisesGroup(ValueError): async with trio.open_nursery() as nursery: nursery.start_soon(child) await ev.wait() @@ -214,7 +214,7 @@ async def sleeper(name: str) -> None: async def raiser(name: str) -> None: ki_self() - with RaisesGroup(KeyboardInterrupt): + with pytest.RaisesGroup(KeyboardInterrupt): async with trio.open_nursery() as nursery: nursery.start_soon(sleeper, "s1") nursery.start_soon(sleeper, "s2") diff --git a/src/trio/_core/_tests/test_ki.py b/src/trio/_core/_tests/test_ki.py index cf37aae6d4..ea45edaef6 100644 --- a/src/trio/_core/_tests/test_ki.py +++ b/src/trio/_core/_tests/test_ki.py @@ -12,8 +12,6 @@ import outcome import pytest -from trio.testing import RaisesGroup - from .tutil import gc_collect_harder try: @@ -309,7 +307,7 @@ async def check_unprotected_kill() -> None: nursery.start_soon(raiser, "r1", record_set) # raises inside a nursery, so the KeyboardInterrupt is wrapped in an ExceptionGroup - with RaisesGroup(KeyboardInterrupt): + with pytest.RaisesGroup(KeyboardInterrupt): _core.run(check_unprotected_kill) assert record_set == {"s1 ok", "s2 ok", "r1 raise ok"} @@ -326,7 +324,7 @@ async def check_protected_kill() -> None: # __aexit__ blocks, and then receives the KI # raises inside a nursery, so the KeyboardInterrupt is wrapped in an ExceptionGroup - with RaisesGroup(KeyboardInterrupt): + with pytest.RaisesGroup(KeyboardInterrupt): _core.run(check_protected_kill) assert record_set == {"s1 ok", "s2 ok", "r1 cancel ok"} diff --git a/src/trio/_core/_tests/test_parking_lot.py b/src/trio/_core/_tests/test_parking_lot.py index 3348e47cbf..55c5144f91 100644 --- a/src/trio/_core/_tests/test_parking_lot.py +++ b/src/trio/_core/_tests/test_parking_lot.py @@ -11,7 +11,6 @@ current_task, remove_parking_lot_breaker, ) -from trio.testing import Matcher, RaisesGroup from ... import _core from ...testing import wait_all_tasks_blocked @@ -267,8 +266,8 @@ async def bad_parker(lot: ParkingLot, scope: _core.CancelScope) -> None: cs = _core.CancelScope() # check that parked task errors - with RaisesGroup( - Matcher(_core.BrokenResourceError, match="^Parking lot broken by"), + with pytest.RaisesGroup( + pytest.RaisesExc(_core.BrokenResourceError, match="^Parking lot broken by"), ): async with _core.open_nursery() as nursery: nursery.start_soon(bad_parker, lot, cs) @@ -382,8 +381,8 @@ async def return_me_and_park( await lot.park() lot = ParkingLot() - with RaisesGroup( - Matcher(_core.BrokenResourceError, match="^Parking lot broken by"), + with pytest.RaisesGroup( + pytest.RaisesExc(_core.BrokenResourceError, match="^Parking lot broken by"), ): async with _core.open_nursery() as nursery: child_task = await nursery.start(return_me_and_park, lot) diff --git a/src/trio/_core/_tests/test_run.py b/src/trio/_core/_tests/test_run.py index 45ea21cdd4..94e448448d 100644 --- a/src/trio/_core/_tests/test_run.py +++ b/src/trio/_core/_tests/test_run.py @@ -27,8 +27,6 @@ from ..._threads import to_thread_run_sync from ..._timeouts import fail_after, sleep from ...testing import ( - Matcher, - RaisesGroup, Sequencer, assert_checkpoints, wait_all_tasks_blocked, @@ -134,7 +132,7 @@ async def test_nursery_warn_use_async_with() -> None: async def test_nursery_main_block_error_basic() -> None: exc = ValueError("whoops") - with RaisesGroup(Matcher(check=lambda e: e is exc)): + with pytest.RaisesGroup(pytest.RaisesExc(check=lambda e: e is exc)): async with _core.open_nursery(): raise exc @@ -145,7 +143,7 @@ async def test_child_crash_basic() -> None: async def erroring() -> NoReturn: raise my_exc - with RaisesGroup(Matcher(check=lambda e: e is my_exc)): + with pytest.RaisesGroup(pytest.RaisesExc(check=lambda e: e is my_exc)): # nursery.__aexit__ propagates exception from child back to parent async with _core.open_nursery() as nursery: nursery.start_soon(erroring) @@ -187,7 +185,7 @@ async def main() -> None: nursery.start_soon(looper) nursery.start_soon(crasher) - with RaisesGroup(Matcher(ValueError, "^argh$")): + with pytest.RaisesGroup(pytest.RaisesExc(ValueError, match="^argh$")): _core.run(main) assert looper_record == ["cancelled"] @@ -204,7 +202,7 @@ async def main() -> NoReturn: nursery.start_soon(crasher) raise KeyError - with RaisesGroup(ValueError, KeyError): + with pytest.RaisesGroup(ValueError, KeyError): _core.run(main) @@ -217,7 +215,7 @@ async def main() -> None: nursery.start_soon(crasher, KeyError) nursery.start_soon(crasher, ValueError) - with RaisesGroup(ValueError, KeyError): + with pytest.RaisesGroup(ValueError, KeyError): _core.run(main) @@ -225,7 +223,7 @@ async def test_child_crash_wakes_parent() -> None: async def crasher() -> NoReturn: raise ValueError("this is a crash") - with RaisesGroup(Matcher(ValueError, "^this is a crash$")): + with pytest.RaisesGroup(pytest.RaisesExc(ValueError, match="^this is a crash$")): async with _core.open_nursery() as nursery: nursery.start_soon(crasher) await sleep_forever() @@ -465,13 +463,13 @@ async def crasher() -> NoReturn: # This is outside the outer scope, so all the Cancelled # exceptions should have been absorbed, leaving just a regular # KeyError from crasher(), wrapped in an ExceptionGroup - with RaisesGroup(KeyError): + with pytest.RaisesGroup(KeyError): with _core.CancelScope() as outer: # Since the outer scope became cancelled before the # nursery block exited, all cancellations inside the # nursery block continue propagating to reach the # outer scope. - with RaisesGroup( + with pytest.RaisesGroup( _core.Cancelled, _core.Cancelled, _core.Cancelled, @@ -490,7 +488,7 @@ async def crasher() -> NoReturn: # And one that raises a different error nursery.start_soon(crasher) # t4 # and then our __aexit__ also receives an outer Cancelled - # reraise the exception caught by RaisesGroup for the + # reraise the exception caught by pytest.RaisesGroup for the # CancelScope to handle raise excinfo.value @@ -824,11 +822,11 @@ def no_context(exc: RuntimeError) -> bool: return exc.__context__ is None msg = "closed before the task exited" - group = RaisesGroup( - Matcher(RuntimeError, match=msg, check=no_context), - Matcher(RuntimeError, match=msg, check=no_context), + group = pytest.RaisesGroup( + pytest.RaisesExc(RuntimeError, match=msg, check=no_context), + pytest.RaisesExc(RuntimeError, match=msg, check=no_context), # sleep_forever - Matcher( + pytest.RaisesExc( RuntimeError, match=msg, check=lambda x: isinstance(x.__context__, _core.Cancelled), @@ -1126,7 +1124,9 @@ async def main() -> None: # the first exceptiongroup is from the first nursery opened in Runner.init() # the second exceptiongroup is from the second nursery opened in Runner.init() # the third exceptongroup is from the nursery defined in `system_task` above - assert RaisesGroup(RaisesGroup(RaisesGroup(KeyError, ValueError))).matches( + assert pytest.RaisesGroup( + pytest.RaisesGroup(pytest.RaisesGroup(KeyError, ValueError)) + ).matches( excinfo.value.__cause__, ) @@ -1156,7 +1156,9 @@ async def main() -> None: _core.run(main) # See explanation for triple-wrap in test_system_task_crash_ExceptionGroup - assert RaisesGroup(RaisesGroup(RaisesGroup(ValueError))).matches( + assert pytest.RaisesGroup( + pytest.RaisesGroup(pytest.RaisesGroup(ValueError)) + ).matches( excinfo.value.__cause__, ) @@ -1172,7 +1174,9 @@ async def main() -> None: with pytest.raises(_core.TrioInternalError) as excinfo: _core.run(main) # "Only" double-wrapped since ki() doesn't create an exceptiongroup - assert RaisesGroup(RaisesGroup(KeyboardInterrupt)).matches(excinfo.value.__cause__) + assert pytest.RaisesGroup(pytest.RaisesGroup(KeyboardInterrupt)).matches( + excinfo.value.__cause__ + ) # This used to fail because checkpoint was a yield followed by an immediate @@ -1280,7 +1284,9 @@ async def child() -> None: await sleep_forever() raise - with RaisesGroup(Matcher(KeyError, check=lambda e: e.__context__ is None)): + with pytest.RaisesGroup( + pytest.RaisesExc(KeyError, check=lambda e: e.__context__ is None) + ): async with _core.open_nursery() as nursery: nursery.start_soon(child) await wait_all_tasks_blocked() @@ -1306,11 +1312,11 @@ async def child() -> None: except KeyError: await sleep_forever() - with RaisesGroup( - Matcher( + with pytest.RaisesGroup( + pytest.RaisesExc( ValueError, - "error text", - lambda e: isinstance(e.__context__, KeyError), + match="error text", + check=lambda e: isinstance(e.__context__, KeyError), ), ): async with _core.open_nursery() as nursery: @@ -1345,7 +1351,9 @@ async def inner(exc: BaseException) -> None: await sleep_forever() assert not_none(sys.exc_info()[1]) is exc - with RaisesGroup(Matcher(KeyError, check=lambda e: e.__context__ is None)): + with pytest.RaisesGroup( + pytest.RaisesExc(KeyError, check=lambda e: e.__context__ is None) + ): async with _core.open_nursery() as nursery: nursery.start_soon(child) await wait_all_tasks_blocked() @@ -1375,11 +1383,11 @@ async def inner() -> None: except IndexError: await sleep_forever() - with RaisesGroup( - Matcher( + with pytest.RaisesGroup( + pytest.RaisesExc( ValueError, - "^Unique Text$", - lambda e: isinstance(e.__context__, IndexError) + match="^Unique Text$", + check=lambda e: isinstance(e.__context__, IndexError) and isinstance(e.__context__.__context__, KeyError), ), ): @@ -1397,7 +1405,9 @@ async def crasher() -> NoReturn: raise KeyError # the ExceptionGroup should not have the KeyError or ValueError as context - with RaisesGroup(ValueError, KeyError, check=lambda x: x.__context__ is None): + with pytest.RaisesGroup( + ValueError, KeyError, check=lambda x: x.__context__ is None + ): async with _core.open_nursery() as nursery: nursery.start_soon(crasher) raise ValueError @@ -1527,7 +1537,9 @@ async def main() -> None: _core.run(main) # the first exceptiongroup is from the first nursery opened in Runner.init() # the second exceptiongroup is from the second nursery opened in Runner.init() - assert RaisesGroup(RaisesGroup(KeyError)).matches(excinfo.value.__cause__) + assert pytest.RaisesGroup(pytest.RaisesGroup(KeyError)).matches( + excinfo.value.__cause__ + ) assert record == {"2nd run_sync_soon ran", "cancelled!"} @@ -1641,7 +1653,7 @@ async def main() -> None: with pytest.raises(_core.TrioInternalError) as excinfo: _core.run(main) - assert RaisesGroup(KeyError).matches(excinfo.value.__cause__) + assert pytest.RaisesGroup(KeyError).matches(excinfo.value.__cause__) assert record == ["main exiting", "2nd ran"] @@ -1876,11 +1888,15 @@ async def async_gen(arg: T) -> AsyncGenerator[T, None]: # pragma: no cover # bad_call_spawn calls the function inside a nursery, so the exception will be # wrapped in an exceptiongroup - with RaisesGroup(Matcher(TypeError, "expecting an async function")): + with pytest.RaisesGroup( + pytest.RaisesExc(TypeError, match="expecting an async function") + ): bad_call_spawn(f()) # type: ignore[arg-type] - with RaisesGroup( - Matcher(TypeError, "expected an async function but got an async generator"), + with pytest.RaisesGroup( + pytest.RaisesExc( + TypeError, match="expected an async function but got an async generator" + ), ): bad_call_spawn(async_gen, 0) # type: ignore @@ -1903,7 +1919,7 @@ async def misguided() -> None: async def test_asyncio_function_inside_nursery_does_not_explode() -> None: # Regression test for https://github.com/python-trio/trio/issues/552 - with RaisesGroup(Matcher(TypeError, "asyncio")): + with pytest.RaisesGroup(pytest.RaisesExc(TypeError, match="asyncio")): async with _core.open_nursery() as nursery: nursery.start_soon(sleep_forever) await create_asyncio_future_in_new_loop() @@ -1943,7 +1959,7 @@ async def noop_with_no_checkpoint() -> None: with _core.CancelScope() as cancel_scope: cancel_scope.cancel() - with RaisesGroup(KeyError): + with pytest.RaisesGroup(KeyError): async with _core.open_nursery(): raise KeyError @@ -2081,7 +2097,7 @@ async def test_task_nursery_stack() -> None: assert task._child_nurseries == [] async with _core.open_nursery() as nursery1: assert task._child_nurseries == [nursery1] - with RaisesGroup(KeyError): + with pytest.RaisesGroup(KeyError): async with _core.open_nursery() as nursery2: assert task._child_nurseries == [nursery1, nursery2] raise KeyError @@ -2179,7 +2195,7 @@ async def start_sleep_then_crash(nursery: _core.Nursery) -> None: async def test_nursery_explicit_exception() -> None: - with RaisesGroup(KeyError): + with pytest.RaisesGroup(KeyError): async with _core.open_nursery(): raise KeyError() @@ -2188,7 +2204,7 @@ async def test_nursery_stop_iteration() -> None: async def fail() -> NoReturn: raise ValueError - with RaisesGroup(StopIteration, ValueError): + with pytest.RaisesGroup(StopIteration, ValueError): async with _core.open_nursery() as nursery: nursery.start_soon(fail) raise StopIteration @@ -2235,7 +2251,7 @@ async def __anext__(self) -> list[int]: # With strict_exception_groups enabled, users now need to unwrap # StopAsyncIteration and re-raise it. # This would be relatively clean on python3.11+ with except*. - # We could also use RaisesGroup, but that's primarily meant as + # We could also use pytest.RaisesGroup, but that's primarily meant as # test infra, not as a runtime tool. if len(e.exceptions) == 1 and isinstance( e.exceptions[0], @@ -2264,7 +2280,7 @@ def check_traceback(exc: KeyError) -> bool: assert tb is not None return tb.tb_frame.f_code is my_child_task.__code__ - with RaisesGroup(Matcher(KeyError, check=check_traceback)): + with pytest.RaisesGroup(pytest.RaisesExc(KeyError, check=check_traceback)): # For now cancel/nursery scopes still leave a bunch of tb gunk behind. # But if there's an Exceptiongroup, they leave it on the group, # which lets us get a clean look at the KeyError itself. @@ -2443,7 +2459,7 @@ async def detachable_coroutine( # Check the exception paths too task = None pdco_outcome = None - with RaisesGroup(KeyError): + with pytest.RaisesGroup(KeyError): async with _core.open_nursery() as nursery: nursery.start_soon(detachable_coroutine, outcome.Error(KeyError()), "uh oh") throw_in = ValueError() @@ -2619,7 +2635,9 @@ async def crasher() -> NoReturn: # (See https://github.com/python-trio/trio/pull/1864) await do_a_cancel() - with RaisesGroup(Matcher(ValueError, "^this is a crash$")): + with pytest.RaisesGroup( + pytest.RaisesExc(ValueError, match="^this is a crash$") + ): async with _core.open_nursery() as nursery: # cover NurseryManager.__aexit__ nursery.start_soon(crasher) @@ -2645,8 +2663,8 @@ async def crasher() -> NoReturn: old_flags = gc.get_debug() try: with ( - RaisesGroup( - Matcher(ValueError, "^this is a crash$"), + pytest.RaisesGroup( + pytest.RaisesExc(ValueError, match="^this is a crash$"), ), _core.CancelScope() as outer, ): @@ -2769,15 +2787,15 @@ def run_main() -> None: # mypy doesn't like kwarg magic _core.run(main, **_create_kwargs(run_strict)) # type: ignore[arg-type] - matcher = Matcher(RuntimeError, r"^test error$") + matcher = pytest.RaisesExc(RuntimeError, match=r"^test error$") if multiple_exceptions: - with RaisesGroup(matcher, matcher): + with pytest.RaisesGroup(matcher, matcher): run_main() elif open_nursery_strict or ( open_nursery_strict is None and run_strict is not False ): - with RaisesGroup(matcher): + with pytest.RaisesGroup(matcher): run_main() else: with pytest.raises(RuntimeError, match=r"^test error$"): @@ -2798,11 +2816,11 @@ async def raise_error() -> NoReturn: raise RuntimeError("test error") # mypy requires explicit type for conditional expression - maybe_wrapped_runtime_error: type[RuntimeError] | RaisesGroup[RuntimeError] = ( - RuntimeError if strict is False else RaisesGroup(RuntimeError) - ) + maybe_wrapped_runtime_error: ( + type[RuntimeError] | pytest.RaisesGroup[RuntimeError] + ) = (RuntimeError if strict is False else pytest.RaisesGroup(RuntimeError)) - with RaisesGroup(RuntimeError, maybe_wrapped_runtime_error): + with pytest.RaisesGroup(RuntimeError, maybe_wrapped_runtime_error): async with _core.open_nursery() as nursery: nursery.start_soon(sleep_forever) nursery.start_soon(raise_error) @@ -2818,7 +2836,7 @@ async def test_cancel_scope_no_cancellederror() -> None: a Cancelled exception, it will NOT set the ``cancelled_caught`` flag. """ - with RaisesGroup(RuntimeError, RuntimeError, match="test"): + with pytest.RaisesGroup(RuntimeError, RuntimeError, match="test"): with _core.CancelScope() as scope: scope.cancel() raise ExceptionGroup("test", [RuntimeError(), RuntimeError()]) @@ -2915,7 +2933,7 @@ async def spawn_tasks_in_old_nursery(task_status: _core.TaskStatus[None]) -> Non async with _core.open_nursery() as nursery: with pytest.raises(_core.TrioInternalError) as excinfo: await nursery.start(spawn_tasks_in_old_nursery) - assert RaisesGroup(ValueError, ValueError).matches(excinfo.value.__cause__) + assert pytest.RaisesGroup(ValueError, ValueError).matches(excinfo.value.__cause__) if sys.version_info >= (3, 11): diff --git a/src/trio/_deprecate.py b/src/trio/_deprecate.py index 5c827b4fda..2f1f9f43a4 100644 --- a/src/trio/_deprecate.py +++ b/src/trio/_deprecate.py @@ -7,6 +7,8 @@ import attrs +from ._util import final + if TYPE_CHECKING: from collections.abc import Callable @@ -139,6 +141,7 @@ def wrapper(*args: ArgsT.args, **kwargs: ArgsT.kwargs) -> RetT: return wrapper +@final @attrs.frozen(slots=False) class DeprecatedAttribute: _not_set: ClassVar[object] = object() diff --git a/src/trio/_tests/_check_type_completeness.json b/src/trio/_tests/_check_type_completeness.json index 72d981f89c..6e8c54ea8b 100644 --- a/src/trio/_tests/_check_type_completeness.json +++ b/src/trio/_tests/_check_type_completeness.json @@ -45,10 +45,6 @@ "No docstring found for class \"trio.socket.herror\"", "No docstring found for function \"trio._core._mock_clock.MockClock.start_clock\"", "No docstring found for function \"trio._core._mock_clock.MockClock.current_time\"", - "No docstring found for function \"trio._core._mock_clock.MockClock.deadline_to_sleep_time\"", - "No docstring found for function \"trio.testing._raises_group._ExceptionInfo.exconly\"", - "No docstring found for function \"trio.testing._raises_group._ExceptionInfo.errisinstance\"", - "No docstring found for function \"trio.testing._raises_group._ExceptionInfo.getrepr\"", - "No docstring found for function \"trio.testing._raises_group.RaisesGroup.expected_type\"" + "No docstring found for function \"trio._core._mock_clock.MockClock.deadline_to_sleep_time\"" ] } diff --git a/src/trio/_tests/test_channel.py b/src/trio/_tests/test_channel.py index 986207b309..c8b2efddc6 100644 --- a/src/trio/_tests/test_channel.py +++ b/src/trio/_tests/test_channel.py @@ -8,7 +8,7 @@ import trio from trio import EndOfChannel, as_safe_channel, open_memory_channel -from ..testing import Matcher, RaisesGroup, assert_checkpoints, wait_all_tasks_blocked +from ..testing import assert_checkpoints, wait_all_tasks_blocked if sys.version_info < (3, 11): from exceptiongroup import ExceptionGroup @@ -518,9 +518,9 @@ async def agen(events: list[str]) -> AsyncGenerator[int]: raise ValueError("agen") events: list[str] = [] - with RaisesGroup( - Matcher(ValueError, match="^agen$"), - Matcher(TypeError, match="^iterator$"), + with pytest.RaisesGroup( + pytest.RaisesExc(ValueError, match="^agen$"), + pytest.RaisesExc(TypeError, match="^iterator$"), ) as g: async with agen(events) as recv_chan: async for i in recv_chan: # pragma: no branch @@ -574,7 +574,7 @@ async def agen() -> AsyncGenerator[None]: raise NotImplementedError("not entered") yield # pragma: no cover - with RaisesGroup(Matcher(ValueError, match="bar"), match="foo"): + with pytest.RaisesGroup(pytest.RaisesExc(ValueError, match="bar"), match="foo"): async with agen() as _: raise ExceptionGroup("foo", [ValueError("bar")]) diff --git a/src/trio/_tests/test_exports.py b/src/trio/_tests/test_exports.py index abc50210d3..0414d43423 100644 --- a/src/trio/_tests/test_exports.py +++ b/src/trio/_tests/test_exports.py @@ -333,7 +333,7 @@ def lookup_symbol(symbol: str) -> dict[str, Any]: # type: ignore[misc, explicit # __init__ is called (and reason they don't use attrs is because they're going # to be reimplemented in pytest). # Not 100% that's the case, and it works locally, so whatever /shrug - if class_ is trio.testing.RaisesGroup or class_ is trio.testing.Matcher: + if module_name == "trio.testing" and class_name in ("_RaisesGroup", "_Matcher"): continue # dir() and inspect.getmembers doesn't display properties from the metaclass diff --git a/src/trio/_tests/test_highlevel_open_tcp_stream.py b/src/trio/_tests/test_highlevel_open_tcp_stream.py index eaec52ad4e..193bf77e75 100644 --- a/src/trio/_tests/test_highlevel_open_tcp_stream.py +++ b/src/trio/_tests/test_highlevel_open_tcp_stream.py @@ -16,7 +16,6 @@ reorder_for_rfc_6555_section_5_4, ) from trio.socket import AF_INET, AF_INET6, IPPROTO_TCP, SOCK_STREAM, SocketType -from trio.testing import Matcher, RaisesGroup if TYPE_CHECKING: from collections.abc import Sequence @@ -550,8 +549,8 @@ async def test_all_fail(autojump_clock: MockClock) -> None: ) assert isinstance(exc, OSError) - subexceptions = (Matcher(OSError, match="^sorry$"),) * 4 - assert RaisesGroup( + subexceptions = (pytest.RaisesExc(OSError, match="^sorry$"),) * 4 + assert pytest.RaisesGroup( *subexceptions, match="all attempts to connect to test.example.com:80 failed", ).matches(exc.__cause__) diff --git a/src/trio/_tests/test_highlevel_serve_listeners.py b/src/trio/_tests/test_highlevel_serve_listeners.py index 9268555b32..1a6ac94690 100644 --- a/src/trio/_tests/test_highlevel_serve_listeners.py +++ b/src/trio/_tests/test_highlevel_serve_listeners.py @@ -5,15 +5,14 @@ from typing import TYPE_CHECKING, NoReturn, cast import attrs +import pytest import trio from trio import Nursery, StapledStream, TaskStatus from trio.testing import ( - Matcher, MemoryReceiveStream, MemorySendStream, MockClock, - RaisesGroup, memory_stream_pair, wait_all_tasks_blocked, ) @@ -21,8 +20,6 @@ if TYPE_CHECKING: from collections.abc import Awaitable, Callable - import pytest - from trio._channel import MemoryReceiveChannel, MemorySendChannel from trio.abc import Stream @@ -124,7 +121,7 @@ def check_error(e: BaseException) -> bool: listener.accept_hook = raise_error - with RaisesGroup(Matcher(check=check_error)): + with pytest.RaisesGroup(pytest.RaisesExc(check=check_error)): await trio.serve_listeners(None, [listener]) # type: ignore[arg-type] @@ -172,7 +169,7 @@ async def connection_watcher( raise Done # the exception is wrapped twice because we open two nested nurseries - with RaisesGroup(RaisesGroup(Done)): + with pytest.RaisesGroup(pytest.RaisesGroup(Done)): async with trio.open_nursery() as nursery: value = await nursery.start(connection_watcher) assert isinstance(value, trio.Nursery) diff --git a/src/trio/_tests/test_signals.py b/src/trio/_tests/test_signals.py index 9c86741742..501ae1e80a 100644 --- a/src/trio/_tests/test_signals.py +++ b/src/trio/_tests/test_signals.py @@ -6,7 +6,6 @@ import pytest import trio -from trio.testing import RaisesGroup from .. import _core from .._signals import _signal_handler, get_pending_signal_count, open_signal_receiver @@ -75,7 +74,7 @@ async def naughty() -> None: async def test_open_signal_receiver_conflict() -> None: - with RaisesGroup(trio.BusyResourceError): + with pytest.RaisesGroup(trio.BusyResourceError): with open_signal_receiver(signal.SIGILL) as receiver: async with trio.open_nursery() as nursery: nursery.start_soon(receiver.__anext__) diff --git a/src/trio/_tests/test_ssl.py b/src/trio/_tests/test_ssl.py index b365f7dd35..085ce8f1dc 100644 --- a/src/trio/_tests/test_ssl.py +++ b/src/trio/_tests/test_ssl.py @@ -15,12 +15,7 @@ from trio import StapledStream from trio._tests.pytest_plugin import skip_if_optional_else_raise from trio.abc import ReceiveStream, SendStream -from trio.testing import ( - Matcher, - MemoryReceiveStream, - MemorySendStream, - RaisesGroup, -) +from trio.testing import MemoryReceiveStream, MemorySendStream try: import trustme @@ -350,7 +345,9 @@ async def do_test( args2: tuple[object, ...], ) -> None: s = PyOpenSSLEchoStream() - with RaisesGroup(Matcher(_core.BusyResourceError, "simultaneous")): + with pytest.RaisesGroup( + pytest.RaisesExc(_core.BusyResourceError, match="simultaneous") + ): async with _core.open_nursery() as nursery: nursery.start_soon(getattr(s, func1), *args1) nursery.start_soon(getattr(s, func2), *args2) @@ -774,7 +771,9 @@ async def do_test( func2: Callable[[S], Awaitable[None]], ) -> None: s, _ = ssl_lockstep_stream_pair(client_ctx) - with RaisesGroup(Matcher(_core.BusyResourceError, "another task")): + with pytest.RaisesGroup( + pytest.RaisesExc(_core.BusyResourceError, match="another task") + ): async with _core.open_nursery() as nursery: nursery.start_soon(func1, s) nursery.start_soon(func2, s) diff --git a/src/trio/_tests/test_subprocess.py b/src/trio/_tests/test_subprocess.py index 71b143c38c..05ac69d3f2 100644 --- a/src/trio/_tests/test_subprocess.py +++ b/src/trio/_tests/test_subprocess.py @@ -22,7 +22,6 @@ import pytest import trio -from trio.testing import Matcher, RaisesGroup from .. import ( Event, @@ -661,7 +660,9 @@ async def do_stuff() -> None: nursery.cancel_scope.cancel() # double wrap from our nursery + the internal nursery - with RaisesGroup(RaisesGroup(Matcher(ValueError, "^foo$"))): + with pytest.RaisesGroup( + pytest.RaisesGroup(pytest.RaisesExc(ValueError, match="^foo$")) + ): _core.run(do_stuff, strict_exception_groups=True) @@ -698,7 +699,7 @@ async def test_warn_on_cancel_SIGKILL_escalation( # the background_process_param exercises a lot of run_process cases, but it uses # check=False, so lets have a test that uses check=True as well async def test_run_process_background_fail() -> None: - with RaisesGroup(subprocess.CalledProcessError): + with pytest.RaisesGroup(subprocess.CalledProcessError): async with _core.open_nursery() as nursery: value = await nursery.start(run_process, EXIT_FALSE) assert isinstance(value, Process) @@ -733,7 +734,7 @@ async def very_broken_open(*args: object, **kwargs: object) -> str: return "oops" monkeypatch.setattr(trio._subprocess, "_open_process", very_broken_open) - with RaisesGroup(AttributeError, AttributeError): + with pytest.RaisesGroup(AttributeError, AttributeError): await run_process(EXIT_TRUE, capture_stdout=True) diff --git a/src/trio/_tests/test_sync.py b/src/trio/_tests/test_sync.py index 0646898983..e4177307fe 100644 --- a/src/trio/_tests/test_sync.py +++ b/src/trio/_tests/test_sync.py @@ -7,8 +7,6 @@ import pytest -from trio.testing import Matcher, RaisesGroup - from .. import _core from .._core._parking_lot import GLOBAL_PARKING_LOT_BREAKER from .._sync import * @@ -696,8 +694,8 @@ async def test_lock_multiple_acquire() -> None: see https://github.com/python-trio/trio/issues/3035""" assert not GLOBAL_PARKING_LOT_BREAKER lock = trio.Lock() - with RaisesGroup( - Matcher( + with pytest.RaisesGroup( + pytest.RaisesExc( trio.BrokenResourceError, match="^Owner of this lock exited without releasing: ", ), diff --git a/src/trio/_tests/test_testing.py b/src/trio/_tests/test_testing.py index e7c9a1a1a7..ca67b8bef7 100644 --- a/src/trio/_tests/test_testing.py +++ b/src/trio/_tests/test_testing.py @@ -6,8 +6,6 @@ import pytest -from trio.testing import RaisesGroup - from .. import _core, sleep, socket as tsocket from .._core._tests.tutil import can_bind_ipv6 from .._highlevel_generic import StapledStream, aclose_forcefully @@ -292,7 +290,7 @@ async def getter(expect: bytes) -> None: nursery.start_soon(putter, b"xyz") # Two gets at the same time -> BusyResourceError - with RaisesGroup(_core.BusyResourceError): + with pytest.RaisesGroup(_core.BusyResourceError): async with _core.open_nursery() as nursery: nursery.start_soon(getter, b"asdf") nursery.start_soon(getter, b"asdf") @@ -428,7 +426,7 @@ async def do_receive_some(max_bytes: int | None) -> bytes: mrs.put_data(b"abc") assert await do_receive_some(None) == b"abc" - with RaisesGroup(_core.BusyResourceError): + with pytest.RaisesGroup(_core.BusyResourceError): async with _core.open_nursery() as nursery: nursery.start_soon(do_receive_some, 10) nursery.start_soon(do_receive_some, 10) diff --git a/src/trio/_tests/test_testing_raisesgroup.py b/src/trio/_tests/test_testing_raisesgroup.py index 9cc9299382..ba453edbe8 100644 --- a/src/trio/_tests/test_testing_raisesgroup.py +++ b/src/trio/_tests/test_testing_raisesgroup.py @@ -7,7 +7,7 @@ import pytest import trio -from trio.testing import Matcher, RaisesGroup +from trio.testing import _Matcher as Matcher, _RaisesGroup as RaisesGroup from trio.testing._raises_group import repr_callable if sys.version_info < (3, 11): @@ -1107,8 +1107,22 @@ def test__ExceptionInfo(monkeypatch: pytest.MonkeyPatch) -> None: "ExceptionInfo", trio.testing._raises_group._ExceptionInfo, ) - with trio.testing.RaisesGroup(ValueError) as excinfo: + with RaisesGroup(ValueError) as excinfo: raise ExceptionGroup("", (ValueError("hello"),)) assert excinfo.type is ExceptionGroup assert excinfo.value.exceptions[0].args == ("hello",) assert isinstance(excinfo.tb, TracebackType) + + +def test_raisesgroup_matcher_deprecation() -> None: + with pytest.deprecated_call(): + trio.testing.Matcher # type: ignore # noqa: B018 + + with pytest.deprecated_call(): + trio.testing.RaisesGroup # type: ignore # noqa: B018 + + with pytest.deprecated_call(): + from trio.testing import Matcher # type: ignore # noqa: F401 + + with pytest.deprecated_call(): + from trio.testing import RaisesGroup # type: ignore # noqa: F401 diff --git a/src/trio/_tests/test_util.py b/src/trio/_tests/test_util.py index 69310d64e7..c8beefa1ca 100644 --- a/src/trio/_tests/test_util.py +++ b/src/trio/_tests/test_util.py @@ -9,7 +9,7 @@ import pytest import trio -from trio.testing import Matcher, RaisesGroup +from trio.testing import _Matcher as Matcher, _RaisesGroup as RaisesGroup from .. import _core from .._core._tests.tutil import ( diff --git a/src/trio/_tests/type_tests/raisesgroup.py b/src/trio/_tests/type_tests/raisesgroup.py deleted file mode 100644 index 012c42b4d8..0000000000 --- a/src/trio/_tests/type_tests/raisesgroup.py +++ /dev/null @@ -1,223 +0,0 @@ -from __future__ import annotations - -import sys -from collections.abc import Callable - -from trio.testing import Matcher, RaisesGroup -from typing_extensions import assert_type - -if sys.version_info < (3, 11): - from exceptiongroup import BaseExceptionGroup, ExceptionGroup - -# split into functions to isolate the different scopes - - -def check_matcher_typevar_default(e: Matcher) -> None: - assert e.exception_type is not None - _exc: type[BaseException] = e.exception_type - # this would previously pass, as the type would be `Any` - e.exception_type().blah() # type: ignore - - -def check_basic_contextmanager() -> None: - with RaisesGroup(ValueError) as e: - raise ExceptionGroup("foo", (ValueError(),)) - assert_type(e.value, ExceptionGroup[ValueError]) - - -def check_basic_matches() -> None: - # check that matches gets rid of the naked ValueError in the union - exc: ExceptionGroup[ValueError] | ValueError = ExceptionGroup("", (ValueError(),)) - if RaisesGroup(ValueError).matches(exc): - assert_type(exc, ExceptionGroup[ValueError]) - - # also check that BaseExceptionGroup shows up for BaseExceptions - if RaisesGroup(KeyboardInterrupt).matches(exc): - assert_type(exc, BaseExceptionGroup[KeyboardInterrupt]) - - -def check_matches_with_different_exception_type() -> None: - e: BaseExceptionGroup[KeyboardInterrupt] = BaseExceptionGroup( - "", - (KeyboardInterrupt(),), - ) - - # note: it might be tempting to have this warn. - # however, that isn't possible with current typing - if RaisesGroup(ValueError).matches(e): - assert_type(e, ExceptionGroup[ValueError]) - - -def check_matcher_init() -> None: - def check_exc(exc: BaseException) -> bool: - return isinstance(exc, ValueError) - - # Check various combinations of constructor signatures. - # At least 1 arg must be provided. - Matcher() # type: ignore - Matcher(ValueError) - Matcher(ValueError, "regex") - Matcher(ValueError, "regex", check_exc) - Matcher(exception_type=ValueError) - Matcher(match="regex") - Matcher(check=check_exc) - Matcher(ValueError, match="regex") - Matcher(match="regex", check=check_exc) - - def check_filenotfound(exc: FileNotFoundError) -> bool: - return not exc.filename.endswith(".tmp") - - # If exception_type is provided, that narrows the `check` method's argument. - Matcher(FileNotFoundError, check=check_filenotfound) - Matcher(ValueError, check=check_filenotfound) # type: ignore - Matcher(check=check_filenotfound) # type: ignore - Matcher(FileNotFoundError, match="regex", check=check_filenotfound) - - -def raisesgroup_check_type_narrowing() -> None: - """Check type narrowing on the `check` argument to `RaisesGroup`. - All `type: ignore`s are correctly pointing out type errors. - """ - - def handle_exc(e: BaseExceptionGroup[BaseException]) -> bool: - return True - - def handle_kbi(e: BaseExceptionGroup[KeyboardInterrupt]) -> bool: - return True - - def handle_value(e: BaseExceptionGroup[ValueError]) -> bool: - return True - - RaisesGroup(BaseException, check=handle_exc) - RaisesGroup(BaseException, check=handle_kbi) # type: ignore - - RaisesGroup(Exception, check=handle_exc) - RaisesGroup(Exception, check=handle_value) # type: ignore - - RaisesGroup(KeyboardInterrupt, check=handle_exc) - RaisesGroup(KeyboardInterrupt, check=handle_kbi) - RaisesGroup(KeyboardInterrupt, check=handle_value) # type: ignore - - RaisesGroup(ValueError, check=handle_exc) - RaisesGroup(ValueError, check=handle_kbi) # type: ignore - RaisesGroup(ValueError, check=handle_value) - - RaisesGroup(ValueError, KeyboardInterrupt, check=handle_exc) - RaisesGroup(ValueError, KeyboardInterrupt, check=handle_kbi) # type: ignore - RaisesGroup(ValueError, KeyboardInterrupt, check=handle_value) # type: ignore - - -def raisesgroup_narrow_baseexceptiongroup() -> None: - """Check type narrowing specifically for the container exceptiongroup.""" - - def handle_group(e: ExceptionGroup[Exception]) -> bool: - return True - - def handle_group_value(e: ExceptionGroup[ValueError]) -> bool: - return True - - RaisesGroup(ValueError, check=handle_group_value) - - RaisesGroup(Exception, check=handle_group) - - -def check_matcher_transparent() -> None: - with RaisesGroup(Matcher(ValueError)) as e: - ... - _: BaseExceptionGroup[ValueError] = e.value - assert_type(e.value, ExceptionGroup[ValueError]) - - -def check_nested_raisesgroups_contextmanager() -> None: - with RaisesGroup(RaisesGroup(ValueError)) as excinfo: - raise ExceptionGroup("foo", (ValueError(),)) - - _: BaseExceptionGroup[BaseExceptionGroup[ValueError]] = excinfo.value - - assert_type( - excinfo.value, - ExceptionGroup[ExceptionGroup[ValueError]], - ) - - assert_type( - excinfo.value.exceptions[0], - # this union is because of how typeshed defines .exceptions - ExceptionGroup[ValueError] | ExceptionGroup[ExceptionGroup[ValueError]], - ) - - -def check_nested_raisesgroups_matches() -> None: - """Check nested RaisesGroups with .matches""" - exc: ExceptionGroup[ExceptionGroup[ValueError]] = ExceptionGroup( - "", - (ExceptionGroup("", (ValueError(),)),), - ) - - if RaisesGroup(RaisesGroup(ValueError)).matches(exc): - assert_type(exc, ExceptionGroup[ExceptionGroup[ValueError]]) - - -def check_multiple_exceptions_1() -> None: - a = RaisesGroup(ValueError, ValueError) - b = RaisesGroup(Matcher(ValueError), Matcher(ValueError)) - c = RaisesGroup(ValueError, Matcher(ValueError)) - - d: RaisesGroup[ValueError] - d = a - d = b - d = c - assert isinstance(d, RaisesGroup) - - -def check_multiple_exceptions_2() -> None: - # This previously failed due to lack of covariance in the TypeVar - a = RaisesGroup(Matcher(ValueError), Matcher(TypeError)) - b = RaisesGroup(Matcher(ValueError), TypeError) - c = RaisesGroup(ValueError, TypeError) - - d: RaisesGroup[Exception] - d = a - d = b - d = c - assert isinstance(d, RaisesGroup) - - -def check_raisesgroup_overloads() -> None: - # allow_unwrapped=True does not allow: - # multiple exceptions - RaisesGroup(ValueError, TypeError, allow_unwrapped=True) # type: ignore - # nested RaisesGroup - RaisesGroup(RaisesGroup(ValueError), allow_unwrapped=True) # type: ignore - # specifying match - RaisesGroup(ValueError, match="foo", allow_unwrapped=True) # type: ignore - # specifying check - RaisesGroup(ValueError, check=bool, allow_unwrapped=True) # type: ignore - # allowed variants - RaisesGroup(ValueError, allow_unwrapped=True) - RaisesGroup(ValueError, allow_unwrapped=True, flatten_subgroups=True) - RaisesGroup(Matcher(ValueError), allow_unwrapped=True) - - # flatten_subgroups=True does not allow nested RaisesGroup - RaisesGroup(RaisesGroup(ValueError), flatten_subgroups=True) # type: ignore - # but rest is plenty fine - RaisesGroup(ValueError, TypeError, flatten_subgroups=True) - RaisesGroup(ValueError, match="foo", flatten_subgroups=True) - RaisesGroup(ValueError, check=bool, flatten_subgroups=True) - RaisesGroup(ValueError, flatten_subgroups=True) - RaisesGroup(Matcher(ValueError), flatten_subgroups=True) - - # if they're both false we can of course specify nested raisesgroup - RaisesGroup(RaisesGroup(ValueError)) - - -def check_triple_nested_raisesgroup() -> None: - with RaisesGroup(RaisesGroup(RaisesGroup(ValueError))) as e: - assert_type(e.value, ExceptionGroup[ExceptionGroup[ExceptionGroup[ValueError]]]) - - -def check_check_typing() -> None: - # `BaseExceptiongroup` should perhaps be `ExceptionGroup`, but close enough - assert_type( - RaisesGroup(ValueError).check, - Callable[[BaseExceptionGroup[ValueError]], bool] | None, - ) diff --git a/src/trio/testing/__init__.py b/src/trio/testing/__init__.py index d93d33aab7..a8f323447e 100644 --- a/src/trio/testing/__init__.py +++ b/src/trio/testing/__init__.py @@ -1,5 +1,6 @@ # Uses `from x import y as y` for compatibility with `pyright --verifytypes` (#2625) +from .. import _deprecate as _deprecate from .._core import ( MockClock as MockClock, wait_all_tasks_blocked as wait_all_tasks_blocked, @@ -28,12 +29,30 @@ memory_stream_pump as memory_stream_pump, ) from ._network import open_stream_to_socket_listener as open_stream_to_socket_listener -from ._raises_group import Matcher as Matcher, RaisesGroup as RaisesGroup +from ._raises_group import Matcher as _Matcher, RaisesGroup as _RaisesGroup from ._sequencer import Sequencer as Sequencer from ._trio_test import trio_test as trio_test ################################################################ +_deprecate.deprecate_attributes( + __name__, + { + "RaisesGroup": _deprecate.DeprecatedAttribute( + _RaisesGroup, + version="0.33.0", + issue=3326, + instead="See https://docs.pytest.org/en/stable/reference/reference.html#pytest.RaisesGroup", + ), + "Matcher": _deprecate.DeprecatedAttribute( + _Matcher, + version="0.33.0", + issue=3326, + instead="See https://docs.pytest.org/en/stable/reference/reference.html#pytest.RaisesExc", + ), + }, +) + fixup_module_metadata(__name__, globals()) del fixup_module_metadata From fc8c19950fceb43286af8017b8ce3be927f94a37 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 07:11:15 +0900 Subject: [PATCH 24/36] Bump dependencies from commit 4f905c (#3374) * Dependency updates * Try working around package installation issues * Actually, mypy uses 3.10 * Update ci.yml for tox changes * Update things for Sphinx version we use * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Oops * More changes... * ... --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: A5rocks Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 9 +++++++++ .pre-commit-config.yaml | 4 ++-- docs-requirements.txt | 8 +++++--- docs/source/conf.py | 24 +++++++++++++++++------- test-requirements.txt | 26 ++++++++++++++------------ tox.ini | 3 +-- 6 files changed, 48 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e84e47c07..2427c39974 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -281,12 +281,21 @@ jobs: cache: pip cache-dependency-path: test-requirements.txt allow-prereleases: true + - name: Setup minimum Python version + if: matrix.check_formatting == '1' + uses: actions/setup-python@v6 + with: + python-version: '3.10' + cache: pip + cache-dependency-path: test-requirements.txt + allow-prereleases: true - name: Check Formatting if: matrix.check_formatting == '1' run: python -m pip install tox && tox -m check - name: Install python3-apport + if: matrix.check_formatting == '0' run: | sudo apt update sudo apt install -q python3-apport diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3b1369f7dd..93a60033b5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,7 +38,7 @@ repos: # tomli needed on 3.10. tomllib is available in stdlib on 3.11+ - tomli - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.40.0 + rev: v1.40.1 hooks: - id: typos - repo: https://github.com/sphinx-contrib/sphinx-lint @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.18 + rev: 0.9.21 hooks: # Compile requirements - id: pip-compile diff --git a/docs-requirements.txt b/docs-requirements.txt index b350e9be1d..b14ca4a38b 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -59,7 +59,9 @@ pyopenssl==25.3.0 # via -r docs-requirements.in requests==2.32.5 # via sphinx -roman-numerals-py==3.1.0 +roman-numerals==4.1.0 + # via roman-numerals-py +roman-numerals-py==4.1.0 # via sphinx sniffio==1.3.1 # via -r docs-requirements.in @@ -67,7 +69,7 @@ snowballstemmer==3.0.1 # via sphinx sortedcontainers==2.4.0 # via -r docs-requirements.in -soupsieve==2.8 +soupsieve==2.8.1 # via beautifulsoup4 sphinx==8.2.3 # via @@ -105,5 +107,5 @@ typing-extensions==4.15.0 # beautifulsoup4 # exceptiongroup # pyopenssl -urllib3==2.6.1 +urllib3==2.6.2 # via requests diff --git a/docs/source/conf.py b/docs/source/conf.py index ab0d1e5059..5017f3895e 100755 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -25,7 +25,9 @@ from pathlib import Path from typing import TYPE_CHECKING, cast -from sphinx.util.inventory import _InventoryItem +if sys.version_info >= (3, 11): + from sphinx.util.inventory import _InventoryItem + from sphinx.util.logging import getLogger if TYPE_CHECKING: @@ -280,12 +282,20 @@ def add_mapping( assert isinstance(inventory, dict) inventory = cast("Inventory", inventory) - inventory[f"py:{reftype}"][f"{target}"] = _InventoryItem( - project_name="Python", - project_version=version, - uri=f"https://docs.python.org/{url_version}/library/{library}.html/{obj}", - display_name="-", - ) + if sys.version_info >= (3, 11): + inventory[f"py:{reftype}"][f"{target}"] = _InventoryItem( + project_name="Python", + project_version=version, + uri=f"https://docs.python.org/{url_version}/library/{library}.html/{obj}", + display_name="-", + ) + else: + inventory[f"py:{reftype}"][f"{target}"] = ( + "Python", + version, + f"https://docs.python.org/{url_version}/library/{library}.html/{obj}", + "-", + ) # This has been removed in Py3.12, so add a link to the 3.11 version with deprecation warnings. add_mapping("method", "pathlib", "Path.link_to", "3.11") diff --git a/test-requirements.txt b/test-requirements.txt index 346de1f3cd..d140ccb74f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -36,7 +36,7 @@ colorama==0.4.6 ; sys_platform == 'win32' # pylint # pytest # sphinx -coverage==7.13.0 +coverage==7.13.1 # via -r test-requirements.in cryptography==46.0.3 # via @@ -50,13 +50,13 @@ distlib==0.4.0 # via virtualenv docutils==0.21.2 ; python_full_version < '3.11' # via sphinx -docutils==0.22.3 ; python_full_version >= '3.11' +docutils==0.22.4 ; python_full_version >= '3.11' # via sphinx exceptiongroup==1.3.1 ; python_full_version < '3.11' # via # -r test-requirements.in # pytest -filelock==3.20.0 +filelock==3.20.1 # via virtualenv identify==2.6.15 # via pre-commit @@ -75,20 +75,20 @@ jedi==0.19.2 ; implementation_name == 'cpython' # via -r test-requirements.in jinja2==3.1.6 # via sphinx -librt==0.7.3 ; implementation_name == 'cpython' +librt==0.7.5 ; implementation_name == 'cpython' and platform_python_implementation != 'PyPy' # via mypy markupsafe==3.0.3 # via jinja2 mccabe==0.7.0 # via pylint -mypy==1.19.0 ; implementation_name == 'cpython' +mypy==1.19.1 ; implementation_name == 'cpython' # via -r test-requirements.in mypy-extensions==1.1.0 # via # -r test-requirements.in # black # mypy -nodeenv==1.9.1 +nodeenv==1.10.0 # via # pre-commit # pyright @@ -112,7 +112,7 @@ platformdirs==4.5.1 # virtualenv pluggy==1.6.0 # via pytest -pre-commit==4.5.0 +pre-commit==4.5.1 # via -r test-requirements.in pycparser==2.23 ; (implementation_name != 'PyPy' and os_name == 'nt') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi @@ -134,7 +134,7 @@ pyyaml==6.0.3 # via pre-commit requests==2.32.5 # via sphinx -roman-numerals==3.1.0 ; python_full_version >= '3.11' +roman-numerals==4.1.0 ; python_full_version >= '3.11' # via sphinx ruff==0.14.10 # via -r test-requirements.in @@ -146,7 +146,9 @@ sortedcontainers==2.4.0 # via -r test-requirements.in sphinx==8.1.3 ; python_full_version < '3.11' # via -r test-requirements.in -sphinx==9.0.4 ; python_full_version >= '3.11' +sphinx==9.0.4 ; python_full_version == '3.11.*' + # via -r test-requirements.in +sphinx==9.1.0 ; python_full_version >= '3.12' # via -r test-requirements.in sphinxcontrib-applehelp==2.0.0 # via sphinx @@ -181,7 +183,7 @@ types-pyopenssl==24.1.0.20240722 # via -r test-requirements.in types-pyyaml==6.0.12.20250915 # via -r test-requirements.in -types-setuptools==80.9.0.20250822 +types-setuptools==80.9.0.20251223 # via types-cffi typing-extensions==4.15.0 # via @@ -194,9 +196,9 @@ typing-extensions==4.15.0 # pyopenssl # pyright # virtualenv -urllib3==2.6.1 +urllib3==2.6.2 # via requests -uv==0.9.18 +uv==0.9.21 # via -r test-requirements.in virtualenv==20.35.4 # via pre-commit diff --git a/tox.ini b/tox.ini index 6b1851f4de..11bd09d0ca 100644 --- a/tox.ini +++ b/tox.ini @@ -97,13 +97,12 @@ base_python = 3.13 commands = pre-commit run pip-compile --all-files -# TODO: allow specifying e.g. typing-3.11 to run with --python[-]version=3.11 [testenv:typing] description = "Run type checks: mypy on all platforms, and pyright on `src/trio[/_core]/_tests/type_tests/`." deps = -r test-requirements.txt exceptiongroup -base_python = 3.13 +base_python = 3.10 set_env = PYRIGHT_PYTHON_IGNORE_WARNINGS=1 commands = From befe650a621a5d990c66c7bcb6756deb547ccd14 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 22:25:31 +0000 Subject: [PATCH 25/36] Dependency updates (#3376) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- docs-requirements.txt | 2 +- test-requirements.txt | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 93a60033b5..93780e4dec 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,7 +38,7 @@ repos: # tomli needed on 3.10. tomllib is available in stdlib on 3.11+ - tomli - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.40.1 + rev: v1.41.0 hooks: - id: typos - repo: https://github.com/sphinx-contrib/sphinx-lint diff --git a/docs-requirements.txt b/docs-requirements.txt index b14ca4a38b..03883f178f 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -10,7 +10,7 @@ babel==2.17.0 # via sphinx beautifulsoup4==4.14.3 # via sphinx-codeautolink -certifi==2025.11.12 +certifi==2026.1.4 # via requests cffi==2.0.0 ; os_name == 'nt' or platform_python_implementation != 'PyPy' # via diff --git a/test-requirements.txt b/test-requirements.txt index d140ccb74f..e398ef8ddc 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,7 +4,7 @@ alabaster==1.0.0 # via sphinx astor==0.8.1 # via -r test-requirements.in -astroid==4.0.2 +astroid==4.0.3 # via pylint async-generator==1.10 # via -r test-requirements.in @@ -16,7 +16,7 @@ babel==2.17.0 # via sphinx black==25.12.0 ; implementation_name == 'cpython' # via -r test-requirements.in -certifi==2025.11.12 +certifi==2026.1.4 # via requests cffi==2.0.0 ; os_name == 'nt' or platform_python_implementation != 'PyPy' # via @@ -56,7 +56,7 @@ exceptiongroup==1.3.1 ; python_full_version < '3.11' # via # -r test-requirements.in # pytest -filelock==3.20.1 +filelock==3.20.2 # via virtualenv identify==2.6.15 # via pre-commit @@ -75,7 +75,7 @@ jedi==0.19.2 ; implementation_name == 'cpython' # via -r test-requirements.in jinja2==3.1.6 # via sphinx -librt==0.7.5 ; implementation_name == 'cpython' and platform_python_implementation != 'PyPy' +librt==0.7.7 ; implementation_name == 'cpython' and platform_python_implementation != 'PyPy' # via mypy markupsafe==3.0.3 # via jinja2 From 01243945fc3d46d4483acf486177389e59fdc6b6 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:58:34 +0000 Subject: [PATCH 26/36] [pre-commit.ci] pre-commit autoupdate (#3377) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate 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/adhtruong/mirrors-typos: v1.41.0 → v1.42.0](https://github.com/adhtruong/mirrors-typos/compare/v1.41.0...v1.42.0) - [github.com/woodruffw/zizmor-pre-commit: v1.19.0 → v1.20.0](https://github.com/woodruffw/zizmor-pre-commit/compare/v1.19.0...v1.20.0) - [github.com/astral-sh/uv-pre-commit: 0.9.21 → 0.9.24](https://github.com/astral-sh/uv-pre-commit/compare/0.9.21...0.9.24) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Ignore new ruff rule --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: A5rocks --- .pre-commit-config.yaml | 8 ++++---- pyproject.toml | 1 + test-requirements.txt | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 93780e4dec..973ed4279a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.10 + rev: v0.14.11 hooks: - id: ruff-check types: [file] @@ -38,7 +38,7 @@ repos: # tomli needed on 3.10. tomllib is available in stdlib on 3.11+ - tomli - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.41.0 + rev: v1.42.0 hooks: - id: typos - repo: https://github.com/sphinx-contrib/sphinx-lint @@ -46,7 +46,7 @@ repos: hooks: - id: sphinx-lint - repo: https://github.com/woodruffw/zizmor-pre-commit - rev: v1.19.0 + rev: v1.20.0 hooks: - id: zizmor - repo: local @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.21 + rev: 0.9.24 hooks: # Compile requirements - id: pip-compile diff --git a/pyproject.toml b/pyproject.toml index 7c0373e35f..b7c3189fb7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -155,6 +155,7 @@ extend-ignore = [ "PERF203", # try-except-in-loop (not always possible to refactor) "PT012", # multiple statements in pytest.raises block "SIM117", # multiple-with-statements (messes up lots of context-based stuff and looks bad) + "RUF067", # non-empty-init-module, since we need lots of logic in our __init__ # conflicts with formatter (ruff recommends these be disabled) "COM812", diff --git a/test-requirements.txt b/test-requirements.txt index e398ef8ddc..0bca01b222 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -136,7 +136,7 @@ requests==2.32.5 # via sphinx roman-numerals==4.1.0 ; python_full_version >= '3.11' # via sphinx -ruff==0.14.10 +ruff==0.14.11 # via -r test-requirements.in sniffio==1.3.1 # via -r test-requirements.in @@ -198,7 +198,7 @@ typing-extensions==4.15.0 # virtualenv urllib3==2.6.2 # via requests -uv==0.9.21 +uv==0.9.24 # via -r test-requirements.in virtualenv==20.35.4 # via pre-commit From 333dce2c86e0a98fc90796bc0a01c90e99eb558b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 20 Jan 2026 02:39:48 +0000 Subject: [PATCH 27/36] Bump dependencies from commit 012439 (#3381) * Dependency updates * Try upperbound for Sphinx * Manually lock docutils too * Final manual lockfile fixes --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: A5rocks --- .pre-commit-config.yaml | 8 +++--- docs-requirements.in | 4 +-- docs-requirements.txt | 6 ++-- src/trio/_tests/check_type_completeness.py | 1 + src/trio/_tests/test_deprecate.py | 20 +++---------- src/trio/_tests/test_fakenet.py | 10 +++---- src/trio/_tests/test_socket.py | 10 +++---- src/trio/_tests/test_unix_pipes.py | 2 +- src/trio/_tests/test_windows_pipes.py | 2 +- .../_tests/tools/test_sync_requirements.py | 5 +--- src/trio/_tools/gen_exports.py | 1 + test-requirements.txt | 28 +++++++++---------- 12 files changed, 42 insertions(+), 55 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 973ed4279a..7609432504 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,11 +20,11 @@ repos: - id: sort-simple-yaml files: .pre-commit-config.yaml - repo: https://github.com/psf/black-pre-commit-mirror - rev: 25.12.0 + rev: 26.1.0 hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.11 + rev: v0.14.13 hooks: - id: ruff-check types: [file] @@ -46,7 +46,7 @@ repos: hooks: - id: sphinx-lint - repo: https://github.com/woodruffw/zizmor-pre-commit - rev: v1.20.0 + rev: v1.22.0 hooks: - id: zizmor - repo: local @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.24 + rev: 0.9.26 hooks: # Compile requirements - id: pip-compile diff --git a/docs-requirements.in b/docs-requirements.in index d942c084dc..adf9304fbc 100644 --- a/docs-requirements.in +++ b/docs-requirements.in @@ -1,6 +1,6 @@ # RTD is currently installing 1.5.3, which has a bug in :lineno-match: (??) -# sphinx 5.3 doesn't work with our _NoValue workaround -sphinx >= 6.0 +# sphinx 5.3 doesn't work with our _NoValue workaround, 9.0 breaks sphinxcontrib-trio +sphinx >= 6.0,<9.0 jinja2 # >= is necessary to prevent `uv` from selecting a `Sphinx` version this does not support sphinx_rtd_theme >= 3 diff --git a/docs-requirements.txt b/docs-requirements.txt index 03883f178f..7ed83e4c48 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -69,7 +69,7 @@ snowballstemmer==3.0.1 # via sphinx sortedcontainers==2.4.0 # via -r docs-requirements.in -soupsieve==2.8.1 +soupsieve==2.8.2 # via beautifulsoup4 sphinx==8.2.3 # via @@ -80,7 +80,7 @@ sphinx==8.2.3 # sphinxcontrib-trio sphinx-codeautolink==0.17.5 # via -r docs-requirements.in -sphinx-rtd-theme==3.0.2 +sphinx-rtd-theme==3.1.0 # via -r docs-requirements.in sphinxcontrib-applehelp==2.0.0 # via sphinx @@ -107,5 +107,5 @@ typing-extensions==4.15.0 # beautifulsoup4 # exceptiongroup # pyopenssl -urllib3==2.6.2 +urllib3==2.6.3 # via requests diff --git a/src/trio/_tests/check_type_completeness.py b/src/trio/_tests/check_type_completeness.py index ef5bdaafa4..a0f23a66e9 100755 --- a/src/trio/_tests/check_type_completeness.py +++ b/src/trio/_tests/check_type_completeness.py @@ -7,6 +7,7 @@ If this check is giving you false alarms, you can ignore them by adding logic to `has_docstring_at_runtime`, in the main loop in `check_type`, or by updating the json file. """ + from __future__ import annotations # this file is not run as part of the tests, instead it's run standalone from check.sh diff --git a/src/trio/_tests/test_deprecate.py b/src/trio/_tests/test_deprecate.py index 4786b06c96..b7614d0e52 100644 --- a/src/trio/_tests/test_deprecate.py +++ b/src/trio/_tests/test_deprecate.py @@ -200,45 +200,33 @@ def docstring_test4() -> None: # pragma: no cover def test_deprecated_docstring_munging() -> None: - assert ( - docstring_test1.__doc__ - == """Hello! + assert docstring_test1.__doc__ == """Hello! .. deprecated:: 2.1 Use hi instead. For details, see `issue #1 `__. """ - ) - assert ( - docstring_test2.__doc__ - == """Hello! + assert docstring_test2.__doc__ == """Hello! .. deprecated:: 2.1 Use hi instead. """ - ) - assert ( - docstring_test3.__doc__ - == """Hello! + assert docstring_test3.__doc__ == """Hello! .. deprecated:: 2.1 For details, see `issue #1 `__. """ - ) - assert ( - docstring_test4.__doc__ - == """Hello! + assert docstring_test4.__doc__ == """Hello! .. deprecated:: 2.1 """ - ) def test_module_with_deprecations(recwarn_always: pytest.WarningsRecorder) -> None: diff --git a/src/trio/_tests/test_fakenet.py b/src/trio/_tests/test_fakenet.py index c7d21ad25b..12dc6a9088 100644 --- a/src/trio/_tests/test_fakenet.py +++ b/src/trio/_tests/test_fakenet.py @@ -98,9 +98,9 @@ async def test_recv_methods() -> None: buf = bytearray(10) with pytest.raises(NotImplementedError, match=r"^partial recvfrom_into$"): - (nbytes, addr) = await s2.recvfrom_into(buf, nbytes=2) + nbytes, addr = await s2.recvfrom_into(buf, nbytes=2) - (nbytes, addr) = await s2.recvfrom_into(buf) + nbytes, addr = await s2.recvfrom_into(buf) assert nbytes == 3 assert buf == b"ghi" + b"\x00" * 7 assert addr == s1.getsockname() @@ -154,7 +154,7 @@ async def test_nonwindows_functionality() -> None: assert exc.value.errno == errno.ENOTCONN assert await s1.sendmsg([b"jkl"], (), 0, s2.getsockname()) == 3 - (data, ancdata, msg_flags, addr) = await s2.recvmsg(10) + data, ancdata, msg_flags, addr = await s2.recvmsg(10) assert data == b"jkl" assert ancdata == [] assert msg_flags == 0 @@ -167,7 +167,7 @@ async def test_nonwindows_functionality() -> None: buf1 = bytearray(2) buf2 = bytearray(3) ret = await s2.recvmsg_into([buf1, buf2]) - (nbytes, ancdata, msg_flags, addr) = ret + nbytes, ancdata, msg_flags, addr = ret assert nbytes == 4 assert buf1 == b"xy" assert buf2 == b"zw" + b"\x00" @@ -179,7 +179,7 @@ async def test_nonwindows_functionality() -> None: assert await s1.sendto(b"xyzwv", s2.getsockname()) == 5 buf1 = bytearray(2) ret = await s2.recvmsg_into([buf1]) - (nbytes, ancdata, msg_flags, addr) = ret + nbytes, ancdata, msg_flags, addr = ret assert nbytes == 2 assert buf1 == b"xy" assert ancdata == [] diff --git a/src/trio/_tests/test_socket.py b/src/trio/_tests/test_socket.py index 2b8a3a5ee1..03918b0e1f 100644 --- a/src/trio/_tests/test_socket.py +++ b/src/trio/_tests/test_socket.py @@ -975,7 +975,7 @@ async def test_send_recv_variants() -> None: # recvfrom + sendto, with and without names for target in targets: assert await a.sendto(b"xxx", target) == 3 - (data, addr) = await b.recvfrom(10) + data, addr = await b.recvfrom(10) assert data == b"xxx" assert addr == a.getsockname() @@ -991,21 +991,21 @@ async def test_send_recv_variants() -> None: await a.sendto(b"xxx", tsocket.MSG_MORE, b.getsockname()) await a.sendto(b"yyy", tsocket.MSG_MORE, b.getsockname()) await a.sendto(b"zzz", b.getsockname()) - (data, addr) = await b.recvfrom(10) + data, addr = await b.recvfrom(10) assert data == b"xxxyyyzzz" assert addr == a.getsockname() # recvfrom_into assert await a.sendto(b"xxx", b.getsockname()) == 3 buf = bytearray(10) - (nbytes, addr) = await b.recvfrom_into(buf) + nbytes, addr = await b.recvfrom_into(buf) assert nbytes == 3 assert buf == b"xxx" + b"\x00" * 7 assert addr == a.getsockname() if hasattr(b, "recvmsg"): assert await a.sendto(b"xxx", b.getsockname()) == 3 - (data, ancdata, msg_flags, addr) = await b.recvmsg(10) + data, ancdata, msg_flags, addr = await b.recvmsg(10) assert data == b"xxx" assert ancdata == [] assert msg_flags == 0 @@ -1016,7 +1016,7 @@ async def test_send_recv_variants() -> None: buf1 = bytearray(2) buf2 = bytearray(3) ret = await b.recvmsg_into([buf1, buf2]) - (nbytes, ancdata, msg_flags, addr) = ret + nbytes, ancdata, msg_flags, addr = ret assert nbytes == 4 assert buf1 == b"xy" assert buf2 == b"zw" + b"\x00" diff --git a/src/trio/_tests/test_unix_pipes.py b/src/trio/_tests/test_unix_pipes.py index 4d1b08c06e..f81004049f 100644 --- a/src/trio/_tests/test_unix_pipes.py +++ b/src/trio/_tests/test_unix_pipes.py @@ -26,7 +26,7 @@ async def make_pipe() -> tuple[FdStream, FdStream]: """Makes a new pair of pipes.""" - (r, w) = os.pipe() + r, w = os.pipe() return FdStream(w), FdStream(r) diff --git a/src/trio/_tests/test_windows_pipes.py b/src/trio/_tests/test_windows_pipes.py index e42736d65d..2aee568156 100644 --- a/src/trio/_tests/test_windows_pipes.py +++ b/src/trio/_tests/test_windows_pipes.py @@ -24,7 +24,7 @@ async def make_pipe() -> tuple[PipeSendStream, PipeReceiveStream]: """Makes a new pair of pipes.""" - (r, w) = pipe() + r, w = pipe() return PipeSendStream(w), PipeReceiveStream(r) diff --git a/src/trio/_tests/tools/test_sync_requirements.py b/src/trio/_tests/tools/test_sync_requirements.py index db64d36eaa..b2acfdceb5 100644 --- a/src/trio/_tests/tools/test_sync_requirements.py +++ b/src/trio/_tests/tools/test_sync_requirements.py @@ -52,16 +52,13 @@ def test_update_requirements( encoding="utf-8", ) assert update_requirements(requirements_file, {"black": "3.1.5", "ruff": "1.2.7"}) - assert ( - requirements_file.read_text(encoding="utf-8") - == """# comment + assert requirements_file.read_text(encoding="utf-8") == """# comment # also comment but spaces line start waffles are delicious no equals black==3.1.5 ; specific version thingy mypy==1.15.0 ruff==1.2.7 # required by soupy cat""" - ) def test_update_requirements_no_changes( diff --git a/src/trio/_tools/gen_exports.py b/src/trio/_tools/gen_exports.py index e3d1659c02..1ec20e6bd8 100755 --- a/src/trio/_tools/gen_exports.py +++ b/src/trio/_tools/gen_exports.py @@ -3,6 +3,7 @@ Code generation script for class methods to be exported as public API """ + from __future__ import annotations import argparse diff --git a/test-requirements.txt b/test-requirements.txt index 0bca01b222..f7591ceab6 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -14,7 +14,7 @@ attrs==25.4.0 # outcome babel==2.17.0 # via sphinx -black==25.12.0 ; implementation_name == 'cpython' +black==26.1.0 ; implementation_name == 'cpython' # via -r test-requirements.in certifi==2026.1.4 # via requests @@ -44,7 +44,7 @@ cryptography==46.0.3 # pyopenssl # trustme # types-pyopenssl -dill==0.4.0 +dill==0.4.1 # via pylint distlib==0.4.0 # via virtualenv @@ -56,9 +56,9 @@ exceptiongroup==1.3.1 ; python_full_version < '3.11' # via # -r test-requirements.in # pytest -filelock==3.20.2 +filelock==3.20.3 # via virtualenv -identify==2.6.15 +identify==2.6.16 # via pre-commit idna==3.11 # via @@ -75,7 +75,7 @@ jedi==0.19.2 ; implementation_name == 'cpython' # via -r test-requirements.in jinja2==3.1.6 # via sphinx -librt==0.7.7 ; implementation_name == 'cpython' and platform_python_implementation != 'PyPy' +librt==0.7.8 ; implementation_name == 'cpython' and platform_python_implementation != 'PyPy' # via mypy markupsafe==3.0.3 # via jinja2 @@ -101,7 +101,7 @@ packaging==25.0 # sphinx parso==0.8.5 ; implementation_name == 'cpython' # via jedi -pathspec==0.12.1 ; implementation_name == 'cpython' +pathspec==1.0.3 ; implementation_name == 'cpython' # via # black # mypy @@ -124,11 +124,11 @@ pylint==4.0.4 # via -r test-requirements.in pyopenssl==25.3.0 # via -r test-requirements.in -pyright==1.1.407 +pyright==1.1.408 # via -r test-requirements.in pytest==9.0.2 # via -r test-requirements.in -pytokens==0.3.0 ; implementation_name == 'cpython' +pytokens==0.4.0 ; implementation_name == 'cpython' # via black pyyaml==6.0.3 # via pre-commit @@ -136,7 +136,7 @@ requests==2.32.5 # via sphinx roman-numerals==4.1.0 ; python_full_version >= '3.11' # via sphinx -ruff==0.14.11 +ruff==0.14.13 # via -r test-requirements.in sniffio==1.3.1 # via -r test-requirements.in @@ -162,14 +162,14 @@ sphinxcontrib-qthelp==2.0.0 # via sphinx sphinxcontrib-serializinghtml==2.0.0 # via sphinx -tomli==2.3.0 ; python_full_version < '3.11' +tomli==2.4.0 ; python_full_version < '3.11' # via # black # mypy # pylint # pytest # sphinx -tomlkit==0.13.3 +tomlkit==0.14.0 # via pylint trustme==1.2.1 # via -r test-requirements.in @@ -196,9 +196,9 @@ typing-extensions==4.15.0 # pyopenssl # pyright # virtualenv -urllib3==2.6.2 +urllib3==2.6.3 # via requests -uv==0.9.24 +uv==0.9.26 # via -r test-requirements.in -virtualenv==20.35.4 +virtualenv==20.36.1 # via pre-commit From 38052123b8a8361368aa5fac7dba80a8aedf5e46 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 21:47:54 +0000 Subject: [PATCH 28/36] [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.13 → v0.14.14](https://github.com/astral-sh/ruff-pre-commit/compare/v0.14.13...v0.14.14) - [github.com/adhtruong/mirrors-typos: v1.42.0 → v1.42.1](https://github.com/adhtruong/mirrors-typos/compare/v1.42.0...v1.42.1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7609432504..45af9d17cb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.13 + rev: v0.14.14 hooks: - id: ruff-check types: [file] @@ -38,7 +38,7 @@ repos: # tomli needed on 3.10. tomllib is available in stdlib on 3.11+ - tomli - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.42.0 + rev: v1.42.1 hooks: - id: typos - repo: https://github.com/sphinx-contrib/sphinx-lint From a0e845c45fc8782aaf331a2a711b38cc6e3fca6d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 21:48:22 +0000 Subject: [PATCH 29/36] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- test-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-requirements.txt b/test-requirements.txt index f7591ceab6..2b88ae18be 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -136,7 +136,7 @@ requests==2.32.5 # via sphinx roman-numerals==4.1.0 ; python_full_version >= '3.11' # via sphinx -ruff==0.14.13 +ruff==0.14.14 # via -r test-requirements.in sniffio==1.3.1 # via -r test-requirements.in From 21250b16b52826ca1b6a38a8f08ecd40974c2c22 Mon Sep 17 00:00:00 2001 From: A5rocks Date: Thu, 29 Jan 2026 16:38:31 +0900 Subject: [PATCH 30/36] Update to Sphinx 9 --- docs-requirements.in | 4 ++-- docs-requirements.txt | 17 +++++++++++------ src/trio/_core/_generated_run.py | 4 ++-- src/trio/_core/_run.py | 5 ++++- src/trio/_tools/gen_exports.py | 2 +- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/docs-requirements.in b/docs-requirements.in index adf9304fbc..d942c084dc 100644 --- a/docs-requirements.in +++ b/docs-requirements.in @@ -1,6 +1,6 @@ # RTD is currently installing 1.5.3, which has a bug in :lineno-match: (??) -# sphinx 5.3 doesn't work with our _NoValue workaround, 9.0 breaks sphinxcontrib-trio -sphinx >= 6.0,<9.0 +# sphinx 5.3 doesn't work with our _NoValue workaround +sphinx >= 6.0 jinja2 # >= is necessary to prevent `uv` from selecting a `Sphinx` version this does not support sphinx_rtd_theme >= 3 diff --git a/docs-requirements.txt b/docs-requirements.txt index 7ed83e4c48..0d6931e391 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -26,7 +26,7 @@ colorama==0.4.6 ; sys_platform == 'win32' # sphinx cryptography==46.0.3 # via pyopenssl -docutils==0.21.2 +docutils==0.22.4 # via # sphinx # sphinx-rtd-theme @@ -49,7 +49,7 @@ markupsafe==3.0.3 # via jinja2 outcome==1.3.0.post0 # via -r docs-requirements.in -packaging==25.0 +packaging==26.0 # via sphinx pycparser==2.23 ; (implementation_name != 'PyPy' and os_name == 'nt') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi @@ -60,8 +60,6 @@ pyopenssl==25.3.0 requests==2.32.5 # via sphinx roman-numerals==4.1.0 - # via roman-numerals-py -roman-numerals-py==4.1.0 # via sphinx sniffio==1.3.1 # via -r docs-requirements.in @@ -71,7 +69,14 @@ sortedcontainers==2.4.0 # via -r docs-requirements.in soupsieve==2.8.2 # via beautifulsoup4 -sphinx==8.2.3 +sphinx==9.0.4 ; python_full_version < '3.12' + # via + # -r docs-requirements.in + # sphinx-codeautolink + # sphinx-rtd-theme + # sphinxcontrib-jquery + # sphinxcontrib-trio +sphinx==9.1.0 ; python_full_version >= '3.12' # via # -r docs-requirements.in # sphinx-codeautolink @@ -98,7 +103,7 @@ sphinxcontrib-qthelp==2.0.0 # via sphinx sphinxcontrib-serializinghtml==2.0.0 # via sphinx -sphinxcontrib-trio==1.1.2 +sphinxcontrib-trio==1.2.0 # via -r docs-requirements.in towncrier==25.8.0 # via -r docs-requirements.in diff --git a/src/trio/_core/_generated_run.py b/src/trio/_core/_generated_run.py index db1454e6c7..4da520109b 100644 --- a/src/trio/_core/_generated_run.py +++ b/src/trio/_core/_generated_run.py @@ -12,7 +12,7 @@ import contextvars from collections.abc import Awaitable, Callable - from outcome import Outcome + import outcome from typing_extensions import Unpack from .._abc import Clock @@ -102,7 +102,7 @@ def current_root_task() -> Task | None: @enable_ki_protection -def reschedule(task: Task, next_send: Outcome[object] = _NO_SEND) -> None: +def reschedule(task: Task, next_send: outcome.Outcome[object] = _NO_SEND) -> None: """Reschedule the given task with the given :class:`outcome.Outcome`. diff --git a/src/trio/_core/_run.py b/src/trio/_core/_run.py index 8a7ddc1dc3..9ecdada1e0 100644 --- a/src/trio/_core/_run.py +++ b/src/trio/_core/_run.py @@ -25,6 +25,7 @@ ) import attrs +import outcome from outcome import Error, Outcome, Value, capture from sniffio import thread_local as sniffio_library from sortedcontainers import SortedDict @@ -1903,7 +1904,9 @@ def current_root_task(self) -> Task | None: ################ @_public - def reschedule(self, task: Task, next_send: Outcome[object] = _NO_SEND) -> None: + def reschedule( + self, task: Task, next_send: outcome.Outcome[object] = _NO_SEND + ) -> None: """Reschedule the given task with the given :class:`outcome.Outcome`. diff --git a/src/trio/_tools/gen_exports.py b/src/trio/_tools/gen_exports.py index 1ec20e6bd8..d1b5deb844 100755 --- a/src/trio/_tools/gen_exports.py +++ b/src/trio/_tools/gen_exports.py @@ -348,7 +348,7 @@ def main() -> None: # pragma: no cover from collections.abc import Awaitable, Callable from typing import Any, TYPE_CHECKING -from outcome import Outcome +import outcome import contextvars from ._run import _NO_SEND, RunStatistics, Task From 95d92051c5acf71d717c77c7c996d4d0db3ab952 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 1 Feb 2026 01:14:40 +0000 Subject: [PATCH 31/36] Dependency updates (#3386) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- docs-requirements.txt | 10 +++++----- test-requirements.txt | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 45af9d17cb..83ad2579b4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,7 +38,7 @@ repos: # tomli needed on 3.10. tomllib is available in stdlib on 3.11+ - tomli - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.42.1 + rev: v1.42.3 hooks: - id: typos - repo: https://github.com/sphinx-contrib/sphinx-lint @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.26 + rev: 0.9.28 hooks: # Compile requirements - id: pip-compile diff --git a/docs-requirements.txt b/docs-requirements.txt index 7ed83e4c48..7aa7c5afd1 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -24,7 +24,7 @@ colorama==0.4.6 ; sys_platform == 'win32' # via # click # sphinx -cryptography==46.0.3 +cryptography==46.0.4 # via pyopenssl docutils==0.21.2 # via @@ -49,9 +49,9 @@ markupsafe==3.0.3 # via jinja2 outcome==1.3.0.post0 # via -r docs-requirements.in -packaging==25.0 +packaging==26.0 # via sphinx -pycparser==2.23 ; (implementation_name != 'PyPy' and os_name == 'nt') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') +pycparser==3.0 ; (implementation_name != 'PyPy' and os_name == 'nt') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi pygments==2.19.2 # via sphinx @@ -69,7 +69,7 @@ snowballstemmer==3.0.1 # via sphinx sortedcontainers==2.4.0 # via -r docs-requirements.in -soupsieve==2.8.2 +soupsieve==2.8.3 # via beautifulsoup4 sphinx==8.2.3 # via @@ -98,7 +98,7 @@ sphinxcontrib-qthelp==2.0.0 # via sphinx sphinxcontrib-serializinghtml==2.0.0 # via sphinx -sphinxcontrib-trio==1.1.2 +sphinxcontrib-trio==1.2.0 # via -r docs-requirements.in towncrier==25.8.0 # via -r docs-requirements.in diff --git a/test-requirements.txt b/test-requirements.txt index 2b88ae18be..ab42b30cd0 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -36,9 +36,9 @@ colorama==0.4.6 ; sys_platform == 'win32' # pylint # pytest # sphinx -coverage==7.13.1 +coverage==7.13.2 # via -r test-requirements.in -cryptography==46.0.3 +cryptography==46.0.4 # via # -r test-requirements.in # pyopenssl @@ -94,14 +94,14 @@ nodeenv==1.10.0 # pyright outcome==1.3.0.post0 # via -r test-requirements.in -packaging==25.0 +packaging==26.0 # via # black # pytest # sphinx parso==0.8.5 ; implementation_name == 'cpython' # via jedi -pathspec==1.0.3 ; implementation_name == 'cpython' +pathspec==1.0.4 ; implementation_name == 'cpython' # via # black # mypy @@ -114,7 +114,7 @@ pluggy==1.6.0 # via pytest pre-commit==4.5.1 # via -r test-requirements.in -pycparser==2.23 ; (implementation_name != 'PyPy' and os_name == 'nt') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') +pycparser==3.0 ; (implementation_name != 'PyPy' and os_name == 'nt') or (implementation_name != 'PyPy' and platform_python_implementation != 'PyPy') # via cffi pygments==2.19.2 # via @@ -128,7 +128,7 @@ pyright==1.1.408 # via -r test-requirements.in pytest==9.0.2 # via -r test-requirements.in -pytokens==0.4.0 ; implementation_name == 'cpython' +pytokens==0.4.1 ; implementation_name == 'cpython' # via black pyyaml==6.0.3 # via pre-commit @@ -183,7 +183,7 @@ types-pyopenssl==24.1.0.20240722 # via -r test-requirements.in types-pyyaml==6.0.12.20250915 # via -r test-requirements.in -types-setuptools==80.9.0.20251223 +types-setuptools==80.10.0.20260124 # via types-cffi typing-extensions==4.15.0 # via @@ -198,7 +198,7 @@ typing-extensions==4.15.0 # virtualenv urllib3==2.6.3 # via requests -uv==0.9.26 +uv==0.9.28 # via -r test-requirements.in virtualenv==20.36.1 # via pre-commit From 1ee96c8d84718cf32ef607a08b9069c8c2cdcc9f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 01:04:57 +0000 Subject: [PATCH 32/36] [pre-commit.ci] pre-commit autoupdate (#3387) --- .pre-commit-config.yaml | 6 +++--- src/trio/_highlevel_open_tcp_stream.py | 2 +- test-requirements.txt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 83ad2579b4..1688604b03 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.14.14 + rev: v0.15.0 hooks: - id: ruff-check types: [file] @@ -38,7 +38,7 @@ repos: # tomli needed on 3.10. tomllib is available in stdlib on 3.11+ - tomli - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.42.3 + rev: v1.43.3 hooks: - id: typos - repo: https://github.com/sphinx-contrib/sphinx-lint @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.9.28 + rev: 0.10.0 hooks: # Compile requirements - id: pip-compile diff --git a/src/trio/_highlevel_open_tcp_stream.py b/src/trio/_highlevel_open_tcp_stream.py index 1787f4a97e..33f1e86025 100644 --- a/src/trio/_highlevel_open_tcp_stream.py +++ b/src/trio/_highlevel_open_tcp_stream.py @@ -162,7 +162,7 @@ def format_host_port(host: str | bytes, port: int | str) -> str: return f"{host}:{port}" -# Twisted's HostnameEndpoint has a good set of configurables: +# Twisted's HostnameEndpoint has a good set of configurations: # https://twistedmatrix.com/documents/current/api/twisted.internet.endpoints.HostnameEndpoint.html # # - per-connection timeout diff --git a/test-requirements.txt b/test-requirements.txt index ab42b30cd0..f752690ded 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -136,7 +136,7 @@ requests==2.32.5 # via sphinx roman-numerals==4.1.0 ; python_full_version >= '3.11' # via sphinx -ruff==0.14.14 +ruff==0.15.0 # via -r test-requirements.in sniffio==1.3.1 # via -r test-requirements.in @@ -198,7 +198,7 @@ typing-extensions==4.15.0 # virtualenv urllib3==2.6.3 # via requests -uv==0.9.28 +uv==0.10.0 # via -r test-requirements.in virtualenv==20.36.1 # via pre-commit From bc94ce138fa81f460f0d65f38a03cf3db04e6fe3 Mon Sep 17 00:00:00 2001 From: Graeme Holliday Date: Tue, 10 Feb 2026 11:36:33 -0500 Subject: [PATCH 33/36] Add coredis and streaQ libraries to documentation Adds two new projects to the library docs which cover a previously lacking area of the Trio ecosystem: Redis support. --- docs/source/awesome-trio-libraries.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/awesome-trio-libraries.rst b/docs/source/awesome-trio-libraries.rst index 875471a21f..4a603960d8 100644 --- a/docs/source/awesome-trio-libraries.rst +++ b/docs/source/awesome-trio-libraries.rst @@ -50,6 +50,7 @@ Database * `asyncakumuli `__ - Client for the `Akumuli `__ time series database. * `aio-databases `_ - Async Support for various databases (triopg, trio-mysql) * `peewee-aio `_ - Peewee Async ORM with trio support (triopg, trio-mysql). +* `coredis `_ - Fast, async, fully-typed Redis client with support for cluster and sentinel IOT @@ -83,6 +84,11 @@ Stream Processing * `Slurry `__ - Slurry is a microframework for building reactive, data processing applications with Trio. +Distributed Task Queue +---------------------- +* `streaQ `_ - Fast, async, fully-typed distributed task queue via Redis streams + + RPC --- * `purepc `__ - Native, async Python gRPC client and server implementation using anyio. From 69404459f8eb0f7c4ca1433174fa0f5e9bad38ff Mon Sep 17 00:00:00 2001 From: A5rocks Date: Sat, 14 Feb 2026 12:36:10 -0500 Subject: [PATCH 34/36] Documentation search improvements (#3378) * Try adding `genindex.html` to toc * Try making a new genindex template * Completely copy over the basic template genindex * Hacks to make index fully qualified * Oops --- docs/source/_templates/genindex.html | 38 ++++++++++++++++++++++++++++ docs/source/index.rst | 1 + 2 files changed, 39 insertions(+) create mode 100644 docs/source/_templates/genindex.html diff --git a/docs/source/_templates/genindex.html b/docs/source/_templates/genindex.html new file mode 100644 index 0000000000..7db72e44d1 --- /dev/null +++ b/docs/source/_templates/genindex.html @@ -0,0 +1,38 @@ +{% extends "!genindex.html" %} + +{# check sphinx/themes/basic/genindex if this snippet has become outdated #} + +{% block body %} + +

{{ _('Index') }}

+ +
+ {% for key, dummy in genindexentries -%} + {{ key }} + {% if not loop.last %}| {% endif %} + {%- endfor %} +
+ +{%- for key, entries in genindexentries %} +

{{ key }}

+ + {%- for column in entries|slice_index(2) if column %} + + {%- endfor %} +
    + {%- for entryname, (links, subitems, _) in column %} + {% set name = links[0][1].rsplit('#', 1)[1] if links else '' %} +
  • {{ indexentries(name if name and '-' not in name else entryname, links) }} + {%- if subitems %} +
      + {%- for subentryname, subentrylinks in subitems %} + {% set sname = subentrylinks[0][1].rsplit('#', 1)[1] if subentrylinks else '' %} +
    • {{ indexentries(sname if sname and '-' not in sname else subentryname, subentrylinks) }}
    • + {%- endfor %} +
    + {%- endif -%}
  • + {%- endfor %} +
+{% endfor %} + +{% endblock %} diff --git a/docs/source/index.rst b/docs/source/index.rst index f8fc6add8f..d64d28041a 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -86,6 +86,7 @@ Vital statistics: contributing.rst releasing.rst code-of-conduct.rst + genindex ==================== Indices and tables From c21654b5f8375c5104625e6748492913cdb9d304 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 14 Feb 2026 18:02:15 +0000 Subject: [PATCH 35/36] Bump dependencies from commit 694044 (#3391) * Dependency updates * Ignore RUF069 for tests * Oops, ignore core tests too --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: A5rocks --- .pre-commit-config.yaml | 6 +++--- docs-requirements.txt | 4 ++-- pyproject.toml | 8 ++++++-- test-requirements.txt | 22 +++++++++++----------- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1688604b03..2cfce7979c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: hooks: - id: black - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.0 + rev: v0.15.1 hooks: - id: ruff-check types: [file] @@ -38,7 +38,7 @@ repos: # tomli needed on 3.10. tomllib is available in stdlib on 3.11+ - tomli - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.43.3 + rev: v1.43.4 hooks: - id: typos - repo: https://github.com/sphinx-contrib/sphinx-lint @@ -73,7 +73,7 @@ repos: additional_dependencies: ["pyyaml"] files: ^(test-requirements\.txt)|(\.pre-commit-config\.yaml)$ - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.10.0 + rev: 0.10.2 hooks: # Compile requirements - id: pip-compile diff --git a/docs-requirements.txt b/docs-requirements.txt index 7d7b6743a8..e74c483b50 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -6,7 +6,7 @@ attrs==25.4.0 # via # -r docs-requirements.in # outcome -babel==2.17.0 +babel==2.18.0 # via sphinx beautifulsoup4==4.14.3 # via sphinx-codeautolink @@ -24,7 +24,7 @@ colorama==0.4.6 ; sys_platform == 'win32' # via # click # sphinx -cryptography==46.0.4 +cryptography==46.0.5 # via pyopenssl docutils==0.22.4 # via diff --git a/pyproject.toml b/pyproject.toml index b7c3189fb7..4e50a217ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -171,12 +171,16 @@ extend-ignore = [ 'src/trio/lowlevel.py' = ['F401'] 'src/trio/socket.py' = ['F401', 'A005'] 'src/trio/testing/__init__.py' = ['F401'] + # RUF029 is ignoring tests that are marked as async functions but # do not use an await in their function bodies. There are several # places where internal trio synchronous code relies on being # called from an async function, where current task is set up. -'src/trio/_tests/*.py' = ['RUF029'] -'src/trio/_core/_tests/*.py' = ['RUF029'] +# +# RUF069 is ignoring exact float comparison, because we use that +# in combination with autojump clocks +'src/trio/_tests/*.py' = ['RUF029', 'RUF069'] +'src/trio/_core/_tests/*.py' = ['RUF029', 'RUF069'] # A005 is ignoring modules that shadow stdlib modules. 'src/trio/_abc.py' = ['A005'] 'src/trio/_socket.py' = ['A005'] diff --git a/test-requirements.txt b/test-requirements.txt index f752690ded..6888340058 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,7 +4,7 @@ alabaster==1.0.0 # via sphinx astor==0.8.1 # via -r test-requirements.in -astroid==4.0.3 +astroid==4.0.4 # via pylint async-generator==1.10 # via -r test-requirements.in @@ -12,7 +12,7 @@ attrs==25.4.0 # via # -r test-requirements.in # outcome -babel==2.17.0 +babel==2.18.0 # via sphinx black==26.1.0 ; implementation_name == 'cpython' # via -r test-requirements.in @@ -36,9 +36,9 @@ colorama==0.4.6 ; sys_platform == 'win32' # pylint # pytest # sphinx -coverage==7.13.2 +coverage==7.13.4 # via -r test-requirements.in -cryptography==46.0.4 +cryptography==46.0.5 # via # -r test-requirements.in # pyopenssl @@ -56,7 +56,7 @@ exceptiongroup==1.3.1 ; python_full_version < '3.11' # via # -r test-requirements.in # pytest -filelock==3.20.3 +filelock==3.24.0 # via virtualenv identify==2.6.16 # via pre-commit @@ -75,7 +75,7 @@ jedi==0.19.2 ; implementation_name == 'cpython' # via -r test-requirements.in jinja2==3.1.6 # via sphinx -librt==0.7.8 ; implementation_name == 'cpython' and platform_python_implementation != 'PyPy' +librt==0.8.0 ; implementation_name == 'cpython' and platform_python_implementation != 'PyPy' # via mypy markupsafe==3.0.3 # via jinja2 @@ -99,13 +99,13 @@ packaging==26.0 # black # pytest # sphinx -parso==0.8.5 ; implementation_name == 'cpython' +parso==0.8.6 ; implementation_name == 'cpython' # via jedi pathspec==1.0.4 ; implementation_name == 'cpython' # via # black # mypy -platformdirs==4.5.1 +platformdirs==4.9.0 # via # black # pylint @@ -136,7 +136,7 @@ requests==2.32.5 # via sphinx roman-numerals==4.1.0 ; python_full_version >= '3.11' # via sphinx -ruff==0.15.0 +ruff==0.15.1 # via -r test-requirements.in sniffio==1.3.1 # via -r test-requirements.in @@ -183,7 +183,7 @@ types-pyopenssl==24.1.0.20240722 # via -r test-requirements.in types-pyyaml==6.0.12.20250915 # via -r test-requirements.in -types-setuptools==80.10.0.20260124 +types-setuptools==82.0.0.20260210 # via types-cffi typing-extensions==4.15.0 # via @@ -198,7 +198,7 @@ typing-extensions==4.15.0 # virtualenv urllib3==2.6.3 # via requests -uv==0.10.0 +uv==0.10.2 # via -r test-requirements.in virtualenv==20.36.1 # via pre-commit From 3073f0c2108bad83c27dda6c83474a9b8ce4a957 Mon Sep 17 00:00:00 2001 From: A5rocks Date: Sun, 15 Feb 2026 03:27:28 +0900 Subject: [PATCH 36/36] Bump version to 0.33.0 --- docs/source/history.rst | 15 +++++++++++++++ newsfragments/3326.deprecated.rst | 1 - newsfragments/3357.bugfix.rst | 1 - src/trio/_version.py | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) delete mode 100644 newsfragments/3326.deprecated.rst delete mode 100644 newsfragments/3357.bugfix.rst diff --git a/docs/source/history.rst b/docs/source/history.rst index d66ac70ff0..2ca86c6980 100644 --- a/docs/source/history.rst +++ b/docs/source/history.rst @@ -5,6 +5,21 @@ Release history .. towncrier release notes start +trio 0.33.0 (2026-02-14) +------------------------ + +Bugfixes +~~~~~~~~ + +- Start supporting Android's new ``"android"`` `sys.platform`. (`#3357 `__) + + +Deprecations and removals +~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Both :class:`trio.testing.RaisesGroup` and :class:`trio.testing.Matcher` have been deprecated. Pytest alternatives ``pytest.RaisesGroup`` and ``pytest.RaisesExc`` (respectively) are considered correct replacement. (`#3326 `__) + + trio 0.32.0 (2025-10-31) ------------------------ diff --git a/newsfragments/3326.deprecated.rst b/newsfragments/3326.deprecated.rst deleted file mode 100644 index 74916bf4e6..0000000000 --- a/newsfragments/3326.deprecated.rst +++ /dev/null @@ -1 +0,0 @@ -Both :class:`trio.testing.RaisesGroup` and :class:`trio.testing.Matcher` have been deprecated. Pytest alternatives ``pytest.RaisesGroup`` and ``pytest.RaisesExc`` (respectively) are considered correct replacement. diff --git a/newsfragments/3357.bugfix.rst b/newsfragments/3357.bugfix.rst deleted file mode 100644 index d2badc264b..0000000000 --- a/newsfragments/3357.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Start supporting Android's new ``"android"`` `sys.platform`. diff --git a/src/trio/_version.py b/src/trio/_version.py index 659188b5e0..7a1cad22bd 100644 --- a/src/trio/_version.py +++ b/src/trio/_version.py @@ -1,3 +1,3 @@ # This file is imported from __init__.py and parsed by setuptools -__version__ = "0.32.0+dev" +__version__ = "0.33.0"