π Adds wheel auditing with abi3audit as a default after
the repair step, with new audit-requires
and audit-command
options (#2805)
π Adds pyemscripten platform tag support (PEP 783),
updates Pyodide to 314.0.0a2, and adds a pyodide-eolenable
flag for building end-of-life Pyodide versions (#2812,
#2848)
π Sets up delvewheel as the default repair-wheel-command
for Windows, so extension module DLLs are now bundled automatically.
Skip by setting it to empty if not needed. (#2831)
β¨ Adds CPython 3.15 support, under the enable
optioncpython-prerelease. This version of cibuildwheel
uses 3.15.0b2. (#2833,
#2850)
While CPython is in beta, the ABI can change, so your wheels
might not be compatible with the final release. For this reason, we
don't recommend distributing wheels until RC1, at which point 3.15 will
be available in cibuildwheel without the flag.
β¨ Adds CPython 3.15 support for iOS and Android (#2857,
#2858)
β¨ Adds Android improvements for building NumPy and related packages,
including auditwheel support, pkg-config and Fortran configuration, and
the xbuild-files
option (#2695)
β¨ Adds CIBUILDWHEEL_BUILD_IDENTIFIER environment
variable set to the current build identifier (e.g.
cp311-manylinux_x86_64) during per-build steps (#2872)
β οΈ Removes the experimental CPython 3.13 free-threading builds and
the cpython-freethreadingenable
option. CPython 3.14+ free-threading support remains available without
the enable flag. (#2684)
β οΈ Drops support for Cirrus CI, which is shutting down June 1, 2026
(#2817)
β οΈ Drops GraalPy 3.11 (gp311) support, as agreed in #2741,
and removes GraalPy 24-only workarounds (#2895)
π Adds SHA256 verification for direct downloads of Python
interpreters, virtualenv, and python-build-standalone assets (#2873)
π Adds tarfile extraction filter for safe archive extraction (#2856)
π Fixes detection of musl libc when downloading
python-build-standalone, which previously always selected the gnu asset
on musl hosts like Alpine (#2889)
π Fixes config-settings
expansion when {project} or {package} contains
spaces or backslashes (#2886)
π Prevents deadlock when linux32 fails and forwards
platform args to the sanity check (#2880,
#2888)
π Fixes container resource leaks on start failure and during teardown
(#2879,
#2887)
π Removes potential partial cache-population in case of error (#2892)
π Raises a clear error when ANDROID_API_LEVEL is not an
integer (#2891)
π Replaces assert with proper exception in python-build-standalone
(#2859)
π Uses ConfigurationError when package_dir is outside
cwd instead of a generic Exception (#2898)
π§ͺ Fixes Android tests using the uv frontend (#2809)
π§ͺ Fixes the update-dependencies workflow to use uv to
run nox (#2808)
π§ͺ Adds unit tests for OCIContainer._get_platform_args
(#2878)
π Updates documentation for delvewheel as the default Windows repair-wheel-command,
including the build diagram, schema defaults, and legal note (#2877,
#2853,
#2891)
π Adds wheel auditing with abi3audit as a default after
the repair step, with new audit-requires
and audit-command
options (#2805)
π Adds pyemscripten platform tag support (PEP 783),
updates Pyodide to 314.0.0a2, and adds a pyodide-eolenable
flag for building end-of-life Pyodide versions (#2812,
#2848)
π Sets up delvewheel as the default repair-wheel-command
for Windows, so extension module DLLs are now bundled automatically.
Skip by setting it to empty if not needed. (#2831)
β¨ Adds CPython 3.15 support, under the enable
optioncpython-prerelease. This version of cibuildwheel
uses 3.15.0b2. (#2833,
#2850)
While CPython is in beta, the ABI can change, so your wheels
might not be compatible with the final release. For this reason, we
don't recommend distributing wheels until RC1, at which point 3.15 will
be available in cibuildwheel without the flag.
β¨ Adds CPython 3.15 support for iOS and Android (#2857,
#2858)
β¨ Adds Android improvements for building NumPy and related packages,
including auditwheel support, pkg-config and Fortran configuration, and
the xbuild-files
option (#2695)
β¨ Adds CIBUILDWHEEL_BUILD_IDENTIFIER environment
variable set to the current build identifier (e.g.
cp311-manylinux_x86_64) during per-build steps (#2872)
β οΈ Removes the experimental CPython 3.13 free-threading builds and
the cpython-freethreadingenable
option. CPython 3.14+ free-threading support remains available without
the enable flag. (#2684)
β οΈ Drops support for Cirrus CI, which is shutting down June 1, 2026
(#2817)
β οΈ Drops GraalPy 3.11 (gp311) support, as agreed in #2741,
and removes GraalPy 24-only workarounds (#2895)
π Adds SHA256 verification for direct downloads of Python
interpreters, virtualenv, and python-build-standalone assets (#2873)
π Adds tarfile extraction filter for safe archive extraction (#2856)
π Fixes detection of musl libc when downloading
python-build-standalone, which previously always selected the gnu asset
on musl hosts like Alpine (#2889)
π Fixes config-settings
expansion when {project} or {package} contains
spaces or backslashes (#2886)
π Prevents deadlock when linux32 fails and forwards
platform args to the sanity check (#2880,
#2888)
π Fixes container resource leaks on start failure and during teardown
(#2879,
#2887)
π Removes potential partial cache-population in case of error (#2892)
π Raises a clear error when ANDROID_API_LEVEL is not an
integer (#2891)
π Replaces assert with proper exception in python-build-standalone
(#2859)
π Uses ConfigurationError when package_dir is outside
cwd instead of a generic Exception (#2898)
π§ͺ Fixes Android tests using the uv frontend (#2809)
π§ͺ Fixes the update-dependencies workflow to use uv to
run nox (#2808)
π§ͺ Adds unit tests for OCIContainer._get_platform_args
(#2878)
π Updates documentation for delvewheel as the default Windows repair-wheel-command,
including the build diagram, schema defaults, and legal note (#2877,
#2853,
#2891)
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
requirements/test-common.txt | 2 +-
requirements/test-ft.txt | 2 +-
requirements/test.txt | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 969f0879c22..9f9b4694425 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -60,7 +60,7 @@ coverage==7.14.1
# via
# -r requirements/test-common.in
# pytest-cov
-cryptography==48.0.0
+cryptography==48.0.1
# via trustme
cython==3.2.5
# via -r requirements/cython.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index f50b6be5cf6..e2c7ac7c746 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -60,7 +60,7 @@ coverage==7.14.1
# via
# -r requirements/test-common.in
# pytest-cov
-cryptography==48.0.0
+cryptography==48.0.1
# via trustme
distlib==0.4.2
# via virtualenv
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 5b8535145dd..85adb37c1f3 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -24,7 +24,7 @@ cfgv==3.5.0
# via pre-commit
click==8.4.1
# via slotscheck
-cryptography==48.0.0
+cryptography==48.0.1
# via trustme
distlib==0.4.2
# via virtualenv
diff --git a/requirements/test-common.txt b/requirements/test-common.txt
index d3e2f267c3e..3bafeed46fe 100644
--- a/requirements/test-common.txt
+++ b/requirements/test-common.txt
@@ -18,7 +18,7 @@ coverage==7.14.1
# via
# -r requirements/test-common.in
# pytest-cov
-cryptography==48.0.0
+cryptography==48.0.1
# via trustme
exceptiongroup==1.3.1
# via pytest
diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt
index d17865edc97..f7d399bce14 100644
--- a/requirements/test-ft.txt
+++ b/requirements/test-ft.txt
@@ -34,7 +34,7 @@ coverage==7.14.1
# via
# -r requirements/test-common.in
# pytest-cov
-cryptography==48.0.0
+cryptography==48.0.1
# via trustme
exceptiongroup==1.3.1
# via pytest
diff --git a/requirements/test.txt b/requirements/test.txt
index aa15eeadcfb..40ccbfadd71 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -34,7 +34,7 @@ coverage==7.14.1
# via
# -r requirements/test-common.in
# pytest-cov
-cryptography==48.0.0
+cryptography==48.0.1
# via trustme
exceptiongroup==1.3.1
# via pytest
From 3990bbcdca061abe72db0b39538b7978adf1784a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 11 Jun 2026 11:08:54 +0000
Subject: [PATCH 015/137] Bump filelock from 3.29.1 to 3.29.3 (#12900)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.29.1 to
3.29.3.
Release notes
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 9f9b4694425..301beb819fd 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -74,7 +74,7 @@ exceptiongroup==1.3.1
# via pytest
execnet==2.1.2
# via pytest-xdist
-filelock==3.29.1
+filelock==3.29.3
# via
# python-discovery
# virtualenv
diff --git a/requirements/dev.txt b/requirements/dev.txt
index e2c7ac7c746..726ca309bb0 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -72,7 +72,7 @@ exceptiongroup==1.3.1
# via pytest
execnet==2.1.2
# via pytest-xdist
-filelock==3.29.1
+filelock==3.29.3
# via
# python-discovery
# virtualenv
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 85adb37c1f3..4a6b9bcd3d4 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -30,7 +30,7 @@ distlib==0.4.2
# via virtualenv
exceptiongroup==1.3.1
# via pytest
-filelock==3.29.1
+filelock==3.29.3
# via
# python-discovery
# virtualenv
From 6995b10e2efe009a4e18493a913b37f4a8e8f52c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 11 Jun 2026 11:12:24 +0000
Subject: [PATCH 016/137] Bump sigstore/gh-action-sigstore-python from 3.3.0 to
3.4.0 (#12899)
Bumps
[sigstore/gh-action-sigstore-python](https://github.com/sigstore/gh-action-sigstore-python)
from 3.3.0 to 3.4.0.
Release notes
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci-cd.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 92a167c1c33..5478fe3171a 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -773,7 +773,7 @@ jobs:
skip-existing: true
- name: Sign the dists with Sigstore
- uses: sigstore/gh-action-sigstore-python@v3.3.0
+ uses: sigstore/gh-action-sigstore-python@v3.4.0
with:
inputs: >-
./dist/*.tar.gz
From dece773ea22d8efae02711fe9a3eaf59c173e8dd Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 12 Jun 2026 11:27:44 +0000
Subject: [PATCH 017/137] Bump distlib from 0.4.2 to 0.4.3 (#12908)
Bumps [distlib](https://github.com/pypa/distlib) from 0.4.2 to 0.4.3.
Changelog
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 301beb819fd..fd03e057ae9 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -64,7 +64,7 @@ cryptography==48.0.1
# via trustme
cython==3.2.5
# via -r requirements/cython.in
-distlib==0.4.2
+distlib==0.4.3
# via virtualenv
docutils==0.21.2
# via
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 726ca309bb0..d83fed4fe56 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -62,7 +62,7 @@ coverage==7.14.1
# pytest-cov
cryptography==48.0.1
# via trustme
-distlib==0.4.2
+distlib==0.4.3
# via virtualenv
docutils==0.21.2
# via
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 4a6b9bcd3d4..a57c568d1c6 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -26,7 +26,7 @@ click==8.4.1
# via slotscheck
cryptography==48.0.1
# via trustme
-distlib==0.4.2
+distlib==0.4.3
# via virtualenv
exceptiongroup==1.3.1
# via pytest
From 061bc47c6786062b54127a42d8e860193c06ba7e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 12 Jun 2026 11:38:18 +0000
Subject: [PATCH 018/137] Bump python-discovery from 1.4.0 to 1.4.2 (#12910)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [python-discovery](https://github.com/tox-dev/python-discovery)
from 1.4.0 to 1.4.2.
Release notes
Stop executable symlink resolution once the stdlib landmark is
reachable and keep macOS framework builds untouched,
matching getpath - Homebrew interpreters no longer get
version-pinned Cellar paths recorded and stable
aliases such as Debian's /usr/bin/python3 are preserved -
by :user:gaborbernat. (:issue:86)
v1.4.1 (2026-06-11)
Bug fixes - 1.4.1
Resolve executable-only symlinks when computing
system_executable, mirroring CPython's
getpath.realpathpython/cpython#115237
symlinked interpreter tree is kept as-is - by
:user:gaborbernat. (:issue:84)
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index fd03e057ae9..9704fe564bb 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -214,7 +214,7 @@ pytest-xdist==3.8.0
# via -r requirements/test-common.in
python-dateutil==2.9.0.post0
# via freezegun
-python-discovery==1.4.0
+python-discovery==1.4.2
# via virtualenv
python-on-whales==0.81.0
# via
diff --git a/requirements/dev.txt b/requirements/dev.txt
index d83fed4fe56..031a117a15b 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -209,7 +209,7 @@ pytest-xdist==3.8.0
# via -r requirements/test-common.in
python-dateutil==2.9.0.post0
# via freezegun
-python-discovery==1.4.0
+python-discovery==1.4.2
# via virtualenv
python-on-whales==0.81.0
# via
diff --git a/requirements/lint.txt b/requirements/lint.txt
index a57c568d1c6..0d816702b88 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -93,7 +93,7 @@ pytest-mock==3.15.1
# via -r requirements/lint.in
python-dateutil==2.9.0.post0
# via freezegun
-python-discovery==1.4.0
+python-discovery==1.4.2
# via virtualenv
python-on-whales==0.81.0
# via -r requirements/lint.in
From 3242ec2909ecfcb3f7e8b2093338dc23bc2dc094 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 12 Jun 2026 12:11:18 +0000
Subject: [PATCH 019/137] Bump virtualenv from 21.4.2 to 21.4.3 (#12912)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [virtualenv](https://github.com/pypa/virtualenv) from 21.4.2 to
21.4.3.
Release notes
Resolve executable-only symlinks when recording home and
base-executable in pyvenv.cfg, mirroring
CPython's
getpath.realpathpython/cpython#115237
binary locate the base stdlib (for example python-build-standalone); a
fully symlinked interpreter tree is kept as-is
by :user:gaborbernat. (:issue:3157)
Stop exporting PS1 from the bash activator so child
processes do not inherit shell prompt state.
(:issue:3158)
Handle CYGWIN/MSYS/MINGW path conversions in fish activation script -
by user::LuNoX. (:issue:3160)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 9704fe564bb..8532cc81ea9 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -307,7 +307,7 @@ uvloop==0.22.1 ; platform_system != "Windows"
# -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.4.2
+virtualenv==21.4.3
# via pre-commit
wait-for-it==2.3.0
# via -r requirements/test-common-base.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 031a117a15b..7f03cf37afa 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -297,7 +297,7 @@ uvloop==0.22.1 ; platform_system != "Windows" and implementation_name == "cpytho
# -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.4.2
+virtualenv==21.4.3
# via pre-commit
wait-for-it==2.3.0
# via -r requirements/test-common-base.in
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 0d816702b88..f6cea4f0bc7 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -128,7 +128,7 @@ uvloop==0.22.1 ; platform_system != "Windows"
# via -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.4.2
+virtualenv==21.4.3
# via pre-commit
zlib-ng==1.0.0
# via -r requirements/lint.in
From 51d6ea5e483ce418f60f7b757d0c01d5a60d7fca Mon Sep 17 00:00:00 2001
From: Rodrigo Nogueira
Date: Fri, 12 Jun 2026 22:15:42 -0300
Subject: [PATCH 020/137] [PR #12746/ff38c236 backport][3.15] Tokenize
Connection header in WebSocket client handshake (#12904)
**This is a backport of PR #12746 as merged into master
(ff38c236ce9cda3a05ae01e7a967012e05074fe6).**
---
aiohttp/client.py | 2 +-
aiohttp/client_reqrep.py | 2 ++
tests/test_client_proto.py | 39 ++++++++++++++++++++++++++++++++++++++
tests/test_client_ws.py | 4 ++++
tests/test_http_parser.py | 28 +++++++++++++++++++++++++++
5 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/aiohttp/client.py b/aiohttp/client.py
index d9d8dfd5db7..c0a616edd6b 100644
--- a/aiohttp/client.py
+++ b/aiohttp/client.py
@@ -1275,7 +1275,7 @@ async def _ws_connect(
headers=resp.headers,
)
- if resp.headers.get(hdrs.CONNECTION, "").lower() != "upgrade":
+ if not resp._upgraded:
raise WSServerHandshakeError(
resp.request_info,
resp.history,
diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py
index c77213e3ed2..8d99cc9818f 100644
--- a/aiohttp/client_reqrep.py
+++ b/aiohttp/client_reqrep.py
@@ -289,6 +289,7 @@ class ClientResponse(HeadersMixin):
_headers: CIMultiDictProxy[str] = None # type: ignore[assignment]
_history: tuple["ClientResponse", ...] = ()
_raw_headers: RawHeaders = None # type: ignore[assignment]
+ _upgraded: bool = False # parser saw a Connection: upgrade token
_connection: Optional["Connection"] = None # current connection
_cookies: SimpleCookie | None = None
@@ -583,6 +584,7 @@ async def start(self, connection: "Connection") -> "ClientResponse":
# headers
self._headers = message.headers # type is CIMultiDictProxy
self._raw_headers = message.raw_headers # type is Tuple[bytes, bytes]
+ self._upgraded = message.upgrade
# payload
self.content = payload
diff --git a/tests/test_client_proto.py b/tests/test_client_proto.py
index b4cd15b00f8..54ba0685a7d 100644
--- a/tests/test_client_proto.py
+++ b/tests/test_client_proto.py
@@ -385,3 +385,42 @@ async def test_abort_without_transport(loop: asyncio.AbstractEventLoop) -> None:
# Should not raise and should still clean up
assert proto._exception is None
mock_drop_timeout.assert_not_called()
+
+
+@pytest.mark.parametrize(
+ ("connection", "expected"),
+ [(b"upgrade, keep-alive", True), (b"keep-alive", False)],
+)
+async def test_response_start_records_upgrade(
+ connection: bytes, expected: bool
+) -> None:
+ """ClientResponse.start() preserves the parser's Connection upgrade flag."""
+ loop = asyncio.get_running_loop()
+ proto = ResponseHandler(loop=loop)
+ proto.connection_made(mock.Mock())
+ conn = mock.Mock(protocol=proto)
+ proto.set_response_params(read_until_eof=True)
+ proto.data_received(
+ b"HTTP/1.1 101 Switching Protocols\r\n"
+ b"Upgrade: websocket\r\n"
+ b"Connection: " + connection + b"\r\n\r\n"
+ )
+
+ url = URL("http://ws-upgrade.org")
+ response = ClientResponse(
+ "get",
+ url,
+ writer=mock.Mock(),
+ continue100=None,
+ timer=TimerNoop(),
+ request_info=mock.Mock(),
+ traces=[],
+ loop=loop,
+ session=mock.Mock(),
+ stream_writer=mock.create_autospec(
+ AbstractStreamWriter, spec_set=True, instance=True
+ ),
+ )
+ await response.start(conn)
+ assert response._upgraded is expected
+ response.close()
diff --git a/tests/test_client_ws.py b/tests/test_client_ws.py
index 169330b7d1a..b4953833bd4 100644
--- a/tests/test_client_ws.py
+++ b/tests/test_client_ws.py
@@ -22,6 +22,7 @@ async def test_ws_connect(ws_key: Any, loop: Any, key_data: Any) -> None:
hdrs.SEC_WEBSOCKET_ACCEPT: ws_key,
hdrs.SEC_WEBSOCKET_PROTOCOL: "chat",
}
+ resp._upgraded = True
resp.connection.protocol.read_timeout = None
with mock.patch("aiohttp.client.os") as m_os:
with mock.patch("aiohttp.client.ClientSession.request") as m_req:
@@ -247,6 +248,8 @@ async def test_ws_connect_err_upgrade(loop, ws_key, key_data) -> None:
async def test_ws_connect_err_conn(loop, ws_key, key_data) -> None:
+ # The parser did not see a Connection: upgrade token (resp._upgraded is
+ # False), so the handshake must be rejected.
resp = mock.Mock()
resp.status = 101
resp.headers = {
@@ -254,6 +257,7 @@ async def test_ws_connect_err_conn(loop, ws_key, key_data) -> None:
hdrs.CONNECTION: "close",
hdrs.SEC_WEBSOCKET_ACCEPT: ws_key,
}
+ resp._upgraded = False
with mock.patch("aiohttp.client.os") as m_os:
with mock.patch("aiohttp.client.ClientSession.request") as m_req:
m_os.urandom.return_value = key_data
diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py
index f2e0e9ae3f6..97643fd64ba 100644
--- a/tests/test_http_parser.py
+++ b/tests/test_http_parser.py
@@ -789,6 +789,34 @@ def test_upgrade_header_non_ascii(parser: HttpRequestParser) -> None:
assert not upgrade
+@pytest.mark.parametrize(
+ ("connection", "expected"),
+ [
+ ("upgrade", True),
+ ("upgrade, keep-alive", True), # other tokens alongside upgrade
+ ("keep-alive, upgrade", True), # upgrade not first
+ ("Upgrade, Keep-Alive", True), # case-insensitive
+ ("keep-alive", False), # no upgrade token
+ ("keep-alive, notupgrade", False), # substring is not a token
+ ],
+)
+def test_response_upgrade_token_in_connection_list(
+ response: HttpResponseParser, connection: str, expected: bool
+) -> None:
+ # RFC 9110 Β§7.6.1: Connection is a comma-separated token list, so the parser
+ # must set msg.upgrade for a 101 response whenever "upgrade" appears as a
+ # token, regardless of position, case, or neighbouring tokens.
+ text = (
+ b"HTTP/1.1 101 Switching Protocols\r\n"
+ b"Upgrade: websocket\r\n"
+ b"Connection: " + connection.encode() + b"\r\n\r\n"
+ )
+ messages, upgrade, tail = response.feed_data(text)
+ msg = messages[0][0]
+ assert msg.upgrade == expected
+ assert upgrade == expected
+
+
def test_request_te_chunked_with_content_length(parser: HttpRequestParser) -> None:
text = (
b"GET /test HTTP/1.1\r\n"
From 83638e03d5eefb67a80644d6ded2b820566d364c Mon Sep 17 00:00:00 2001
From: Sam Bull
Date: Sat, 13 Jun 2026 16:43:33 +0100
Subject: [PATCH 021/137] Narrow Exception.args types (#12920) (#12921)
(cherry picked from commit 83b06e48159c697b13741983d9c1f24d6a179fc5)
---
CHANGES/12920.feature.rst | 1 +
aiohttp/client_exceptions.py | 11 +++++++++++
aiohttp/http_exceptions.py | 6 ++++++
tests/test_client_exceptions.py | 20 ++++++++++++++++++++
tests/test_http_exceptions.py | 10 ++++++++++
5 files changed, 48 insertions(+)
create mode 100644 CHANGES/12920.feature.rst
diff --git a/CHANGES/12920.feature.rst b/CHANGES/12920.feature.rst
new file mode 100644
index 00000000000..d5802ab27ad
--- /dev/null
+++ b/CHANGES/12920.feature.rst
@@ -0,0 +1 @@
+Narrowed the ``args`` type on aiohttp exception classes -- by :user:`Dreamsorcerer`.
diff --git a/aiohttp/client_exceptions.py b/aiohttp/client_exceptions.py
index e3e503b7ca2..0a6ac27ee97 100644
--- a/aiohttp/client_exceptions.py
+++ b/aiohttp/client_exceptions.py
@@ -72,6 +72,8 @@ class ClientResponseError(ClientError):
headers: Response headers.
"""
+ args: tuple[RequestInfo, tuple[ClientResponse, ...]]
+
def __init__(
self,
request_info: RequestInfo,
@@ -177,6 +179,8 @@ class ClientConnectorError(ClientOSError):
a connection can not be established.
"""
+ args: tuple[ConnectionKey, OSError]
+
def __init__(self, connection_key: ConnectionKey, os_error: OSError) -> None:
self._conn_key = connection_key
self._os_error = os_error
@@ -254,6 +258,8 @@ class ServerConnectionError(ClientConnectionError):
class ServerDisconnectedError(ServerConnectionError):
"""Server disconnected."""
+ args: tuple[RawResponseMessage | str]
+
def __init__(self, message: RawResponseMessage | str | None = None) -> None:
if message is None:
message = "Server disconnected"
@@ -277,6 +283,8 @@ class SocketTimeoutError(ServerTimeoutError):
class ServerFingerprintMismatch(ServerConnectionError):
"""SSL certificate does not match expected fingerprint."""
+ args: tuple[bytes, bytes, str, int]
+
def __init__(self, expected: bytes, got: bytes, host: str, port: int) -> None:
self.expected = expected
self.got = got
@@ -301,6 +309,8 @@ class InvalidURL(ClientError, ValueError):
# Derive from ValueError for backward compatibility
+ args: tuple[StrOrURL] | tuple[StrOrURL, str]
+
def __init__(self, url: StrOrURL, description: str | None = None) -> None:
# The type of url is not yarl.URL because the exception can be raised
# on URL(url) call
@@ -381,6 +391,7 @@ class ClientConnectorCertificateError(*cert_errors_bases): # type: ignore[misc]
"""Response certificate error."""
_conn_key: ConnectionKey
+ args: tuple[ConnectionKey, Exception]
def __init__(
# TODO: If we require ssl in future, this can become ssl.CertificateError
diff --git a/aiohttp/http_exceptions.py b/aiohttp/http_exceptions.py
index 54cc4af36be..9404790e0bc 100644
--- a/aiohttp/http_exceptions.py
+++ b/aiohttp/http_exceptions.py
@@ -78,6 +78,8 @@ class DecompressSizeError(PayloadEncodingError):
class LineTooLong(BadHttpMessage):
+ args: tuple[str | bytes, str | int, str]
+
def __init__(
self,
line: str | bytes,
@@ -89,6 +91,8 @@ def __init__(
class InvalidHeader(BadHttpMessage):
+ args: tuple[bytes | str]
+
def __init__(self, hdr: bytes | str) -> None:
hdr_s = hdr.decode(errors="backslashreplace") if isinstance(hdr, bytes) else hdr
super().__init__(f"Invalid HTTP header: {hdr!r}")
@@ -97,6 +101,8 @@ def __init__(self, hdr: bytes | str) -> None:
class BadStatusLine(BadHttpMessage):
+ args: tuple[str]
+
def __init__(self, line: str = "", error: str | None = None) -> None:
if not isinstance(line, str):
line = repr(line)
diff --git a/tests/test_client_exceptions.py b/tests/test_client_exceptions.py
index e5a8d73ca10..67e64d1c5de 100644
--- a/tests/test_client_exceptions.py
+++ b/tests/test_client_exceptions.py
@@ -2,6 +2,7 @@
import errno
import pickle
+import sys
from unittest import mock
import pytest
@@ -9,6 +10,11 @@
from yarl import URL
from aiohttp import client, client_reqrep
+from aiohttp.http_parser import RawResponseMessage
+from aiohttp.typedefs import StrOrURL
+
+if sys.version_info >= (3, 11):
+ from typing import assert_type
class TestClientResponseError:
@@ -22,6 +28,10 @@ class TestClientResponseError:
def test_default_status(self) -> None:
err = client.ClientResponseError(history=(), request_info=self.request_info)
assert err.status == 0
+ if sys.version_info >= (3, 11):
+ assert_type(
+ err.args, tuple[client.RequestInfo, tuple[client.ClientResponse, ...]]
+ )
def test_status(self) -> None:
err = client.ClientResponseError(
@@ -138,6 +148,8 @@ def test_ctor(self) -> None:
assert err.host == "example.com"
assert err.port == 8080
assert err.ssl is True
+ if sys.version_info >= (3, 11):
+ assert_type(err.args, tuple[client_reqrep.ConnectionKey, OSError])
def test_pickle(self) -> None:
err = client.ClientConnectorError(
@@ -196,6 +208,8 @@ def test_ctor(self) -> None:
assert err.host == "example.com"
assert err.port == 8080
assert err.ssl is False
+ if sys.version_info >= (3, 11):
+ assert_type(err.args, tuple[client_reqrep.ConnectionKey, Exception])
def test_pickle(self) -> None:
certificate_error = Exception("Bad certificate")
@@ -249,6 +263,8 @@ def test_ctor(self) -> None:
err = client.ServerDisconnectedError(message="No connection")
assert err.message == "No connection"
+ if sys.version_info >= (3, 11):
+ assert_type(err.args, tuple[RawResponseMessage | str])
def test_pickle(self) -> None:
err = client.ServerDisconnectedError(message="No connection")
@@ -283,6 +299,8 @@ def test_ctor(self) -> None:
assert err.got == b"got"
assert err.host == "example.com"
assert err.port == 8080
+ if sys.version_info >= (3, 11):
+ assert_type(err.args, tuple[bytes, bytes, str, int])
def test_pickle(self) -> None:
err = client.ServerFingerprintMismatch(
@@ -311,6 +329,8 @@ def test_ctor(self) -> None:
err = client.InvalidURL(url=":wrong:url:", description=":description:")
assert err.url == ":wrong:url:"
assert err.description == ":description:"
+ if sys.version_info >= (3, 11):
+ assert_type(err.args, tuple[StrOrURL] | tuple[StrOrURL, str])
def test_pickle(self) -> None:
err = client.InvalidURL(url=":wrong:url:")
diff --git a/tests/test_http_exceptions.py b/tests/test_http_exceptions.py
index 6186a71c681..38d388add8b 100644
--- a/tests/test_http_exceptions.py
+++ b/tests/test_http_exceptions.py
@@ -1,9 +1,13 @@
# Tests for http_exceptions.py
import pickle
+import sys
from aiohttp import http_exceptions
+if sys.version_info >= (3, 11):
+ from typing import assert_type
+
class TestHttpProcessingError:
def test_ctor(self) -> None:
@@ -73,6 +77,8 @@ def test_ctor(self) -> None:
assert err.code == 400
assert err.message == "Got more than 10 bytes when reading: b'spam'."
assert err.headers is None
+ if sys.version_info >= (3, 11):
+ assert_type(err.args, tuple[bytes, int])
def test_pickle(self) -> None:
err = http_exceptions.LineTooLong(line=b"spam", limit=10, actual_size="12")
@@ -104,6 +110,8 @@ def test_ctor(self) -> None:
assert err.code == 400
assert err.message == "Invalid HTTP header: 'X-Spam'"
assert err.headers is None
+ if sys.version_info >= (3, 11):
+ assert_type(err.args, tuple[bytes | str])
def test_pickle(self) -> None:
err = http_exceptions.InvalidHeader(hdr="X-Spam")
@@ -131,6 +139,8 @@ def test_ctor(self) -> None:
err = http_exceptions.BadStatusLine("Test")
assert err.line == "Test"
assert str(err) == "400, message:\n Bad status line 'Test'"
+ if sys.version_info >= (3, 11):
+ assert_type(err.args, tuple[str])
def test_ctor2(self) -> None:
err = http_exceptions.BadStatusLine(b"")
From 5c6ac4c937f347742d72c5c330318646bcd60cb5 Mon Sep 17 00:00:00 2001
From: Sam Bull
Date: Sat, 13 Jun 2026 16:44:35 +0100
Subject: [PATCH 022/137] =?UTF-8?q?explicitly=20throw=20error=20for=20sock?=
=?UTF-8?q?s5=20proxy=20since=20is=20not=20supported=20suppor=E2=80=A6=20(?=
=?UTF-8?q?#12919)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
β¦ted currently and will be implied disconnected if connect to (#10147)
(cherry picked from commit 31702b2532c74e3dec4c9ffd9465343f7a2fd37a)
---------
Co-authored-by: NewUserHa <32261870+NewUserHa@users.noreply.github.com>
---
aiohttp/client.py | 9 ++-------
aiohttp/client_reqrep.py | 7 +++++++
aiohttp/connector.py | 8 +-------
aiohttp/helpers.py | 7 +++++++
tests/test_client_request.py | 8 ++++++++
5 files changed, 25 insertions(+), 14 deletions(-)
diff --git a/aiohttp/client.py b/aiohttp/client.py
index c0a616edd6b..0bec36e072f 100644
--- a/aiohttp/client.py
+++ b/aiohttp/client.py
@@ -80,19 +80,14 @@
ClientWebSocketResponse as ClientWebSocketResponse,
ClientWSTimeout as ClientWSTimeout,
)
-from .connector import (
- HTTP_AND_EMPTY_SCHEMA_SET,
- BaseConnector as BaseConnector,
- NamedPipeConnector as NamedPipeConnector,
- TCPConnector as TCPConnector,
- UnixConnector as UnixConnector,
-)
+from .connector import BaseConnector, NamedPipeConnector, TCPConnector, UnixConnector
from .cookiejar import CookieJar
from .helpers import (
_SENTINEL,
DEBUG,
DEFAULT_CHUNK_SIZE,
EMPTY_BODY_METHODS,
+ HTTP_AND_EMPTY_SCHEMA_SET,
BasicAuth,
TimeoutHandle,
basicauth_from_netrc,
diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py
index 8d99cc9818f..684b1e77ae7 100644
--- a/aiohttp/client_reqrep.py
+++ b/aiohttp/client_reqrep.py
@@ -36,6 +36,7 @@
from .formdata import FormData
from .helpers import (
_SENTINEL,
+ HTTP_AND_EMPTY_SCHEMA_SET,
BaseTimerContext,
BasicAuth,
HeadersMixin,
@@ -1337,6 +1338,12 @@ def update_proxy(
self.proxy_headers = None
return
+ if proxy.scheme not in HTTP_AND_EMPTY_SCHEMA_SET:
+ raise ValueError(
+ f"aiohttp only supports http(s) proxies (got: {proxy.scheme!r}).\n"
+ "See third-party libraries for other proxy schemes."
+ )
+
if proxy_auth and not isinstance(proxy_auth, helpers.BasicAuth):
raise ValueError("proxy_auth must be None or BasicAuth() tuple")
self.proxy_auth = proxy_auth
diff --git a/aiohttp/connector.py b/aiohttp/connector.py
index b7aa7b7647a..438c336da7f 100644
--- a/aiohttp/connector.py
+++ b/aiohttp/connector.py
@@ -37,6 +37,7 @@
from .client_reqrep import ClientRequest, Fingerprint, _merge_ssl_params
from .helpers import (
_SENTINEL,
+ HIGH_LEVEL_SCHEMA_SET,
ceil_timeout,
is_canonical_ipv4_address,
is_ip_address,
@@ -66,13 +67,6 @@
ssl = None # type: ignore[assignment]
SSLContext = object # type: ignore[misc,assignment]
-EMPTY_SCHEMA_SET = frozenset({""})
-HTTP_SCHEMA_SET = frozenset({"http", "https"})
-WS_SCHEMA_SET = frozenset({"ws", "wss"})
-
-HTTP_AND_EMPTY_SCHEMA_SET = HTTP_SCHEMA_SET | EMPTY_SCHEMA_SET
-HIGH_LEVEL_SCHEMA_SET = HTTP_AND_EMPTY_SCHEMA_SET | WS_SCHEMA_SET
-
NEEDS_CLEANUP_CLOSED = (3, 13, 0) <= sys.version_info < (
3,
13,
diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py
index 469c99dd63c..f86a7387af0 100644
--- a/aiohttp/helpers.py
+++ b/aiohttp/helpers.py
@@ -83,6 +83,13 @@
)
+EMPTY_SCHEMA_SET = frozenset({""})
+HTTP_SCHEMA_SET = frozenset({"http", "https"})
+WS_SCHEMA_SET = frozenset({"ws", "wss"})
+HTTP_AND_EMPTY_SCHEMA_SET = HTTP_SCHEMA_SET | EMPTY_SCHEMA_SET
+HIGH_LEVEL_SCHEMA_SET = HTTP_AND_EMPTY_SCHEMA_SET | WS_SCHEMA_SET
+
+
CHAR = {chr(i) for i in range(0, 128)}
CTL = {chr(i) for i in range(0, 32)} | {
chr(127),
diff --git a/tests/test_client_request.py b/tests/test_client_request.py
index 90ffcecb5d8..93920055c4d 100644
--- a/tests/test_client_request.py
+++ b/tests/test_client_request.py
@@ -220,6 +220,14 @@ def test_hostname_err(make_request) -> None:
make_request("get", "http://:8080/")
+@pytest.mark.parametrize("scheme", ("socks5", "socks5h"))
+async def test_proxy_scheme_err(make_request: _RequestMaker, scheme: str) -> None:
+ with pytest.raises(ValueError, match=f"'{scheme}'"):
+ make_request(
+ "get", URL("http://py.org/"), proxy=URL(f"{scheme}://127.0.0.1:80")
+ )
+
+
def test_host_header_host_first(make_request) -> None:
req = make_request("get", "http://python.org/")
assert list(req.headers)[0] == hdrs.HOST
From 197fdbb975fa1079e673b3c733c9ec105859d39f Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Sat, 13 Jun 2026 18:51:08 +0100
Subject: [PATCH 023/137] [PR #12914/de1aded8 backport][3.15] Add incident
response plan (#12924)
**This is a backport of PR #12914 as merged into master
(de1aded81301aba0f96471904df1df97683f9e18).**
Co-authored-by: Sam Bull
---
CHANGES/12914.contrib.rst | 3 +
docs/contributing-admins.rst | 297 +++++++++++++++++++++++++++++++++++
docs/spelling_wordlist.txt | 3 +
3 files changed, 303 insertions(+)
create mode 100644 CHANGES/12914.contrib.rst
diff --git a/CHANGES/12914.contrib.rst b/CHANGES/12914.contrib.rst
new file mode 100644
index 00000000000..5ef030c2786
--- /dev/null
+++ b/CHANGES/12914.contrib.rst
@@ -0,0 +1,3 @@
+Added admin documentation on incident response and on running reproducer code
+safely, covering security vulnerability handling and supply-chain, account, and
+CI/infrastructure compromise -- by :user:`Dreamsorcerer`.
diff --git a/docs/contributing-admins.rst b/docs/contributing-admins.rst
index 749e1f87da1..2fffc965fce 100644
--- a/docs/contributing-admins.rst
+++ b/docs/contributing-admins.rst
@@ -8,6 +8,19 @@ For regular contributors, return to :doc:`contributing`.
.. contents::
:local:
+ :depth: 1
+
+.. highlight:: none
+
+Running reproducer code
+-----------------------
+
+.. warning::
+
+ When evaluating a bug report or vulnerability report, treat reproducer code as
+ untrusted. If you don't understand what it does or are unfamiliar with a library
+ it imports **do not run it** (and ask the reporter to provide a simpler reproducer).
+ We also recommend that any reproducers you do run are executed in a container.
Creating a new release
----------------------
@@ -60,3 +73,287 @@ If doing a minor release:
#. Update both ``target-branch`` backports for Dependabot to reference the new branch name in ``.github/dependabot.yml``.
#. Delete the older backport label (e.g. backport-3.8): https://github.com/aio-libs/aiohttp/labels
#. Add a new backport label (e.g. backport-3.10).
+
+Incident response
+-----------------
+
+This section covers responding to a reported security vulnerability and to
+three classes of compromise -- of the supply chain, of a maintainer account,
+and of the project infrastructure. It picks up *after* a report or a problem
+is in hand.
+
+.. note::
+
+ Vulnerability reports arrive through the organization's ``SECURITY.md``
+ -- GitHub private vulnerability reporting, or email to the security coordinators.
+ Never triage a suspected vulnerability in a public issue or pull request.
+
+The security coordinator who first triages a report is its *incident lead*,
+and may explicitly hand off to another coordinator. The lead owns the GitHub
+Security Advisory (GHSA) draft, fix coordination, the release, and notification.
+
+Severity tiers
+~~~~~~~~~~~~~~
+
+aiohttp is volunteer-maintained, so there is no response-time commitment. The
+tier guides prioritization, the release decision, and how widely the fix is
+announced. All security fixes -- regardless of tier -- land on ``master`` and
+are backported to the currently supported ``x.y`` branch (and the next
+``x.y`` branch, if one is in development).
+
+High
+ A concrete remote impact an attacker can actually achieve against a
+ default deployment. Examples: a single request that stops the server from
+ handling further requests (a server-wide denial of service), reading files
+ on the server outside the project root, remote code execution,
+ authentication bypass. Cut a dedicated security release once the fix lands.
+
+Medium
+ Bounded impact, or impact that needs a non-default option, an unusual
+ configuration, or a local position. Examples: request smuggling demonstrated
+ to cause a real issue against a common proxy, or a DoS that use significant server
+ resources with a low effort sustained attack.
+ Cut a dedicated security release.
+
+Low
+ Limited or hard-to-exploit impact, or impact confined to debug or
+ non-default paths. Examples: minor version or error-message disclosure,
+ parser leniency with no demonstrated security impact, or request smuggling
+ that has not been demonstrated to cause a real issue against a common
+ proxy. May ride the next routine release rather than cutting a dedicated
+ security release.
+
+Severity is rated by demonstrated attacker impact. A denial of service that
+disables further request handling for the whole server from a single request
+leans High; one that requires sustained low-effort traffic to consume
+significant server resources leans Medium. Request smuggling and parser bugs
+are rated by what an attacker can actually achieve against a common proxy or
+in a realistic deployment, not by the shape of the bug.
+
+Responding to a reported vulnerability
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+#. **Acknowledge and confirm.** Reply to the reporter in the GitHub private
+ report or the email thread. Reproduce the issue
+ (`but don't run untrusted code `_) against
+ ``master`` and the maintained ``x.y`` branches. If it is not reproducible
+ or is out of scope, record the decision and close the report.
+#. **Open the GHSA draft.** For an email report, if the reporter can't use Github
+ for some reason, then open a new advisory to track the issue.
+#. **Develop the fix privately** in the GHSA's temporary private fork ("Start a
+ temporary private fork" in the draft) -- never on a public branch or in a
+ public pull request. Include a regression test unless it would provide too
+ much information for an attacker to replicate.
+#. **Title the advisory** with the vulnerability class and affected component.
+#. **Write the description** using the GHSA description template::
+
+ ### Summary
+
+
+
+ ### Impact
+
+
+
+ ### Workaround
+
+
+
+ ---
+
+ Patch:
+
+ An important detail that Github will use to decide the CVE severity is exploit
+ maturity. Try to clarify the likelihood of a vulnerability being attacked
+ (e.g. if exploit code is available, or active attacks in-the-wild already exist).
+#. **Set the affected and fixed versions**.
+#. **Assign a severity tier** (see `Severity tiers`_).
+#. **Credit the reporter** (if report came by email) in the GHSA Credits field.
+#. **Credit the developer** using "Remediation developer" if different from Reporter.
+#. **Request a CVE** by clicking the button in the advisory.
+#. **Get the fix reviewed** in the private fork by at least one other maintainer.
+#. **Consider early notify** for high severity issues
+ (see `Notifying about a disclosed vulnerability`_).
+#. **Coordinate a release.**
+
+ #. **Agree the timing.** For an embargoed high-severity incident, set the
+ release date to align with the lift of any private-list embargo.
+ #. **Merge all the private forks** and create and merge the backports for
+ each.
+ #. **Create the release.** Follow `Creating a new release`_.
+#. **Update patch link** in GHSA description.
+#. **Publish the GHSA** usually around 1 day after release.
+#. **Notify** according to the severity tier
+ (see `Notifying about a disclosed vulnerability`_).
+#. **Run the post-incident steps** (see `Post-incident`_).
+
+Notifying about a disclosed vulnerability
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Notification is cumulative -- each tier adds to the ones below it. Mind the
+timing: the ``linux-distros`` pre-notification happens under embargo, *before*
+the release, while everything else happens at or after the release.
+
+Baseline (all severities)
+ The published GHSA (which feeds the CVE and the GitHub Advisory Database).
+
+Higher (Medium-High)
+ For most high severity issues, additionally post to the public ``oss-security``
+ mailing list once the advisory is public.
+
+Highest (High-Critical)
+ For the most serious issues, additionally *before* the public release, send
+ an embargoed pre-notification to the private distribution-security lists -- primarily
+ ``linux-distros`` -- and attempt to notify affected downstreams. Use the
+ `vuln_search.py dependents-enumeration script
+ `_
+ to search aiohttp's GitHub dependents; it needs a GitHub token and may need to be
+ run over a few days, so start it early.
+
+Keep the embargo as short as practical. Typically an embargo of 1 or 2 days is expected.
+
+Template for the embargoed ``linux-distros`` pre-notification::
+
+ To: linux-distros@vs.openwall.org
+ Subject: aiohttp:
+
+ We are coordinating disclosure of a security vulnerability in aiohttp
+ (https://github.com/aio-libs/aiohttp), the asyncio HTTP client/server
+ library.
+
+ Summary
+ <2-3 sentences: the flaw and its impact>
+
+ Affected versions
+
+
+ CVE
+
+
+ Fix
+
+
+ Proposed public disclosure date
+ -- as short as practical. On that date we will publish the
+ GitHub Security Advisory, ship a patched PyPI release, and post to
+ oss-security.
+
+ This issue is not yet public; please observe the embargo until that date.
+
+ Contact:
+
+Template for the public ``oss-security`` disclosure::
+
+ To: oss-security@lists.openwall.com
+ Subject: CVE-XXXX-XXXXX: aiohttp
+
+ A security vulnerability has been fixed in aiohttp, the asyncio HTTP
+ client/server library (https://github.com/aio-libs/aiohttp).
+
+ CVE: CVE-XXXX-XXXXX
+ Advisory:
+
+ Affected versions: <...>
+ Fixed versions: <...>
+
+ Description
+
+
+ Impact
+
+
+ Mitigation
+ ">
+
+Post-incident
+~~~~~~~~~~~~~
+
+#. **Update** ``THREAT_MODEL.md``. Have an AI coding agent fetch the new advisories
+ and update the document. Review the changes.
+#. **File follow-up hardening** as ordinary public issues or pull requests.
+
+Supply-chain or release compromise
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A malicious or tampered release on PyPI, or a compromised publish pipeline.
+
+#. **Pull the bad artifact.** On PyPI, *yank* the affected release -- this
+ hides it from new resolves while keeping existing pins working. *Delete* it
+ only if it is actively malicious and removal is the safer choice. This needs
+ a PyPI project owner or maintainer account.
+#. **Verify the legitimate artifacts with Sigstore.** Every genuine release is
+ signed by the release pipeline, with the ``.sigstore`` bundles attached to
+ the GitHub Release. Verify the ``sdist`` and wheels; a divergence from what is on
+ PyPI localizes the tampering.
+#. **Lock the publish path.** Publishing uses a PyPI OIDC trusted publisher, so
+ there is no long-lived token to rotate. Instead, on PyPI temporarily remove
+ the trusted-publisher binding, and on GitHub restrict or pause the ``pypi``
+ deployment environment used by the release job.
+#. **Audit the release inputs.** Review recent changes to the release workflow,
+ its tag trigger, and the third-party action versions it uses.
+#. **Audit the committed Cython sources.** The generated ``.c`` files ship in
+ the ``sdist`` and are not checked against their ``.pyx`` sources in CI (see
+ ``THREAT_MODEL.md`` section 5.19). Regenerate them with ``make cythonize``
+ and ``git diff`` against the released revision. Re-verify the
+ ``vendor/llhttp`` pin and ``package-lock.json``.
+#. **Reissue a clean release** via `Creating a new release`_ once the cause is
+ fixed. Never re-publish over a yanked version number.
+#. **Notify** per severity -- a malicious published release is High.
+
+Maintainer account compromise
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A GitHub or PyPI account takeover, or leaked publishing or signing material.
+
+#. **Suspend the account.** Another GitHub organization owner can suspend it,
+ revoke its repository access, and start the audit-log review. If the
+ compromised account is the sole organization owner, or no other owner is
+ reachable, contact GitHub Support at https://support.github.com/contact
+ for emergency account lock down and organization recovery.
+#. **Kill sessions and credentials** on the affected account. On GitHub: change
+ the password, sign out all sessions, revoke every personal access token,
+ OAuth app, and SSH or GPG key, then re-enroll two-factor authentication.
+ On PyPI: change the password, re-enroll two-factor authentication,
+ and revoke API tokens.
+#. **Audit PyPI project ownership.** OIDC publishing means there is usually no
+ long-lived PyPI token to leak, but a compromised project owner can add a
+ trusted publisher or upload directly. Review the project collaborators and
+ trusted-publisher bindings, and remove anything unrecognized.
+#. **Audit what the account could have done** from the GitHub audit log:
+ pushes, tag creation, branch-protection or settings changes, new secrets or
+ deploy keys, new collaborators, and workflow edits.
+#. **Revert and re-verify.** Force-revert any unauthorized commits or tags, and
+ re-verify recent releases against their ``Sigstore`` bundles. If a malicious
+ release shipped, escalate to `Supply-chain or release compromise`_.
+#. **Handle signing material.** ``Sigstore`` signing is key-less, so there is no
+ static signing key to rotate. Any separate GPG key used for signed tags must
+ be treated as compromised, then revoked and rotated.
+
+CI or infrastructure compromise
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A compromise of GitHub Actions, repository settings, or branch protections.
+
+#. **Freeze the release path.** Pause the ``pypi`` deployment environment and
+ stop tag-triggered deploys until the compromise is scoped.
+#. **Review branch protection and required reviews** on ``master`` and the
+ ``x.y`` branches. These are enforced on GitHub and are not visible in the
+ repository; confirm nothing was relaxed and restore the known-good
+ configuration.
+#. **Audit Actions secrets and environments** -- the repository and
+ organization secrets, the ``pypi`` environment's protection rules and
+ reviewers, and any new deploy keys. Confirm the workflow keeps empty
+ top-level permissions and that ``id-token: write`` is scoped to the deploy
+ job only.
+#. **Diff recently changed workflows** with ``git log -- .github/workflows/``,
+ especially the release workflow, the auto-merge workflow (which runs in the
+ privileged ``pull_request_target`` context), and the CodeQL workflow. Look
+ for added steps, changed action references, or new triggers.
+#. **Check the committed Cython sources** as in `Supply-chain or release
+ compromise`_ -- regenerate them and ``git diff``.
+#. **Re-pin and rebuild.** If a third-party action was compromised, pin the
+ affected actions by full commit hash, re-run CI from a clean known-good
+ revision, and re-verify with ``Sigstore`` any release made during the suspected
+ window.
+#. **Escalate** to `Supply-chain or release compromise`_ if a release shipped
+ through the compromised pipeline.
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index 920fc7f17fa..9ff16a5df2a 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -35,6 +35,7 @@ backoff
backend
backends
backport
+backported
Backport
Backporting
backports
@@ -319,6 +320,7 @@ seekable
sendfile
serializable
serializer
+severities
shourtcuts
skipuntil
Skyscanner
@@ -360,6 +362,7 @@ toolbar
toplevel
towncrier
tp
+triages
tuples
ue
UI
From f0b55124fcef4519960748af1933daac4c5e174a Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Sat, 13 Jun 2026 18:51:21 +0100
Subject: [PATCH 024/137] [PR #12914/de1aded8 backport][3.14] Add incident
response plan (#12923)
**This is a backport of PR #12914 as merged into master
(de1aded81301aba0f96471904df1df97683f9e18).**
Co-authored-by: Sam Bull
---
CHANGES/12914.contrib.rst | 3 +
docs/contributing-admins.rst | 297 +++++++++++++++++++++++++++++++++++
docs/spelling_wordlist.txt | 3 +
3 files changed, 303 insertions(+)
create mode 100644 CHANGES/12914.contrib.rst
diff --git a/CHANGES/12914.contrib.rst b/CHANGES/12914.contrib.rst
new file mode 100644
index 00000000000..5ef030c2786
--- /dev/null
+++ b/CHANGES/12914.contrib.rst
@@ -0,0 +1,3 @@
+Added admin documentation on incident response and on running reproducer code
+safely, covering security vulnerability handling and supply-chain, account, and
+CI/infrastructure compromise -- by :user:`Dreamsorcerer`.
diff --git a/docs/contributing-admins.rst b/docs/contributing-admins.rst
index 749e1f87da1..2fffc965fce 100644
--- a/docs/contributing-admins.rst
+++ b/docs/contributing-admins.rst
@@ -8,6 +8,19 @@ For regular contributors, return to :doc:`contributing`.
.. contents::
:local:
+ :depth: 1
+
+.. highlight:: none
+
+Running reproducer code
+-----------------------
+
+.. warning::
+
+ When evaluating a bug report or vulnerability report, treat reproducer code as
+ untrusted. If you don't understand what it does or are unfamiliar with a library
+ it imports **do not run it** (and ask the reporter to provide a simpler reproducer).
+ We also recommend that any reproducers you do run are executed in a container.
Creating a new release
----------------------
@@ -60,3 +73,287 @@ If doing a minor release:
#. Update both ``target-branch`` backports for Dependabot to reference the new branch name in ``.github/dependabot.yml``.
#. Delete the older backport label (e.g. backport-3.8): https://github.com/aio-libs/aiohttp/labels
#. Add a new backport label (e.g. backport-3.10).
+
+Incident response
+-----------------
+
+This section covers responding to a reported security vulnerability and to
+three classes of compromise -- of the supply chain, of a maintainer account,
+and of the project infrastructure. It picks up *after* a report or a problem
+is in hand.
+
+.. note::
+
+ Vulnerability reports arrive through the organization's ``SECURITY.md``
+ -- GitHub private vulnerability reporting, or email to the security coordinators.
+ Never triage a suspected vulnerability in a public issue or pull request.
+
+The security coordinator who first triages a report is its *incident lead*,
+and may explicitly hand off to another coordinator. The lead owns the GitHub
+Security Advisory (GHSA) draft, fix coordination, the release, and notification.
+
+Severity tiers
+~~~~~~~~~~~~~~
+
+aiohttp is volunteer-maintained, so there is no response-time commitment. The
+tier guides prioritization, the release decision, and how widely the fix is
+announced. All security fixes -- regardless of tier -- land on ``master`` and
+are backported to the currently supported ``x.y`` branch (and the next
+``x.y`` branch, if one is in development).
+
+High
+ A concrete remote impact an attacker can actually achieve against a
+ default deployment. Examples: a single request that stops the server from
+ handling further requests (a server-wide denial of service), reading files
+ on the server outside the project root, remote code execution,
+ authentication bypass. Cut a dedicated security release once the fix lands.
+
+Medium
+ Bounded impact, or impact that needs a non-default option, an unusual
+ configuration, or a local position. Examples: request smuggling demonstrated
+ to cause a real issue against a common proxy, or a DoS that use significant server
+ resources with a low effort sustained attack.
+ Cut a dedicated security release.
+
+Low
+ Limited or hard-to-exploit impact, or impact confined to debug or
+ non-default paths. Examples: minor version or error-message disclosure,
+ parser leniency with no demonstrated security impact, or request smuggling
+ that has not been demonstrated to cause a real issue against a common
+ proxy. May ride the next routine release rather than cutting a dedicated
+ security release.
+
+Severity is rated by demonstrated attacker impact. A denial of service that
+disables further request handling for the whole server from a single request
+leans High; one that requires sustained low-effort traffic to consume
+significant server resources leans Medium. Request smuggling and parser bugs
+are rated by what an attacker can actually achieve against a common proxy or
+in a realistic deployment, not by the shape of the bug.
+
+Responding to a reported vulnerability
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+#. **Acknowledge and confirm.** Reply to the reporter in the GitHub private
+ report or the email thread. Reproduce the issue
+ (`but don't run untrusted code `_) against
+ ``master`` and the maintained ``x.y`` branches. If it is not reproducible
+ or is out of scope, record the decision and close the report.
+#. **Open the GHSA draft.** For an email report, if the reporter can't use Github
+ for some reason, then open a new advisory to track the issue.
+#. **Develop the fix privately** in the GHSA's temporary private fork ("Start a
+ temporary private fork" in the draft) -- never on a public branch or in a
+ public pull request. Include a regression test unless it would provide too
+ much information for an attacker to replicate.
+#. **Title the advisory** with the vulnerability class and affected component.
+#. **Write the description** using the GHSA description template::
+
+ ### Summary
+
+
+
+ ### Impact
+
+
+
+ ### Workaround
+
+
+
+ ---
+
+ Patch:
+
+ An important detail that Github will use to decide the CVE severity is exploit
+ maturity. Try to clarify the likelihood of a vulnerability being attacked
+ (e.g. if exploit code is available, or active attacks in-the-wild already exist).
+#. **Set the affected and fixed versions**.
+#. **Assign a severity tier** (see `Severity tiers`_).
+#. **Credit the reporter** (if report came by email) in the GHSA Credits field.
+#. **Credit the developer** using "Remediation developer" if different from Reporter.
+#. **Request a CVE** by clicking the button in the advisory.
+#. **Get the fix reviewed** in the private fork by at least one other maintainer.
+#. **Consider early notify** for high severity issues
+ (see `Notifying about a disclosed vulnerability`_).
+#. **Coordinate a release.**
+
+ #. **Agree the timing.** For an embargoed high-severity incident, set the
+ release date to align with the lift of any private-list embargo.
+ #. **Merge all the private forks** and create and merge the backports for
+ each.
+ #. **Create the release.** Follow `Creating a new release`_.
+#. **Update patch link** in GHSA description.
+#. **Publish the GHSA** usually around 1 day after release.
+#. **Notify** according to the severity tier
+ (see `Notifying about a disclosed vulnerability`_).
+#. **Run the post-incident steps** (see `Post-incident`_).
+
+Notifying about a disclosed vulnerability
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Notification is cumulative -- each tier adds to the ones below it. Mind the
+timing: the ``linux-distros`` pre-notification happens under embargo, *before*
+the release, while everything else happens at or after the release.
+
+Baseline (all severities)
+ The published GHSA (which feeds the CVE and the GitHub Advisory Database).
+
+Higher (Medium-High)
+ For most high severity issues, additionally post to the public ``oss-security``
+ mailing list once the advisory is public.
+
+Highest (High-Critical)
+ For the most serious issues, additionally *before* the public release, send
+ an embargoed pre-notification to the private distribution-security lists -- primarily
+ ``linux-distros`` -- and attempt to notify affected downstreams. Use the
+ `vuln_search.py dependents-enumeration script
+ `_
+ to search aiohttp's GitHub dependents; it needs a GitHub token and may need to be
+ run over a few days, so start it early.
+
+Keep the embargo as short as practical. Typically an embargo of 1 or 2 days is expected.
+
+Template for the embargoed ``linux-distros`` pre-notification::
+
+ To: linux-distros@vs.openwall.org
+ Subject: aiohttp:
+
+ We are coordinating disclosure of a security vulnerability in aiohttp
+ (https://github.com/aio-libs/aiohttp), the asyncio HTTP client/server
+ library.
+
+ Summary
+ <2-3 sentences: the flaw and its impact>
+
+ Affected versions
+
+
+ CVE
+
+
+ Fix
+
+
+ Proposed public disclosure date
+ -- as short as practical. On that date we will publish the
+ GitHub Security Advisory, ship a patched PyPI release, and post to
+ oss-security.
+
+ This issue is not yet public; please observe the embargo until that date.
+
+ Contact:
+
+Template for the public ``oss-security`` disclosure::
+
+ To: oss-security@lists.openwall.com
+ Subject: CVE-XXXX-XXXXX: aiohttp
+
+ A security vulnerability has been fixed in aiohttp, the asyncio HTTP
+ client/server library (https://github.com/aio-libs/aiohttp).
+
+ CVE: CVE-XXXX-XXXXX
+ Advisory:
+
+ Affected versions: <...>
+ Fixed versions: <...>
+
+ Description
+
+
+ Impact
+
+
+ Mitigation
+ ">
+
+Post-incident
+~~~~~~~~~~~~~
+
+#. **Update** ``THREAT_MODEL.md``. Have an AI coding agent fetch the new advisories
+ and update the document. Review the changes.
+#. **File follow-up hardening** as ordinary public issues or pull requests.
+
+Supply-chain or release compromise
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A malicious or tampered release on PyPI, or a compromised publish pipeline.
+
+#. **Pull the bad artifact.** On PyPI, *yank* the affected release -- this
+ hides it from new resolves while keeping existing pins working. *Delete* it
+ only if it is actively malicious and removal is the safer choice. This needs
+ a PyPI project owner or maintainer account.
+#. **Verify the legitimate artifacts with Sigstore.** Every genuine release is
+ signed by the release pipeline, with the ``.sigstore`` bundles attached to
+ the GitHub Release. Verify the ``sdist`` and wheels; a divergence from what is on
+ PyPI localizes the tampering.
+#. **Lock the publish path.** Publishing uses a PyPI OIDC trusted publisher, so
+ there is no long-lived token to rotate. Instead, on PyPI temporarily remove
+ the trusted-publisher binding, and on GitHub restrict or pause the ``pypi``
+ deployment environment used by the release job.
+#. **Audit the release inputs.** Review recent changes to the release workflow,
+ its tag trigger, and the third-party action versions it uses.
+#. **Audit the committed Cython sources.** The generated ``.c`` files ship in
+ the ``sdist`` and are not checked against their ``.pyx`` sources in CI (see
+ ``THREAT_MODEL.md`` section 5.19). Regenerate them with ``make cythonize``
+ and ``git diff`` against the released revision. Re-verify the
+ ``vendor/llhttp`` pin and ``package-lock.json``.
+#. **Reissue a clean release** via `Creating a new release`_ once the cause is
+ fixed. Never re-publish over a yanked version number.
+#. **Notify** per severity -- a malicious published release is High.
+
+Maintainer account compromise
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A GitHub or PyPI account takeover, or leaked publishing or signing material.
+
+#. **Suspend the account.** Another GitHub organization owner can suspend it,
+ revoke its repository access, and start the audit-log review. If the
+ compromised account is the sole organization owner, or no other owner is
+ reachable, contact GitHub Support at https://support.github.com/contact
+ for emergency account lock down and organization recovery.
+#. **Kill sessions and credentials** on the affected account. On GitHub: change
+ the password, sign out all sessions, revoke every personal access token,
+ OAuth app, and SSH or GPG key, then re-enroll two-factor authentication.
+ On PyPI: change the password, re-enroll two-factor authentication,
+ and revoke API tokens.
+#. **Audit PyPI project ownership.** OIDC publishing means there is usually no
+ long-lived PyPI token to leak, but a compromised project owner can add a
+ trusted publisher or upload directly. Review the project collaborators and
+ trusted-publisher bindings, and remove anything unrecognized.
+#. **Audit what the account could have done** from the GitHub audit log:
+ pushes, tag creation, branch-protection or settings changes, new secrets or
+ deploy keys, new collaborators, and workflow edits.
+#. **Revert and re-verify.** Force-revert any unauthorized commits or tags, and
+ re-verify recent releases against their ``Sigstore`` bundles. If a malicious
+ release shipped, escalate to `Supply-chain or release compromise`_.
+#. **Handle signing material.** ``Sigstore`` signing is key-less, so there is no
+ static signing key to rotate. Any separate GPG key used for signed tags must
+ be treated as compromised, then revoked and rotated.
+
+CI or infrastructure compromise
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A compromise of GitHub Actions, repository settings, or branch protections.
+
+#. **Freeze the release path.** Pause the ``pypi`` deployment environment and
+ stop tag-triggered deploys until the compromise is scoped.
+#. **Review branch protection and required reviews** on ``master`` and the
+ ``x.y`` branches. These are enforced on GitHub and are not visible in the
+ repository; confirm nothing was relaxed and restore the known-good
+ configuration.
+#. **Audit Actions secrets and environments** -- the repository and
+ organization secrets, the ``pypi`` environment's protection rules and
+ reviewers, and any new deploy keys. Confirm the workflow keeps empty
+ top-level permissions and that ``id-token: write`` is scoped to the deploy
+ job only.
+#. **Diff recently changed workflows** with ``git log -- .github/workflows/``,
+ especially the release workflow, the auto-merge workflow (which runs in the
+ privileged ``pull_request_target`` context), and the CodeQL workflow. Look
+ for added steps, changed action references, or new triggers.
+#. **Check the committed Cython sources** as in `Supply-chain or release
+ compromise`_ -- regenerate them and ``git diff``.
+#. **Re-pin and rebuild.** If a third-party action was compromised, pin the
+ affected actions by full commit hash, re-run CI from a clean known-good
+ revision, and re-verify with ``Sigstore`` any release made during the suspected
+ window.
+#. **Escalate** to `Supply-chain or release compromise`_ if a release shipped
+ through the compromised pipeline.
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
index a62ffed4ae9..24561f52d67 100644
--- a/docs/spelling_wordlist.txt
+++ b/docs/spelling_wordlist.txt
@@ -35,6 +35,7 @@ backoff
backend
backends
backport
+backported
Backport
Backporting
backports
@@ -318,6 +319,7 @@ seekable
sendfile
serializable
serializer
+severities
shourtcuts
skipuntil
Skyscanner
@@ -359,6 +361,7 @@ toolbar
toplevel
towncrier
tp
+triages
tuples
ue
UI
From f5c10beff9e5165dc53ec9d140c37b058c3a22bf Mon Sep 17 00:00:00 2001
From: Taras Kozlov
Date: Sun, 14 Jun 2026 20:28:25 +0200
Subject: [PATCH 025/137] [PR #12913/c2f4ac403 backport][3.15] Parameterize
test_simple_web_file_response benchmarks by file size (#12926)
---
CHANGES/12913.misc.rst | 2 +
tests/test_benchmarks_web_fileresponse.py | 49 +++++++++++++++++------
2 files changed, 39 insertions(+), 12 deletions(-)
create mode 100644 CHANGES/12913.misc.rst
diff --git a/CHANGES/12913.misc.rst b/CHANGES/12913.misc.rst
new file mode 100644
index 00000000000..e2c2673bcaa
--- /dev/null
+++ b/CHANGES/12913.misc.rst
@@ -0,0 +1,2 @@
+Parameterized test_simple_web_file_response and test_simple_web_file_response_fallback benchmarks by file size (small, large)
+Previously, benchmarks only ran for a small (around 11 kb) file. -- by :user:`tarasko`.
diff --git a/tests/test_benchmarks_web_fileresponse.py b/tests/test_benchmarks_web_fileresponse.py
index 5f06bb71caa..09df89daa01 100644
--- a/tests/test_benchmarks_web_fileresponse.py
+++ b/tests/test_benchmarks_web_fileresponse.py
@@ -1,7 +1,9 @@
"""codspeed benchmarks for the web file responses."""
import asyncio
+import os
import pathlib
+from dataclasses import dataclass
from typing import TYPE_CHECKING
import pytest
@@ -17,26 +19,48 @@
BenchmarkFixture = pytest_codspeed.BenchmarkFixture
+@dataclass(frozen=True)
+class BenchmarkFile:
+ path: pathlib.Path
+ response_count: int
+
+
+@pytest.fixture(
+ params=((10 * 1024, 100), (1024 * 1024, 10)),
+ ids=("small", "large"),
+)
+def benchmark_file(
+ request: pytest.FixtureRequest, tmp_path: pathlib.Path
+) -> BenchmarkFile:
+ size, response_count = request.param
+ filepath = tmp_path / "sample.txt"
+ filepath.touch()
+ os.truncate(filepath, size)
+ return BenchmarkFile(filepath, response_count)
+
+
def test_simple_web_file_response(
loop: asyncio.AbstractEventLoop,
aiohttp_client: AiohttpClient,
benchmark: BenchmarkFixture,
conn_type: ConnectionType,
+ benchmark_file: BenchmarkFile,
) -> None:
- """Benchmark creating 100 simple web.FileResponse."""
- response_count = 100
- filepath = pathlib.Path(__file__).parent / "sample.txt"
+ """Benchmark simple web.FileResponse."""
async def handler(request: web.Request) -> web.FileResponse:
- return web.FileResponse(path=filepath)
+ return web.FileResponse(path=benchmark_file.path)
app = web.Application()
app.router.add_route("GET", "/", handler)
async def run_file_response_benchmark() -> None:
client = await aiohttp_client(app, server_kwargs=conn_type.s_kwargs)
- for _ in range(response_count):
- await client.get("/", **conn_type.c_kwargs)
+ for _ in range(benchmark_file.response_count):
+ response = await client.get("/", **conn_type.c_kwargs)
+ # Consume response.
+ # Large responses may leave transport unclosed on at least python 3.10.
+ await response.read()
await client.close()
@benchmark
@@ -49,24 +73,25 @@ def test_simple_web_file_sendfile_fallback_response(
aiohttp_client: AiohttpClient,
benchmark: BenchmarkFixture,
conn_type: ConnectionType,
+ benchmark_file: BenchmarkFile,
) -> None:
- """Benchmark creating 100 simple web.FileResponse without sendfile."""
- response_count = 100
- filepath = pathlib.Path(__file__).parent / "sample.txt"
+ """Benchmark simple web.FileResponse without sendfile."""
async def handler(request: web.Request) -> web.FileResponse:
transport = request.transport
assert transport is not None
transport._sendfile_compatible = False # type: ignore[attr-defined]
- return web.FileResponse(path=filepath)
+ return web.FileResponse(path=benchmark_file.path)
app = web.Application()
app.router.add_route("GET", "/", handler)
async def run_file_response_benchmark() -> None:
client = await aiohttp_client(app, server_kwargs=conn_type.s_kwargs)
- for _ in range(response_count):
- await client.get("/", **conn_type.c_kwargs)
+
+ for _ in range(benchmark_file.response_count):
+ response = await client.get("/", **conn_type.c_kwargs)
+ await response.read()
await client.close()
@benchmark
From 8a0f750569cebe2bb1e5d0132587daa15f9325b1 Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Sun, 14 Jun 2026 19:59:38 +0100
Subject: [PATCH 026/137] [PR #12928/23725105 backport][3.15] Create
SECURITY_EXTRA.md (#12929)
**This is a backport of PR #12928 as merged into master
(2372510576b3bfeecb8438056288681a95536bb8).**
Co-authored-by: Sam Bull
---
SECURITY_EXTRA.md | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 SECURITY_EXTRA.md
diff --git a/SECURITY_EXTRA.md b/SECURITY_EXTRA.md
new file mode 100644
index 00000000000..f9b9da7c80a
--- /dev/null
+++ b/SECURITY_EXTRA.md
@@ -0,0 +1,10 @@
+# Vulnerability reporting guidelines for aiohttp
+
+- Most reports need a reproducer that makes an HTTP request. Attackers do not
+ have direct access to aiohttp internals; a report must demonstrate how an
+ attacker can actually exploit it.
+- Any report about excessive memory use must generally use a payload of
+ atlest 1 MiB and show that it completely bypasses any size restrictions
+ (asyncio reads upto 256 KiB from the socket at a time, so many parts of
+ aiohttp assume that 256 KiB are being loaded into memory all the time for
+ every request).
From 7b899aa54de455b912fc7874d17c54bc8bad28d4 Mon Sep 17 00:00:00 2001
From: Sam Bull
Date: Sun, 14 Jun 2026 20:07:31 +0100
Subject: [PATCH 027/137] Return ResourceRoute from .set_options_route()
(#12917) (#12930)
(cherry picked from commit 9ad62d093fafb2e64b589b1f23620bfedc799732)
---
CHANGES/12917.feature.rst | 2 ++
aiohttp/web_urldispatcher.py | 6 ++++--
docs/web_reference.rst | 15 +++++++++++++++
tests/test_urldispatch.py | 6 +++++-
4 files changed, 26 insertions(+), 3 deletions(-)
create mode 100644 CHANGES/12917.feature.rst
diff --git a/CHANGES/12917.feature.rst b/CHANGES/12917.feature.rst
new file mode 100644
index 00000000000..773c2ff5d90
--- /dev/null
+++ b/CHANGES/12917.feature.rst
@@ -0,0 +1,2 @@
+Changed ``StaticResource.set_options_route()`` to return the
+created ``ResourceRoute``, matching ``Resource.add_route`` -- by :user:`Dreamsorcerer`.
diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py
index 74351eecb06..b49307948e0 100644
--- a/aiohttp/web_urldispatcher.py
+++ b/aiohttp/web_urldispatcher.py
@@ -620,13 +620,15 @@ def get_info(self) -> _InfoDict:
"routes": self._routes,
}
- def set_options_route(self, handler: Handler) -> None:
+ def set_options_route(self, handler: Handler) -> "ResourceRoute":
if "OPTIONS" in self._routes:
raise RuntimeError("OPTIONS route was set already")
- self._routes["OPTIONS"] = ResourceRoute(
+ route = ResourceRoute(
"OPTIONS", handler, self, expect_handler=self._expect_handler
)
+ self._routes["OPTIONS"] = route
self._allowed_methods.add("OPTIONS")
+ return route
async def resolve(self, request: Request) -> _Resolve:
path = request.rel_url.path_safe
diff --git a/docs/web_reference.rst b/docs/web_reference.rst
index 5932d016536..811ed9fc97d 100644
--- a/docs/web_reference.rst
+++ b/docs/web_reference.rst
@@ -2368,6 +2368,21 @@ Resource classes hierarchy::
if file not found has no impact
+ .. method:: set_options_route(handler)
+
+ Register *handler* as the ``OPTIONS`` route for this resource.
+
+ Raises :exc:`RuntimeError` if an ``OPTIONS`` route was already set.
+
+ :param handler: a :ref:`web-handler` for
+ ``OPTIONS`` requests.
+
+ :return: the newly created :class:`ResourceRoute`.
+
+ .. versionchanged:: 3.15
+
+ Now returns the created :class:`ResourceRoute` instead of ``None``.
+
.. class:: PrefixedSubAppResource
diff --git a/tests/test_urldispatch.py b/tests/test_urldispatch.py
index 8d1134ed8d1..49fbf2951c7 100644
--- a/tests/test_urldispatch.py
+++ b/tests/test_urldispatch.py
@@ -520,7 +520,11 @@ async def test_add_static_set_options_route(router: web.UrlDispatcher) -> None:
async def handler(request: web.Request) -> NoReturn:
assert False
- resource.set_options_route(handler)
+ route = resource.set_options_route(handler)
+ assert isinstance(route, web.ResourceRoute)
+ assert route.method == hdrs.METH_OPTIONS
+ assert route.handler is handler
+ assert route.resource is resource
mapping, allowed_methods = await resource.resolve(
make_mocked_request("OPTIONS", "/st/path")
)
From ec86af4b0dac66b692a23594f78c938ddcc2e2ee Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 15 Jun 2026 10:55:03 +0000
Subject: [PATCH 028/137] Bump pypa/cibuildwheel from 4.0.0 to 4.1.0 (#12933)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from
4.0.0 to 4.1.0.
Release notes
β¨ Updates Pyodide to the final 314.0.0 release, so Pyodide 3.14
wheels now build by default without the pyodide-prereleaseenable
flag. (#2906)
π Raises clear errors when a build produces no wheel, instead of
failing later with a confusing message (#2909)
π Speeds up CLI startup through lazy imports on Python 3.15 (#2797)
π Adds an FAQ section on caching cibuildwheel's downloaded tools
with CIBW_CACHE_PATH (#2842)
π Documentation improvements: clarifies which shell is used for
command options, clarifies environment variable precedence, and fixes a
dead Pyodide env info link (#2904,
#2905,
#2911)
β¨ Updates Pyodide to the final 314.0.0 release, so Pyodide 3.14
wheels now build by default without the pyodide-prereleaseenable
flag. (#2906)
π Raises clear errors when a build produces no wheel, instead of
failing later with a confusing message (#2909)
π Speeds up CLI startup through lazy imports on Python 3.15 (#2797)
π Adds an FAQ section on caching cibuildwheel's downloaded tools
with CIBW_CACHE_PATH (#2842)
π Documentation improvements: clarifies which shell is used for
command options, clarifies environment variable precedence, and fixes a
dead Pyodide env info link (#2904,
#2905,
#2911)
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci-cd.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 5478fe3171a..5884fa7d81b 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -665,7 +665,7 @@ jobs:
run: |
make cythonize
- name: Build wheels
- uses: pypa/cibuildwheel@v4.0.0
+ uses: pypa/cibuildwheel@v4.1.0
with:
# `build-frontend = "build[uv]"` (pyproject.toml) requires uv to be
# available on the runner for Windows and macOS. Installing
From b88de9278a02d3ab202eaf6c439bfb9e18d83f0c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 15 Jun 2026 11:27:59 +0000
Subject: [PATCH 029/137] Bump pytest from 9.0.3 to 9.1.0 (#12935)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.3 to
9.1.0.
Release notes
Removals and backward incompatible breaking changes
#14533:
When using --doctest-modules, autouse fixtures with
module, package or session scope
that are defined inline in Python test modules (not plugins or
conftests) will now possibly execute twice.
If this is undesirable, move the fixture definition to a
conftest.py file if possible.
Technical explanation for those interested:
When using --doctest-modules, pytest possibly collects Python modules twice, once as
pytest.Module and once as a DoctestModule
(depending on the configuration).
Due to improvements in pytest's fixture implementation, if e.g. the
DoctestModule collects a fixture, it is now visible to it
only, and not to the Module.
This means that both need to register the fixtures independently.
Deprecations (removal in next major release)
#10819:
Added a deprecation warning for class-scoped fixtures defined as
instance methods (without @classmethod). Such fixtures set
attributes on a different instance than the test methods use, leading to
unexpected behavior. Use @classmethod decorator instead --
by yastcher.
See 10819 and 14011.
#12882:
Calling request.getfixturevalue()
<pytest.FixtureRequest.getfixturevalue> during teardown to
request a fixture that was not already requested is now deprecated and
will become an error in pytest 10.
See dynamic-fixture-request-during-teardown for
details.
#13409:
Using non-~collections.abc.Collection iterables (such as
generators, iterators, or custom iterable objects) for the
argvalues parameter in @pytest.mark.parametrize
<pytest.mark.parametrize ref> and
metafunc.parametrize <pytest.Metafunc.parametrize> is
now deprecated.
These iterables get exhausted after the first iteration,
leading to tests getting unexpectedly skipped in cases such as running
pytest.main() multiple times,
using class-level parametrize decorators,
or collecting tests multiple times.
See parametrize-iterators for details and
suggestions.
#13946:
The private config.inicfg attribute is now deprecated.
Use config.getini() <pytest.Config.getini> to access
configuration values instead.
See config-inicfg for more details.
#14004:
Passing baseid to ~pytest.FixtureDef or
nodeid strings to fixture registration APIs is now
deprecated. These are internal pytest APIs that are used by some
plugins.
Use the node parameter instead for fixture scoping. This
enables more robust node-based
matching instead of string prefix matching.
If you've used nodeid=None, pass node=session
instead.
This will be removed in pytest 10.
#14335:
The method of configuring hooks using markers, deprecated since pytest
7.2, is now scheduled to be removed in pytest 10.
See hook-markers for more details.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
requirements/test-common-base.txt | 2 +-
requirements/test-common.txt | 2 +-
requirements/test-ft.txt | 2 +-
requirements/test-mobile.txt | 2 +-
requirements/test.txt | 2 +-
8 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 8532cc81ea9..bf55a4737e4 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -189,7 +189,7 @@ pyproject-hooks==1.2.0
# via
# build
# pip-tools
-pytest==9.0.3
+pytest==9.1.0
# via
# -r requirements/lint.in
# -r requirements/test-common-base.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 7f03cf37afa..a7359a2657d 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -184,7 +184,7 @@ pyproject-hooks==1.2.0
# via
# build
# pip-tools
-pytest==9.0.3
+pytest==9.1.0
# via
# -r requirements/lint.in
# -r requirements/test-common-base.in
diff --git a/requirements/lint.txt b/requirements/lint.txt
index f6cea4f0bc7..2de3a877182 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -82,7 +82,7 @@ pygments==2.20.0
# via
# pytest
# rich
-pytest==9.0.3
+pytest==9.1.0
# via
# -r requirements/lint.in
# pytest-codspeed
diff --git a/requirements/test-common-base.txt b/requirements/test-common-base.txt
index 1a8e58dcfce..26d7d72738d 100644
--- a/requirements/test-common-base.txt
+++ b/requirements/test-common-base.txt
@@ -26,7 +26,7 @@ proxy-py==2.4.10
# via -r requirements/test-common-base.in
pygments==2.20.0
# via pytest
-pytest==9.0.3
+pytest==9.1.0
# via
# -r requirements/test-common-base.in
# pytest-cov
diff --git a/requirements/test-common.txt b/requirements/test-common.txt
index 3bafeed46fe..ca0a16320e4 100644
--- a/requirements/test-common.txt
+++ b/requirements/test-common.txt
@@ -66,7 +66,7 @@ pygments==2.20.0
# via
# pytest
# rich
-pytest==9.0.3
+pytest==9.1.0
# via
# -r requirements/test-common-base.in
# pytest-codspeed
diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt
index f7d399bce14..88b3bb3e53a 100644
--- a/requirements/test-ft.txt
+++ b/requirements/test-ft.txt
@@ -102,7 +102,7 @@ pygments==2.20.0
# via
# pytest
# rich
-pytest==9.0.3
+pytest==9.1.0
# via
# -r requirements/test-common-base.in
# pytest-codspeed
diff --git a/requirements/test-mobile.txt b/requirements/test-mobile.txt
index 907bc041b1d..6cb95848459 100644
--- a/requirements/test-mobile.txt
+++ b/requirements/test-mobile.txt
@@ -70,7 +70,7 @@ pycparser==3.0
# via cffi
pygments==2.20.0
# via pytest
-pytest==9.0.3
+pytest==9.1.0
# via
# -r requirements/test-common-base.in
# pytest-cov
diff --git a/requirements/test.txt b/requirements/test.txt
index 40ccbfadd71..64b6826bf67 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -102,7 +102,7 @@ pygments==2.20.0
# via
# pytest
# rich
-pytest==9.0.3
+pytest==9.1.0
# via
# -r requirements/test-common-base.in
# pytest-codspeed
From 693235b11a74899c4c4b3a257921f3c69beadcbe Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 15 Jun 2026 11:47:06 +0000
Subject: [PATCH 030/137] Bump virtualenv from 21.4.3 to 21.5.0 (#12939)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [virtualenv](https://github.com/pypa/virtualenv) from 21.4.3 to
21.5.0.
Release notes
Drop support for Python 3.8; virtualenv now requires Python 3.9 or
later to run and to create environments. Remove the
embedded wheel seed package, which virtualenv bundled only
for Python 3.8. The --wheel and --no-wheel
options stay as no-ops, but now warn that virtualenv will remove them in
a release after 2026-12 - by
:user:gaborbernat. (:issue:3170)
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index bf55a4737e4..326a8b5189d 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -307,7 +307,7 @@ uvloop==0.22.1 ; platform_system != "Windows"
# -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.4.3
+virtualenv==21.5.0
# via pre-commit
wait-for-it==2.3.0
# via -r requirements/test-common-base.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index a7359a2657d..23bbe416a64 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -297,7 +297,7 @@ uvloop==0.22.1 ; platform_system != "Windows" and implementation_name == "cpytho
# -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.4.3
+virtualenv==21.5.0
# via pre-commit
wait-for-it==2.3.0
# via -r requirements/test-common-base.in
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 2de3a877182..cbd02a39e0f 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -128,7 +128,7 @@ uvloop==0.22.1 ; platform_system != "Windows"
# via -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.4.3
+virtualenv==21.5.0
# via pre-commit
zlib-ng==1.0.0
# via -r requirements/lint.in
From cf74b0fec1c0e63adc6659010cdca9e35dfbf66b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 15 Jun 2026 11:49:30 +0000
Subject: [PATCH 031/137] Bump cryptography from 48.0.1 to 49.0.0 (#12937)
Bumps [cryptography](https://github.com/pyca/cryptography) from 48.0.1
to 49.0.0.
Changelog
* **BACKWARDS INCOMPATIBLE:** Support for ``x86_64`` macOS has been
removed.
We now only publish ``arm64`` wheels for macOS.
* **BACKWARDS INCOMPATIBLE:** Support for 32-bit Windows has been
removed.
Users should move to a 64-bit Python installation.
* **BACKWARDS INCOMPATIBLE:** Removed the deprecated
``PUBLIC_KEY_TYPES``, ``PRIVATE_KEY_TYPES``,
``CERTIFICATE_PRIVATE_KEY_TYPES``,
``CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES``,
and ``CERTIFICATE_PUBLIC_KEY_TYPES`` type aliases. Use
``PublicKeyTypes``, ``PrivateKeyTypes``,
``CertificateIssuerPrivateKeyTypes``,
``CertificateIssuerPublicKeyTypes``, and ``CertificatePublicKeyTypes``
instead. These were deprecated in version 40.0.
* **BACKWARDS INCOMPATIBLE:**
:class:`~cryptography.hazmat.primitives.ciphers.algorithms.ChaCha20`
now treats the first 4 bytes of the ``nonce`` as a 32-bit little-endian
block
counter (as defined in :rfc:`7539`) and tracks the number of bytes
processed.
Attempting to encrypt or decrypt more data than the counter allows
before it
would overflow now raises a :class:`ValueError` rather than silently
diverging
from RFC 7539. Setting the counter portion of the ``nonce`` to zero
allows
encrypting up to 256 GiB with a given nonce.
* **BACKWARDS INCOMPATIBLE:** Loading an X.509 certificate whose ECDSA
or DSA
signature ``AlgorithmIdentifier`` contains encoded NULL parameters now
raises
a :class:`ValueError`. Such certificates are invalid, but older versions
of
Java emitted them; previously they loaded with a deprecation warning.
* Fixed cross-compilation of the CFFI bindings when
``PYO3_CROSS_LIB_DIR``
is set. The build now derives the Python include directory from
``PYO3_CROSS_LIB_DIR`` instead of querying the host interpreter, which
previously caused the build to fail during cross-compilations for
embedded
systems, on hosts which have same-version Python development headers
installed as the target Python.
* Added support for signing and verifying X.509 certificates,
certificate
signing requests, and certificate revocation lists with
:doc:`/hazmat/primitives/asymmetric/mldsa` keys, as well as loading
certificates that contain ML-DSA public keys.
* Added :meth:`~cryptography.hazmat.primitives.hpke.KEM.enc_length` to
:class:`~cryptography.hazmat.primitives.hpke.KEM` so callers can split
the
encapsulated key from the ciphertext returned by
:meth:`~cryptography.hazmat.primitives.hpke.Suite.encrypt`.
*
:meth:`~cryptography.x509.verification.ExtensionPolicy.require_present`,
:meth:`~cryptography.x509.verification.ExtensionPolicy.may_be_present`,
and
:meth:`~cryptography.x509.verification.ExtensionPolicy.require_not_present`
now accept any extension type. Previously only a fixed set of extension
types was supported, which made it impossible to account for otherwise
unrecognized critical extensions during path validation.
* Added support for using :class:`~cryptography.x509.Certificate`,
:class:`~cryptography.x509.CertificateSigningRequest`, and
:class:`~cryptography.x509.CertificateRevocationList` as field types in
:doc:`/hazmat/asn1/index` structures.
* Added :func:`~cryptography.hazmat.asn1.value_set`, a class decorator
that
</tr></table>
... (truncated)
Commits
e300bbe
bump version and changelog for 49.0.0 (#15030)
fa74cd8
Add external mu (message representative) support for ML-DSA (#14979)
f594db3
chore(deps): bump openssl from 0.10.80 to 0.10.81 (#15029)
608e011
chore(deps): bump openssl-sys from 0.9.116 to 0.9.117 (#15028)
a322bc4
chore(deps): bump cc from 1.2.63 to 1.2.64 (#15027)
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
requirements/test-common.txt | 2 +-
requirements/test-ft.txt | 2 +-
requirements/test.txt | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 326a8b5189d..a550bd8941a 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -60,7 +60,7 @@ coverage==7.14.1
# via
# -r requirements/test-common.in
# pytest-cov
-cryptography==48.0.1
+cryptography==49.0.0
# via trustme
cython==3.2.5
# via -r requirements/cython.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 23bbe416a64..0f2cd51094a 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -60,7 +60,7 @@ coverage==7.14.1
# via
# -r requirements/test-common.in
# pytest-cov
-cryptography==48.0.1
+cryptography==49.0.0
# via trustme
distlib==0.4.3
# via virtualenv
diff --git a/requirements/lint.txt b/requirements/lint.txt
index cbd02a39e0f..5f7798f896a 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -24,7 +24,7 @@ cfgv==3.5.0
# via pre-commit
click==8.4.1
# via slotscheck
-cryptography==48.0.1
+cryptography==49.0.0
# via trustme
distlib==0.4.3
# via virtualenv
diff --git a/requirements/test-common.txt b/requirements/test-common.txt
index ca0a16320e4..7d2ed8fa4fc 100644
--- a/requirements/test-common.txt
+++ b/requirements/test-common.txt
@@ -18,7 +18,7 @@ coverage==7.14.1
# via
# -r requirements/test-common.in
# pytest-cov
-cryptography==48.0.1
+cryptography==49.0.0
# via trustme
exceptiongroup==1.3.1
# via pytest
diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt
index 88b3bb3e53a..0496da82e69 100644
--- a/requirements/test-ft.txt
+++ b/requirements/test-ft.txt
@@ -34,7 +34,7 @@ coverage==7.14.1
# via
# -r requirements/test-common.in
# pytest-cov
-cryptography==48.0.1
+cryptography==49.0.0
# via trustme
exceptiongroup==1.3.1
# via pytest
diff --git a/requirements/test.txt b/requirements/test.txt
index 64b6826bf67..4930af9466e 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -34,7 +34,7 @@ coverage==7.14.1
# via
# -r requirements/test-common.in
# pytest-cov
-cryptography==48.0.1
+cryptography==49.0.0
# via trustme
exceptiongroup==1.3.1
# via pytest
From 9c8ae422da361771e6a4a78181e6c82c3d98a056 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 15 Jun 2026 11:57:30 +0000
Subject: [PATCH 032/137] Bump filelock from 3.29.3 to 3.29.4 (#12941)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.29.3 to
3.29.4.
Release notes
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index a550bd8941a..88ad90a18bf 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -74,7 +74,7 @@ exceptiongroup==1.3.1
# via pytest
execnet==2.1.2
# via pytest-xdist
-filelock==3.29.3
+filelock==3.29.4
# via
# python-discovery
# virtualenv
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 0f2cd51094a..e4171c5e7c4 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -72,7 +72,7 @@ exceptiongroup==1.3.1
# via pytest
execnet==2.1.2
# via pytest-xdist
-filelock==3.29.3
+filelock==3.29.4
# via
# python-discovery
# virtualenv
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 5f7798f896a..026bfbacb3d 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -30,7 +30,7 @@ distlib==0.4.3
# via virtualenv
exceptiongroup==1.3.1
# via pytest
-filelock==3.29.3
+filelock==3.29.4
# via
# python-discovery
# virtualenv
From afd8e342bac43bf7f180740231637c6678040294 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 17 Jun 2026 11:08:08 +0000
Subject: [PATCH 033/137] Bump virtualenv from 21.5.0 to 21.5.1 (#12949)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [virtualenv](https://github.com/pypa/virtualenv) from 21.5.0 to
21.5.1.
Release notes
Refuse to create environments whose Python the bundled wheels no
longer cover (currently below 3.9). virtualenv used
to substitute the newest bundled pip, which cannot run on
such a target, leaving a broken environment; seeder
selection now rejects it up front with a clear error.
--no-seed and third-party seeders that ship compatible
wheels still work - by :user:gaborbernat.
(:issue:3171)
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 88ad90a18bf..cad5dc730e2 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -307,7 +307,7 @@ uvloop==0.22.1 ; platform_system != "Windows"
# -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.5.0
+virtualenv==21.5.1
# via pre-commit
wait-for-it==2.3.0
# via -r requirements/test-common-base.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index e4171c5e7c4..3b123b7667b 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -297,7 +297,7 @@ uvloop==0.22.1 ; platform_system != "Windows" and implementation_name == "cpytho
# -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.5.0
+virtualenv==21.5.1
# via pre-commit
wait-for-it==2.3.0
# via -r requirements/test-common-base.in
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 026bfbacb3d..7368a3c6163 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -128,7 +128,7 @@ uvloop==0.22.1 ; platform_system != "Windows"
# via -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.5.0
+virtualenv==21.5.1
# via pre-commit
zlib-ng==1.0.0
# via -r requirements/lint.in
From d16f3683af05f71de04f6fa47636ed4a5d5e8c9a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 17 Jun 2026 11:13:04 +0000
Subject: [PATCH 034/137] Bump certifi from 2026.5.20 to 2026.6.17 (#12951)
Bumps [certifi](https://github.com/certifi/python-certifi) from
2026.5.20 to 2026.6.17.
Commits
Bump actions/publish-immutable-action from 0.0.3 to 0.0.4 in the
minor-actions-dependencies group across 1 directory by @βdependabot[bot]
in actions/checkout#2458
Bump actions/publish-immutable-action from 0.0.3 to 0.0.4 in the
minor-actions-dependencies group across 1 directory by @βdependabot[bot]
in actions/checkout#2458
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci-cd.yml | 20 ++++++++++----------
.github/workflows/codeql.yml | 2 +-
.github/workflows/dependency-submission.yml | 2 +-
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 5884fa7d81b..b83ba79af52 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -56,7 +56,7 @@ jobs:
timeout-minutes: 5
steps:
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: true
- name: >-
@@ -113,7 +113,7 @@ jobs:
timeout-minutes: 5
steps:
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: true
- name: Cache llhttp generated files
@@ -167,7 +167,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: true
- name: Setup Python ${{ matrix.pyver }}
@@ -274,7 +274,7 @@ jobs:
archs: arm64_iphonesimulator
steps:
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: true
- name: Setup Python ${{ matrix.pyver }}
@@ -338,7 +338,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: true
- name: Setup Python ${{ matrix.pyver }}
@@ -417,7 +417,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout project
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: true
- name: Setup Python 3.13.2
@@ -460,7 +460,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: true
- name: Setup Python
@@ -547,7 +547,7 @@ jobs:
needs: pre-deploy
steps:
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: true
- name: Setup Python
@@ -624,7 +624,7 @@ jobs:
platform: ios
steps:
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: true
- name: Set up QEMU
@@ -714,7 +714,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: true
- name: Login
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 797b7591cd8..445a2efd906 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -26,7 +26,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
diff --git a/.github/workflows/dependency-submission.yml b/.github/workflows/dependency-submission.yml
index 9a499f22629..75e265936dc 100644
--- a/.github/workflows/dependency-submission.yml
+++ b/.github/workflows/dependency-submission.yml
@@ -23,7 +23,7 @@ jobs:
contents: write # Required by the Dependency submission API
steps:
- name: Checkout
- uses: actions/checkout@v6
+ uses: actions/checkout@v7
with:
submodules: true
- name: Read vendored llhttp version
From eae0621fa1f6b56e1107c161d347a2bbf16341d3 Mon Sep 17 00:00:00 2001
From: Sergei Grachev
Date: Fri, 19 Jun 2026 15:18:10 +0200
Subject: [PATCH 044/137] [PR #12954/5c293f4f backport][3.15] Don't re-arm read
timeout on a connection returned to the pool (#12967)
**This is a backport of PR #12954 as merged into master
(5c293f4f71f6188b446afd331afa47262a874f4f).**
---
CHANGES/12953.bugfix.rst | 6 ++++++
CHANGES/12954.bugfix.rst | 1 +
CONTRIBUTORS.txt | 1 +
aiohttp/client_proto.py | 4 +++-
tests/test_client_functional.py | 37 +++++++++++++++++++++++++++++++++
5 files changed, 48 insertions(+), 1 deletion(-)
create mode 100644 CHANGES/12953.bugfix.rst
create mode 120000 CHANGES/12954.bugfix.rst
diff --git a/CHANGES/12953.bugfix.rst b/CHANGES/12953.bugfix.rst
new file mode 100644
index 00000000000..20edc2dda87
--- /dev/null
+++ b/CHANGES/12953.bugfix.rst
@@ -0,0 +1,6 @@
+Fixed the ``sock_read`` timeout being re-armed on a keep-alive connection after
+it had been returned to the pool. An idle pooled connection could be left with a
+pending read timeout that fired and poisoned it, so the next request reusing the
+connection failed immediately with :exc:`aiohttp.SocketTimeoutError`. The read
+timeout is now only rescheduled when resuming a transport that was actually
+paused -- by :user:`daragok`.
diff --git a/CHANGES/12954.bugfix.rst b/CHANGES/12954.bugfix.rst
new file mode 120000
index 00000000000..baccfb94cc8
--- /dev/null
+++ b/CHANGES/12954.bugfix.rst
@@ -0,0 +1 @@
+12953.bugfix.rst
\ No newline at end of file
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 830d86cb3a6..6ce89d092ae 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -334,6 +334,7 @@ Sebastian Hanula
Sebastian HΓΌther
Sebastien Geffroy
SeongSoo Cho
+Sergei Grachev
Sergey Ninua
Sergey Skripnick
Serhii Charykov
diff --git a/aiohttp/client_proto.py b/aiohttp/client_proto.py
index a0b8512af9b..2bac26b24d4 100644
--- a/aiohttp/client_proto.py
+++ b/aiohttp/client_proto.py
@@ -190,8 +190,10 @@ def pause_reading(self) -> None:
self._drop_timeout()
def resume_reading(self, resume_parser: bool = True) -> None:
+ was_paused = self._reading_paused
super().resume_reading(resume_parser)
- self._reschedule_timeout()
+ if was_paused:
+ self._reschedule_timeout()
def set_exception(
self,
diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py
index 6bd57bee612..524c4a2efcd 100644
--- a/tests/test_client_functional.py
+++ b/tests/test_client_functional.py
@@ -1251,6 +1251,43 @@ async def handler(request: web.Request) -> web.Response:
assert result == b"foo"
+async def test_sock_read_timeout_not_rearmed_on_pooled_connection(
+ aiohttp_client: AiohttpClient,
+) -> None:
+ # Reading the buffered body of a completed response must not re-arm the
+ # sock_read timeout on a connection that has already been released to the
+ # keep-alive pool. Otherwise the timer fires while the connection sits idle
+ # in the pool, stamps SocketTimeoutError on it, and the next request that
+ # reuses it fails immediately (with no real read having stalled).
+ async def handler(request: web.Request) -> web.Response:
+ return web.json_response({"ok": True})
+
+ app = web.Application()
+ app.router.add_get("/", handler)
+
+ timeout = aiohttp.ClientTimeout(total=30, sock_read=0.1)
+ client = await aiohttp_client(app, timeout=timeout)
+
+ async with client.get("/") as resp:
+ assert resp.status == 200
+ await resp.read()
+
+ assert client.session.connector is not None
+ pooled = next(iter(client.session.connector._conns.values()))
+ proto = pooled[0][0]
+ # The pooled connection must carry no read-timeout handle, otherwise
+ # it could trigger an exception on the next request.
+ assert proto._read_timeout_handle is None
+ assert proto.exception() is None
+
+ # The connection is still reusable.
+ async with client.get("/") as resp:
+ assert resp.status == 200
+ assert await resp.json() == {"ok": True}
+
+ assert next(iter(client.session.connector._conns.values()))[0][0] is proto
+
+
async def test_timeout_on_reading_data(aiohttp_client, mocker) -> None:
loop = asyncio.get_event_loop()
From 4cce8a5f551227633f6afb6bb694086029080030 Mon Sep 17 00:00:00 2001
From: Sergei Grachev
Date: Fri, 19 Jun 2026 15:18:32 +0200
Subject: [PATCH 045/137] [PR #12954/5c293f4f backport][3.14] Don't re-arm read
timeout on a connection returned to the pool (#12966)
**This is a backport of PR #12954 as merged into master
(5c293f4f71f6188b446afd331afa47262a874f4f).**
---
CHANGES/12953.bugfix.rst | 6 ++++++
CHANGES/12954.bugfix.rst | 1 +
CONTRIBUTORS.txt | 1 +
aiohttp/client_proto.py | 4 +++-
tests/test_client_functional.py | 37 +++++++++++++++++++++++++++++++++
5 files changed, 48 insertions(+), 1 deletion(-)
create mode 100644 CHANGES/12953.bugfix.rst
create mode 120000 CHANGES/12954.bugfix.rst
diff --git a/CHANGES/12953.bugfix.rst b/CHANGES/12953.bugfix.rst
new file mode 100644
index 00000000000..20edc2dda87
--- /dev/null
+++ b/CHANGES/12953.bugfix.rst
@@ -0,0 +1,6 @@
+Fixed the ``sock_read`` timeout being re-armed on a keep-alive connection after
+it had been returned to the pool. An idle pooled connection could be left with a
+pending read timeout that fired and poisoned it, so the next request reusing the
+connection failed immediately with :exc:`aiohttp.SocketTimeoutError`. The read
+timeout is now only rescheduled when resuming a transport that was actually
+paused -- by :user:`daragok`.
diff --git a/CHANGES/12954.bugfix.rst b/CHANGES/12954.bugfix.rst
new file mode 120000
index 00000000000..baccfb94cc8
--- /dev/null
+++ b/CHANGES/12954.bugfix.rst
@@ -0,0 +1 @@
+12953.bugfix.rst
\ No newline at end of file
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 830d86cb3a6..6ce89d092ae 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -334,6 +334,7 @@ Sebastian Hanula
Sebastian HΓΌther
Sebastien Geffroy
SeongSoo Cho
+Sergei Grachev
Sergey Ninua
Sergey Skripnick
Serhii Charykov
diff --git a/aiohttp/client_proto.py b/aiohttp/client_proto.py
index a0b8512af9b..2bac26b24d4 100644
--- a/aiohttp/client_proto.py
+++ b/aiohttp/client_proto.py
@@ -190,8 +190,10 @@ def pause_reading(self) -> None:
self._drop_timeout()
def resume_reading(self, resume_parser: bool = True) -> None:
+ was_paused = self._reading_paused
super().resume_reading(resume_parser)
- self._reschedule_timeout()
+ if was_paused:
+ self._reschedule_timeout()
def set_exception(
self,
diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py
index 6bd57bee612..524c4a2efcd 100644
--- a/tests/test_client_functional.py
+++ b/tests/test_client_functional.py
@@ -1251,6 +1251,43 @@ async def handler(request: web.Request) -> web.Response:
assert result == b"foo"
+async def test_sock_read_timeout_not_rearmed_on_pooled_connection(
+ aiohttp_client: AiohttpClient,
+) -> None:
+ # Reading the buffered body of a completed response must not re-arm the
+ # sock_read timeout on a connection that has already been released to the
+ # keep-alive pool. Otherwise the timer fires while the connection sits idle
+ # in the pool, stamps SocketTimeoutError on it, and the next request that
+ # reuses it fails immediately (with no real read having stalled).
+ async def handler(request: web.Request) -> web.Response:
+ return web.json_response({"ok": True})
+
+ app = web.Application()
+ app.router.add_get("/", handler)
+
+ timeout = aiohttp.ClientTimeout(total=30, sock_read=0.1)
+ client = await aiohttp_client(app, timeout=timeout)
+
+ async with client.get("/") as resp:
+ assert resp.status == 200
+ await resp.read()
+
+ assert client.session.connector is not None
+ pooled = next(iter(client.session.connector._conns.values()))
+ proto = pooled[0][0]
+ # The pooled connection must carry no read-timeout handle, otherwise
+ # it could trigger an exception on the next request.
+ assert proto._read_timeout_handle is None
+ assert proto.exception() is None
+
+ # The connection is still reusable.
+ async with client.get("/") as resp:
+ assert resp.status == 200
+ assert await resp.json() == {"ok": True}
+
+ assert next(iter(client.session.connector._conns.values()))[0][0] is proto
+
+
async def test_timeout_on_reading_data(aiohttp_client, mocker) -> None:
loop = asyncio.get_event_loop()
From ced30b9d8c68139ba2a14f616def1ee92194247a Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Sun, 21 Jun 2026 01:57:10 +0100
Subject: [PATCH 046/137] [PR #12946/b9ef540d backport][3.15] Free up disk
space for Android emulator in CI (#12971)
**This is a backport of PR #12946 as merged into master
(b9ef540d67accd5df0f0368fca42c8fc2304fced).**
---
.github/workflows/ci-cd.yml | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index b83ba79af52..2d5f15510f8 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -298,6 +298,14 @@ jobs:
- name: Cythonize
run: |
make cythonize
+ - name: Free up disk space for Android emulator
+ if: ${{ matrix.config.platform == 'android' }}
+ uses: BRAINSia/free-disk-space@v2.1.3
+ with:
+ android: false
+ docker-images: false
+ mandb: false
+ large-packages: false
- name: Enable KVM group perms for Android emulator
if: ${{ matrix.config.platform == 'android' }}
# This is normally done by cibuildwheel automatically, when it detects Github Actions. But by unsetting GITHUB_ACTIONS
From c5293938af13b3efbcd77619d34501cbaa255c2a Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Sun, 21 Jun 2026 01:57:31 +0100
Subject: [PATCH 047/137] [PR #12946/b9ef540d backport][3.14] Free up disk
space for Android emulator in CI (#12970)
**This is a backport of PR #12946 as merged into master
(b9ef540d67accd5df0f0368fca42c8fc2304fced).**
---
.github/workflows/ci-cd.yml | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 8e435a56bbe..b13477ce446 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -298,6 +298,14 @@ jobs:
- name: Cythonize
run: |
make cythonize
+ - name: Free up disk space for Android emulator
+ if: ${{ matrix.config.platform == 'android' }}
+ uses: BRAINSia/free-disk-space@v2.1.3
+ with:
+ android: false
+ docker-images: false
+ mandb: false
+ large-packages: false
- name: Enable KVM group perms for Android emulator
if: ${{ matrix.config.platform == 'android' }}
# This is normally done by cibuildwheel automatically, when it detects Github Actions. But by unsetting GITHUB_ACTIONS
From c2883123dea10ed7394d64b916c0853cb35ad199 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 22 Jun 2026 11:13:22 +0000
Subject: [PATCH 048/137] Bump pytest from 9.1.0 to 9.1.1 (#12974)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.1.0 to
9.1.1.
Release notes
#14220:
Fixed a logic bug in pytest.RaisesGroup which would might
cause it to display incorrect "It matches FooError() which was paired with BarError" messages.
#14591:
Fixed a regression in pytest 9.1.0 which caused overriding a
parametrized fixture with an indirect @βpytest.mark.parametrize to fail with "duplicate parametrization of
'<fixture name>'".
#14606:
Fixed list-item typing errors from mypy in
@pytest.mark.parametrize <pytest.mark.parametrize
ref>argvalues parameter.
#14608:
Fixed a regression in pytest 9.1.0 where conftest.py files
located in <invocation dir>/test* were no longer
loaded as initial conftests when invoked without arguments.
This could cause certain hooks (like pytest_addoption) in
these files to not fire.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
requirements/test-common-base.txt | 2 +-
requirements/test-common.txt | 2 +-
requirements/test-ft.txt | 2 +-
requirements/test-mobile.txt | 2 +-
requirements/test.txt | 2 +-
8 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index e245a276c4e..1ecda530527 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -189,7 +189,7 @@ pyproject-hooks==1.2.0
# via
# build
# pip-tools
-pytest==9.1.0
+pytest==9.1.1
# via
# -r requirements/lint.in
# -r requirements/test-common-base.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index c1212e57885..75c6840db77 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -184,7 +184,7 @@ pyproject-hooks==1.2.0
# via
# build
# pip-tools
-pytest==9.1.0
+pytest==9.1.1
# via
# -r requirements/lint.in
# -r requirements/test-common-base.in
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 7368a3c6163..51f167d4840 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -82,7 +82,7 @@ pygments==2.20.0
# via
# pytest
# rich
-pytest==9.1.0
+pytest==9.1.1
# via
# -r requirements/lint.in
# pytest-codspeed
diff --git a/requirements/test-common-base.txt b/requirements/test-common-base.txt
index 26d7d72738d..6ae41a8d067 100644
--- a/requirements/test-common-base.txt
+++ b/requirements/test-common-base.txt
@@ -26,7 +26,7 @@ proxy-py==2.4.10
# via -r requirements/test-common-base.in
pygments==2.20.0
# via pytest
-pytest==9.1.0
+pytest==9.1.1
# via
# -r requirements/test-common-base.in
# pytest-cov
diff --git a/requirements/test-common.txt b/requirements/test-common.txt
index 7d2ed8fa4fc..d7a299a442b 100644
--- a/requirements/test-common.txt
+++ b/requirements/test-common.txt
@@ -66,7 +66,7 @@ pygments==2.20.0
# via
# pytest
# rich
-pytest==9.1.0
+pytest==9.1.1
# via
# -r requirements/test-common-base.in
# pytest-codspeed
diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt
index 0496da82e69..3f0752d0c33 100644
--- a/requirements/test-ft.txt
+++ b/requirements/test-ft.txt
@@ -102,7 +102,7 @@ pygments==2.20.0
# via
# pytest
# rich
-pytest==9.1.0
+pytest==9.1.1
# via
# -r requirements/test-common-base.in
# pytest-codspeed
diff --git a/requirements/test-mobile.txt b/requirements/test-mobile.txt
index 6cb95848459..1740cb6dcbb 100644
--- a/requirements/test-mobile.txt
+++ b/requirements/test-mobile.txt
@@ -70,7 +70,7 @@ pycparser==3.0
# via cffi
pygments==2.20.0
# via pytest
-pytest==9.1.0
+pytest==9.1.1
# via
# -r requirements/test-common-base.in
# pytest-cov
diff --git a/requirements/test.txt b/requirements/test.txt
index 4930af9466e..4fd68d6a9ed 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -102,7 +102,7 @@ pygments==2.20.0
# via
# pytest
# rich
-pytest==9.1.0
+pytest==9.1.1
# via
# -r requirements/test-common-base.in
# pytest-codspeed
From dcee65d34ff3437461fe0a39b511983a33be9c6f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 22 Jun 2026 11:17:55 +0000
Subject: [PATCH 049/137] Bump coverage from 7.14.1 to 7.14.2 (#12975)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.14.1
to 7.14.2.
Changelog
Fix: some messages were being written to stdout, making
coverage json -o - useless for capturing JSON output. Now
messages are written to stderr,
fixing issue 2197_.
Fix: CoverageData kept one SQLite connection per thread
that recorded
coverage, but never closed them when those threads terminated. On long
runs
with many short-lived threads this leaked one file descriptor per dead
thread, eventually failing with OSError: [Errno 24] Too many open
files.
Connections belonging to terminated threads are now closed and dropped.
Fixes issue 2192. Thanks, Matthew Lloyd <pull
2193_>.
Fix: when using sys.monitoring, we were assuming we could use the
COVERAGE_ID tool id. But other tools might also assume they
could use
that id. Pre-allocated ids don't really make sense, so now we search for
a
usable one instead. Fixes issue 2187_.
Following the advice of cibuildwheel <no-13t_>_,
we no longer distribute
wheels for Python 3.13 free-threaded.
Fix: the default ... exclusion rule now also matches
function bodies
whose closing return-type bracket is on its own line (for example, after
a
long -> dict[ ... ] annotation that a formatter has
split over multiple
lines). Closes issue 2185, thanks Mengjia Shang
<pull 2196_>.
Fix: On 3.13t, we incorrectly issued Couldn't import C
tracer errors.
We can't import the C tracer because in 7.14.2 we stopped shipping
compiled
wheels for 3.13t. Thanks, Hugo van Kemenade <pull
2203_>_.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/test-common-base.txt | 2 +-
requirements/test-common.txt | 2 +-
requirements/test-ft.txt | 2 +-
requirements/test-mobile.txt | 2 +-
requirements/test.txt | 2 +-
7 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 4d11e29c33b..e1324708e2e 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -56,7 +56,7 @@ click==8.4.1
# slotscheck
# towncrier
# wait-for-it
-coverage==7.14.2
+coverage==7.14.3
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 73f36742cf7..49c44ae7c66 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -56,7 +56,7 @@ click==8.4.1
# slotscheck
# towncrier
# wait-for-it
-coverage==7.14.2
+coverage==7.14.3
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/test-common-base.txt b/requirements/test-common-base.txt
index da32fe6c19e..eb946d58e35 100644
--- a/requirements/test-common-base.txt
+++ b/requirements/test-common-base.txt
@@ -6,7 +6,7 @@
#
click==8.4.1
# via wait-for-it
-coverage==7.14.2
+coverage==7.14.3
# via pytest-cov
exceptiongroup==1.3.1
# via pytest
diff --git a/requirements/test-common.txt b/requirements/test-common.txt
index 26033e340f5..84c0953fe13 100644
--- a/requirements/test-common.txt
+++ b/requirements/test-common.txt
@@ -14,7 +14,7 @@ cffi==2.0.0
# via cryptography
click==8.4.1
# via wait-for-it
-coverage==7.14.2
+coverage==7.14.3
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt
index 52d6182a86d..97303d3d1d7 100644
--- a/requirements/test-ft.txt
+++ b/requirements/test-ft.txt
@@ -30,7 +30,7 @@ cffi==2.0.0
# pycares
click==8.4.1
# via wait-for-it
-coverage==7.14.2
+coverage==7.14.3
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/test-mobile.txt b/requirements/test-mobile.txt
index b6d9bcb1f44..3ea9f97308d 100644
--- a/requirements/test-mobile.txt
+++ b/requirements/test-mobile.txt
@@ -26,7 +26,7 @@ cffi==2.0.0 ; sys_platform != "android" and sys_platform != "ios"
# pycares
click==8.4.1
# via wait-for-it
-coverage==7.14.2
+coverage==7.14.3
# via pytest-cov
exceptiongroup==1.3.1
# via pytest
diff --git a/requirements/test.txt b/requirements/test.txt
index adedfe7f74a..c74de0ec72a 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -30,7 +30,7 @@ cffi==2.0.0
# pycares
click==8.4.1
# via wait-for-it
-coverage==7.14.2
+coverage==7.14.3
# via
# -r requirements/test-common.in
# pytest-cov
From 4279455d05e135ed46d5dd56ae1de472c8b9c09f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 24 Jun 2026 10:53:31 +0000
Subject: [PATCH 055/137] Bump actions/cache from 5.0.5 to 6.0.0 (#12987)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [actions/cache](https://github.com/actions/cache) from 5.0.5 to
6.0.0.
Release notes
Update @azure/storage-blob to ^12.29.1 via
@actions/cache@5.0.1#1685
5.0.0
[!IMPORTANT]
actions/cache@v5 runs on the Node.js 24 runtime and
requires a minimum Actions Runner version of 2.327.1.
If you are using self-hosted runners, ensure they are updated before
upgrading.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci-cd.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 2d5f15510f8..7f4d2d83d5e 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -71,7 +71,7 @@ jobs:
with:
python-version: 3.11
- name: Cache PyPI
- uses: actions/cache@v5.0.5
+ uses: actions/cache@v6.0.0
with:
key: pip-lint-${{ hashFiles('requirements/*.txt') }}
path: ~/.cache/pip
@@ -117,7 +117,7 @@ jobs:
with:
submodules: true
- name: Cache llhttp generated files
- uses: actions/cache@v5.0.5
+ uses: actions/cache@v6.0.0
id: cache
with:
key: llhttp-${{ hashFiles('vendor/llhttp/package*.json', 'vendor/llhttp/src/**/*') }}
From 947a0e9b73f2c1175232094e6f65e14d0ee1f5eb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 25 Jun 2026 11:02:21 +0000
Subject: [PATCH 056/137] Bump cython from 3.2.5 to 3.2.6 (#12990)
Bumps [cython](https://github.com/cython/cython) from 3.2.5 to 3.2.6.
Changelog
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/cython.in | 2 +-
requirements/cython.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index e1324708e2e..6d59698ede9 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -62,7 +62,7 @@ coverage==7.14.3
# pytest-cov
cryptography==49.0.0
# via trustme
-cython==3.2.5
+cython==3.2.6
# via -r requirements/cython.in
distlib==0.4.3
# via virtualenv
diff --git a/requirements/cython.in b/requirements/cython.in
index c8fcfa99220..9468fa1cd3d 100644
--- a/requirements/cython.in
+++ b/requirements/cython.in
@@ -1,3 +1,3 @@
-r multidict.in
-Cython >= 3.2.5
+Cython >= 3.2.6
diff --git a/requirements/cython.txt b/requirements/cython.txt
index d6e2a89bf4d..a39c83cf8f5 100644
--- a/requirements/cython.txt
+++ b/requirements/cython.txt
@@ -4,7 +4,7 @@
#
# pip-compile --allow-unsafe --output-file=requirements/cython.txt --resolver=backtracking --strip-extras requirements/cython.in
#
-cython==3.2.5
+cython==3.2.6
# via -r requirements/cython.in
multidict==6.7.1
# via -r requirements/multidict.in
From 854f1c6bce1386ccd2509c1b0c45edd318c3d5ff Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 25 Jun 2026 11:10:11 +0000
Subject: [PATCH 057/137] Bump click from 8.4.1 to 8.4.2 (#12992)
Bumps [click](https://github.com/pallets/click) from 8.4.1 to 8.4.2.
Changelog
Fix Fish shell completion broken in 8.4.0 by
{pr}3126. Newlines and
tabs in option help text are now escaped, keeping the original
completion
format while still supporting multi-line help. {issue}3502
{issue}3043 {pr}3504
{pr}3508
Deprecated commands and options with empty or missing help text no
longer
render a stray leading space before the (DEPRECATED) label.
{pr}3509
A {class}Group with
invoke_without_command=True marks its subcommand as
optional in the usage help, showing [COMMAND] instead of
COMMAND.
{issue}3059 {pr}3507
echo_via_pager flushes after each write, so passing a
generator streams
output to the pager incrementally instead of staying hidden until the
pipe
buffer fills. {issue}3242 {issue}2542
{pr}3534
echo_via_pager and get_pager_file no
longer close a borrowed stdout
stream when no external pager runs, completing the partial
I/O operation on closed file fix from
{pr}3482. {issue}3449
{pr}3533
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci-cd.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 7f4d2d83d5e..28d776da237 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -71,7 +71,7 @@ jobs:
with:
python-version: 3.11
- name: Cache PyPI
- uses: actions/cache@v6.0.0
+ uses: actions/cache@v6.1.0
with:
key: pip-lint-${{ hashFiles('requirements/*.txt') }}
path: ~/.cache/pip
@@ -117,7 +117,7 @@ jobs:
with:
submodules: true
- name: Cache llhttp generated files
- uses: actions/cache@v6.0.0
+ uses: actions/cache@v6.1.0
id: cache
with:
key: llhttp-${{ hashFiles('vendor/llhttp/package*.json', 'vendor/llhttp/src/**/*') }}
From 2183f8e9187f9718737cd720eddeecc2f3f9b7ed Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 30 Jun 2026 11:02:24 +0000
Subject: [PATCH 061/137] Bump cython from 3.2.6 to 3.2.8 (#13012)
Bumps [cython](https://github.com/cython/cython) from 3.2.6 to 3.2.8.
Changelog
Assigning a Python 3.14+ .__annotate__ function to a
Cython compiled function no longer
evaluates annotations eagerly. Fixes a regression with
@functools.wraps() in Cython 3.2.6.
Patch by Jelle Zijlstra. (Github issue :issue:7767)
In freethreading Python, the necessary object keep-alive while
executing user code in
.__dealloc__() uses a safer scheme.
Patch by Yaxing Cai. (Github issue :issue:7769)
The local function state used by sys.monitoring read
from uninitialised memory.
(Github issue :issue:7774)
The .ag_running flag attribute of async generators was
always false on big-endian systems.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
requirements/test-common.txt | 2 +-
requirements/test-ft.txt | 2 +-
requirements/test.txt | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 6207c100954..22c1a209a30 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -18,7 +18,7 @@ alabaster==1.0.0
# via sphinx
annotated-types==0.7.0
# via pydantic
-ast-serialize==0.5.0
+ast-serialize==0.6.0
# via mypy
async-timeout==5.0.1 ; python_version < "3.11"
# via
diff --git a/requirements/dev.txt b/requirements/dev.txt
index a8acfd23078..ab980060088 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -18,7 +18,7 @@ alabaster==1.0.0
# via sphinx
annotated-types==0.7.0
# via pydantic
-ast-serialize==0.5.0
+ast-serialize==0.6.0
# via mypy
async-timeout==5.0.1 ; python_version < "3.11"
# via
diff --git a/requirements/lint.txt b/requirements/lint.txt
index b7396ae3366..c4b3d2d839b 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -8,7 +8,7 @@ aiodns==4.0.4
# via -r requirements/lint.in
annotated-types==0.7.0
# via pydantic
-ast-serialize==0.5.0
+ast-serialize==0.6.0
# via mypy
async-timeout==5.0.1
# via valkey
diff --git a/requirements/test-common.txt b/requirements/test-common.txt
index 99879898575..7bd09d5f507 100644
--- a/requirements/test-common.txt
+++ b/requirements/test-common.txt
@@ -6,7 +6,7 @@
#
annotated-types==0.7.0
# via pydantic
-ast-serialize==0.5.0
+ast-serialize==0.6.0
# via mypy
blockbuster==1.5.26
# via -r requirements/test-common.in
diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt
index 6fc30cbea10..010bfd75ae8 100644
--- a/requirements/test-ft.txt
+++ b/requirements/test-ft.txt
@@ -12,7 +12,7 @@ aiosignal==1.4.0
# via -r requirements/runtime-deps.in
annotated-types==0.7.0
# via pydantic
-ast-serialize==0.5.0
+ast-serialize==0.6.0
# via mypy
async-timeout==5.0.1 ; python_version < "3.11"
# via -r requirements/runtime-deps.in
diff --git a/requirements/test.txt b/requirements/test.txt
index b69428243b2..5d36af33250 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -12,7 +12,7 @@ aiosignal==1.4.0
# via -r requirements/runtime-deps.in
annotated-types==0.7.0
# via pydantic
-ast-serialize==0.5.0
+ast-serialize==0.6.0
# via mypy
async-timeout==5.0.1 ; python_version < "3.11"
# via -r requirements/runtime-deps.in
From 091ce7b72762c1664c6a344c6a9c525b2e374f59 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 Jul 2026 11:18:50 +0000
Subject: [PATCH 065/137] Bump librt from 0.11.0 to 0.12.0 (#13028)
Bumps [librt](https://github.com/mypyc/librt) from 0.11.0 to 0.12.0.
Commits
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
requirements/test-common.txt | 2 +-
requirements/test-ft.txt | 2 +-
requirements/test.txt | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 22c1a209a30..140a21e8ea8 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -111,7 +111,7 @@ jinja2==3.1.6
# sphinx
# sphinxcontrib-mermaid
# towncrier
-librt==0.11.0
+librt==0.12.0
# via mypy
markdown-it-py==3.0.0
# via
diff --git a/requirements/dev.txt b/requirements/dev.txt
index ab980060088..1aaac6debde 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -109,7 +109,7 @@ jinja2==3.1.6
# sphinx
# sphinxcontrib-mermaid
# towncrier
-librt==0.11.0
+librt==0.12.0
# via mypy
markdown-it-py==3.0.0
# via
diff --git a/requirements/lint.txt b/requirements/lint.txt
index c4b3d2d839b..198f27a91f8 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -46,7 +46,7 @@ iniconfig==2.3.0
# via pytest
isal==1.8.0
# via -r requirements/lint.in
-librt==0.11.0
+librt==0.12.0
# via mypy
markdown-it-py==4.2.0
# via rich
diff --git a/requirements/test-common.txt b/requirements/test-common.txt
index 7bd09d5f507..a6e85edad5e 100644
--- a/requirements/test-common.txt
+++ b/requirements/test-common.txt
@@ -34,7 +34,7 @@ iniconfig==2.3.0
# via pytest
isal==1.8.0 ; python_version < "3.14" and implementation_name == "cpython"
# via -r requirements/test-common.in
-librt==0.11.0
+librt==0.12.0
# via mypy
markdown-it-py==4.2.0
# via rich
diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt
index 010bfd75ae8..f8fb4d41bc4 100644
--- a/requirements/test-ft.txt
+++ b/requirements/test-ft.txt
@@ -58,7 +58,7 @@ iniconfig==2.3.0
# via pytest
isal==1.8.0 ; python_version < "3.14" and implementation_name == "cpython"
# via -r requirements/test-common.in
-librt==0.11.0
+librt==0.12.0
# via mypy
markdown-it-py==4.2.0
# via rich
diff --git a/requirements/test.txt b/requirements/test.txt
index 5d36af33250..a2f8623fd85 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -58,7 +58,7 @@ iniconfig==2.3.0
# via pytest
isal==1.8.0 ; python_version < "3.14" and implementation_name == "cpython"
# via -r requirements/test-common.in
-librt==0.11.0
+librt==0.12.0
# via mypy
markdown-it-py==4.2.0
# via rich
From 732d74a25644604f623154ab479b4dc2e4a02b26 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 2 Jul 2026 11:11:21 +0000
Subject: [PATCH 066/137] Bump aiohappyeyeballs from 2.6.2 to 2.7.1 (#13036)
Bumps [aiohappyeyeballs](https://github.com/aio-libs/aiohappyeyeballs)
from 2.6.2 to 2.7.1.
Release notes
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/base-ft.txt | 2 +-
requirements/base.txt | 2 +-
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/runtime-deps.txt | 2 +-
requirements/test-ft.txt | 2 +-
requirements/test-mobile.txt | 2 +-
requirements/test.txt | 2 +-
8 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/requirements/base-ft.txt b/requirements/base-ft.txt
index be40c5bee8f..d52f9988399 100644
--- a/requirements/base-ft.txt
+++ b/requirements/base-ft.txt
@@ -6,7 +6,7 @@
#
aiodns==4.0.4 ; sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
-aiohappyeyeballs==2.6.2
+aiohappyeyeballs==2.7.1
# via -r requirements/runtime-deps.in
aiosignal==1.4.0
# via -r requirements/runtime-deps.in
diff --git a/requirements/base.txt b/requirements/base.txt
index 5d8ba45de28..f1372b0b16d 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -6,7 +6,7 @@
#
aiodns==4.0.4 ; sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
-aiohappyeyeballs==2.6.2
+aiohappyeyeballs==2.7.1
# via -r requirements/runtime-deps.in
aiosignal==1.4.0
# via -r requirements/runtime-deps.in
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 140a21e8ea8..6b9179656a4 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -8,7 +8,7 @@ aiodns==4.0.4 ; sys_platform != "android" and sys_platform != "ios"
# via
# -r requirements/lint.in
# -r requirements/runtime-deps.in
-aiohappyeyeballs==2.6.2
+aiohappyeyeballs==2.7.1
# via -r requirements/runtime-deps.in
aiohttp-theme==0.1.7
# via -r requirements/doc.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 1aaac6debde..d09e4943d44 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -8,7 +8,7 @@ aiodns==4.0.4 ; sys_platform != "android" and sys_platform != "ios"
# via
# -r requirements/lint.in
# -r requirements/runtime-deps.in
-aiohappyeyeballs==2.6.2
+aiohappyeyeballs==2.7.1
# via -r requirements/runtime-deps.in
aiohttp-theme==0.1.7
# via -r requirements/doc.in
diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt
index 28afc656000..d1f98e8c7db 100644
--- a/requirements/runtime-deps.txt
+++ b/requirements/runtime-deps.txt
@@ -6,7 +6,7 @@
#
aiodns==4.0.4 ; sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
-aiohappyeyeballs==2.6.2
+aiohappyeyeballs==2.7.1
# via -r requirements/runtime-deps.in
aiosignal==1.4.0
# via -r requirements/runtime-deps.in
diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt
index f8fb4d41bc4..b553c50ed4c 100644
--- a/requirements/test-ft.txt
+++ b/requirements/test-ft.txt
@@ -6,7 +6,7 @@
#
aiodns==4.0.4 ; sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
-aiohappyeyeballs==2.6.2
+aiohappyeyeballs==2.7.1
# via -r requirements/runtime-deps.in
aiosignal==1.4.0
# via -r requirements/runtime-deps.in
diff --git a/requirements/test-mobile.txt b/requirements/test-mobile.txt
index 4dc437fd8f3..9a3e54e7840 100644
--- a/requirements/test-mobile.txt
+++ b/requirements/test-mobile.txt
@@ -6,7 +6,7 @@
#
aiodns==4.0.4 ; sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
-aiohappyeyeballs==2.6.2
+aiohappyeyeballs==2.7.1
# via -r requirements/runtime-deps.in
aiosignal==1.4.0
# via -r requirements/runtime-deps.in
diff --git a/requirements/test.txt b/requirements/test.txt
index a2f8623fd85..15a459e34ce 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -6,7 +6,7 @@
#
aiodns==4.0.4 ; sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
-aiohappyeyeballs==2.6.2
+aiohappyeyeballs==2.7.1
# via -r requirements/runtime-deps.in
aiosignal==1.4.0
# via -r requirements/runtime-deps.in
From 7482f39a23e11095f1204cddd077ee938d0babfd Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 3 Jul 2026 11:03:52 +0000
Subject: [PATCH 067/137] Bump coverage from 7.14.3 to 7.15.0 (#13043)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.14.3
to 7.15.0.
Changelog
Since 7.14.0, reporting commands implicitly combine parallel data
files. Now
those commands have a new option --keep-combined to retain
the data files
after combining them instead of the default, which is to delete them.
Finishes issue 2198_.
Fix: the LCOV report would incorrectly count excluded functions as
uncovered,
as described in issue 2205. This is now fixed thanks to
Martin Kuntz Jacobsen <pull 2206_>.
When running your program, coverage now correctly sets
yourmodule.__spec__.loader as strongly recommended
<--loader--_>,
avoiding the deprecation warning described in issue
2208. Thanks, A5rocks <pull 2209_>_.
Fix: with Python 3.10, running with the -I (isolated
mode) option didn't
correctly omit the current directory from the module search path, as
described in issue 2103. That is now fixed thanks to
Ilia Sorokin <pull 2211_>.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/test-common-base.txt | 2 +-
requirements/test-common.txt | 2 +-
requirements/test-ft.txt | 2 +-
requirements/test-mobile.txt | 2 +-
requirements/test.txt | 2 +-
7 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 6b9179656a4..6b2b9009583 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -56,7 +56,7 @@ click==8.4.2
# slotscheck
# towncrier
# wait-for-it
-coverage==7.14.3
+coverage==7.15.0
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/dev.txt b/requirements/dev.txt
index d09e4943d44..74aec2abbe0 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -56,7 +56,7 @@ click==8.4.2
# slotscheck
# towncrier
# wait-for-it
-coverage==7.14.3
+coverage==7.15.0
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/test-common-base.txt b/requirements/test-common-base.txt
index 580621a8584..8d72c97efa7 100644
--- a/requirements/test-common-base.txt
+++ b/requirements/test-common-base.txt
@@ -6,7 +6,7 @@
#
click==8.4.2
# via wait-for-it
-coverage==7.14.3
+coverage==7.15.0
# via pytest-cov
exceptiongroup==1.3.1
# via pytest
diff --git a/requirements/test-common.txt b/requirements/test-common.txt
index a6e85edad5e..13aaaa3ce94 100644
--- a/requirements/test-common.txt
+++ b/requirements/test-common.txt
@@ -14,7 +14,7 @@ cffi==2.0.0
# via cryptography
click==8.4.2
# via wait-for-it
-coverage==7.14.3
+coverage==7.15.0
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt
index b553c50ed4c..2ae5ea0b2ea 100644
--- a/requirements/test-ft.txt
+++ b/requirements/test-ft.txt
@@ -30,7 +30,7 @@ cffi==2.0.0
# pycares
click==8.4.2
# via wait-for-it
-coverage==7.14.3
+coverage==7.15.0
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/test-mobile.txt b/requirements/test-mobile.txt
index 9a3e54e7840..b0a6d9f4f34 100644
--- a/requirements/test-mobile.txt
+++ b/requirements/test-mobile.txt
@@ -26,7 +26,7 @@ cffi==2.0.0 ; sys_platform != "android" and sys_platform != "ios"
# pycares
click==8.4.2
# via wait-for-it
-coverage==7.14.3
+coverage==7.15.0
# via pytest-cov
exceptiongroup==1.3.1
# via pytest
diff --git a/requirements/test.txt b/requirements/test.txt
index 15a459e34ce..160b35275ea 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -30,7 +30,7 @@ cffi==2.0.0
# pycares
click==8.4.2
# via wait-for-it
-coverage==7.14.3
+coverage==7.15.0
# via
# -r requirements/test-common.in
# pytest-cov
From fa9fc05e79c675f4e64d3a57d1bbf23eec8611bb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 3 Jul 2026 11:17:49 +0000
Subject: [PATCH 068/137] Bump filelock from 3.29.4 to 3.29.5 (#13046)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.29.4 to
3.29.5.
Release notes
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 6b2b9009583..2bc9dd303b1 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -74,7 +74,7 @@ exceptiongroup==1.3.1
# via pytest
execnet==2.1.2
# via pytest-xdist
-filelock==3.29.4
+filelock==3.29.5
# via
# python-discovery
# virtualenv
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 74aec2abbe0..85440f2b92d 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -72,7 +72,7 @@ exceptiongroup==1.3.1
# via pytest
execnet==2.1.2
# via pytest-xdist
-filelock==3.29.4
+filelock==3.29.5
# via
# python-discovery
# virtualenv
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 198f27a91f8..c13134c648a 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -30,7 +30,7 @@ distlib==0.4.3
# via virtualenv
exceptiongroup==1.3.1
# via pytest
-filelock==3.29.4
+filelock==3.29.5
# via
# python-discovery
# virtualenv
From bbf87b3ab5f58a79480b980bea251d685995162d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 6 Jul 2026 11:33:41 +0000
Subject: [PATCH 069/137] Bump astral-sh/setup-uv from 8.2.0 to 8.3.0 (#13062)
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from
8.2.0 to 8.3.0.
Commits
d31148d
Strip environment markers from detected uv dependency pins (#938)
17c3989
Fix cache keys for Python version ranges (#937)
3cc3c11
chore(deps): roll up Dependabot updates (#936)
9225f84
chore(deps): bump release-drafter/release-drafter from 7.3.1 to 7.4.0
(#924)
fc16fa3
chore(deps): bump actions/checkout from 6.0.2 to 7.0.0 (#926)
a1a7345
ci: call docs update workflow from release (#933)
a5e9cbf
docs: update version references to v8.2.0 (#932)
c5680ec
chore: update known checksums for 0.11.26 (#930)
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci-cd.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 28d776da237..d622eb9f3ee 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -175,7 +175,7 @@ jobs:
# important: do not use system python
env:
UV_PYTHON_PREFERENCE: only-managed
- uses: astral-sh/setup-uv@v8.2.0
+ uses: astral-sh/setup-uv@v8.3.0
with:
python-version: ${{ matrix.pyver }}
activate-environment: true
@@ -282,7 +282,7 @@ jobs:
# important: do not use system python
env:
UV_PYTHON_PREFERENCE: only-managed
- uses: astral-sh/setup-uv@v8.2.0
+ uses: astral-sh/setup-uv@v8.3.0
with:
python-version: ${{ matrix.pyver }}
activate-environment: true
@@ -354,7 +354,7 @@ jobs:
# important: do not use system python
env:
UV_PYTHON_PREFERENCE: only-managed
- uses: astral-sh/setup-uv@v8.2.0
+ uses: astral-sh/setup-uv@v8.3.0
with:
python-version: ${{ matrix.pyver }}
activate-environment: true
From aa360555826879b8378043b6f00c32cbaf5be918 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 6 Jul 2026 11:34:16 +0000
Subject: [PATCH 070/137] Bump python-discovery from 1.4.2 to 1.4.3 (#13064)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [python-discovery](https://github.com/tox-dev/python-discovery)
from 1.4.2 to 1.4.3.
Release notes
Packaging updates and notes for downstreams - 1.4.3
Constrain the hatchling build requirement per Python
version so sdist builds resolve a compatible backend on the
declared >=3.8 floor - hatchling>=1.28
dropped Python 3.9, leaving 3.8/3.9 unbuildable from source - by
:user:gaborbernat. (:issue:92)
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 2bc9dd303b1..9a9a34e1819 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -214,7 +214,7 @@ pytest-xdist==3.8.0
# via -r requirements/test-common.in
python-dateutil==2.9.0.post0
# via freezegun
-python-discovery==1.4.2
+python-discovery==1.4.3
# via virtualenv
python-on-whales==0.81.0
# via
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 85440f2b92d..984ceb1b4af 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -209,7 +209,7 @@ pytest-xdist==3.8.0
# via -r requirements/test-common.in
python-dateutil==2.9.0.post0
# via freezegun
-python-discovery==1.4.2
+python-discovery==1.4.3
# via virtualenv
python-on-whales==0.81.0
# via
diff --git a/requirements/lint.txt b/requirements/lint.txt
index c13134c648a..cb1ba80fc5f 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -93,7 +93,7 @@ pytest-mock==3.15.1
# via -r requirements/lint.in
python-dateutil==2.9.0.post0
# via freezegun
-python-discovery==1.4.2
+python-discovery==1.4.3
# via virtualenv
python-on-whales==0.81.0
# via -r requirements/lint.in
From d61dc3e97b2132ddaf33ba44274e82fc4f68585d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 6 Jul 2026 11:36:32 +0000
Subject: [PATCH 071/137] Bump setuptools from 82.0.1 to 83.0.0 (#13065)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [setuptools](https://github.com/pypa/setuptools) from 82.0.1 to
83.0.0.
Changelog
MANIFEST.in matching (via FileList) is now
insensitive to Unicode
normalization form. A pattern authored in one form (e.g. NFC, as
typically
saved by editors) now matches a file whose name is stored on disk in
another
(e.g. NFD, as produced by macOS APFS/HFS+). Previously an
exclude,
global-exclude, recursive-exclude, or
prune rule could silently
fail to drop a non-ASCII-named file from the source distribution,
publishing
it despite the exclusion -- see GHSA-h35f-9h28-mq5c.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 9a9a34e1819..c1af1824dd1 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -231,7 +231,7 @@ requests==2.34.2
# sphinxcontrib-spelling
rich==15.0.0
# via pytest-codspeed
-setuptools==82.0.1
+setuptools==83.0.0
# via pip-tools
setuptools-git==1.2
# via -r requirements/test-common-base.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 984ceb1b4af..d3a6fde77cd 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -224,7 +224,7 @@ requests==2.34.2
# via sphinx
rich==15.0.0
# via pytest-codspeed
-setuptools==82.0.1
+setuptools==83.0.0
# via pip-tools
setuptools-git==1.2
# via -r requirements/test-common-base.in
From 1e1b6690c13ec892f9151d10a5c4bdf6380a2ae6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 6 Jul 2026 11:38:01 +0000
Subject: [PATCH 072/137] Bump slotscheck from 0.20.0 to 0.20.1 (#13066)
Bumps [slotscheck](https://github.com/ariebovenberg/slotscheck) from
0.20.0 to 0.20.1.
Release notes
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 3 +--
requirements/dev.txt | 3 +--
requirements/lint.txt | 4 +---
3 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index c1af1824dd1..d1321767ce7 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -53,7 +53,6 @@ charset-normalizer==3.4.7
click==8.4.2
# via
# pip-tools
- # slotscheck
# towncrier
# wait-for-it
coverage==7.15.0
@@ -237,7 +236,7 @@ setuptools-git==1.2
# via -r requirements/test-common-base.in
six==1.17.0
# via python-dateutil
-slotscheck==0.20.0
+slotscheck==0.20.1
# via -r requirements/lint.in
snowballstemmer==3.1.1
# via sphinx
diff --git a/requirements/dev.txt b/requirements/dev.txt
index d3a6fde77cd..d121afdc3f6 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -53,7 +53,6 @@ charset-normalizer==3.4.7
click==8.4.2
# via
# pip-tools
- # slotscheck
# towncrier
# wait-for-it
coverage==7.15.0
@@ -230,7 +229,7 @@ setuptools-git==1.2
# via -r requirements/test-common-base.in
six==1.17.0
# via python-dateutil
-slotscheck==0.20.0
+slotscheck==0.20.1
# via -r requirements/lint.in
snowballstemmer==3.1.1
# via sphinx
diff --git a/requirements/lint.txt b/requirements/lint.txt
index cb1ba80fc5f..2bb294b6da9 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -22,8 +22,6 @@ cffi==2.0.0
# pycares
cfgv==3.5.0
# via pre-commit
-click==8.4.2
- # via slotscheck
cryptography==49.0.0
# via trustme
distlib==0.4.3
@@ -103,7 +101,7 @@ rich==15.0.0
# via pytest-codspeed
six==1.17.0
# via python-dateutil
-slotscheck==0.20.0
+slotscheck==0.20.1
# via -r requirements/lint.in
tomli==2.4.1
# via
From 40f89922d6d6a90ed68c00291910ba13135a5847 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2026 11:32:46 +0000
Subject: [PATCH 073/137] Bump astral-sh/setup-uv from 8.3.0 to 8.3.1 (#13072)
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from
8.3.0 to 8.3.1.
Commits
f98e069
Change update-docs PR labels from 'update-docs' to 'documentation' (#945)
cd46263
chore: update known checksums for 0.11.27 (#944)
11245c7
docs: update version references to v8.3.0 (#939)
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci-cd.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index d622eb9f3ee..9ce995594e6 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -175,7 +175,7 @@ jobs:
# important: do not use system python
env:
UV_PYTHON_PREFERENCE: only-managed
- uses: astral-sh/setup-uv@v8.3.0
+ uses: astral-sh/setup-uv@v8.3.1
with:
python-version: ${{ matrix.pyver }}
activate-environment: true
@@ -282,7 +282,7 @@ jobs:
# important: do not use system python
env:
UV_PYTHON_PREFERENCE: only-managed
- uses: astral-sh/setup-uv@v8.3.0
+ uses: astral-sh/setup-uv@v8.3.1
with:
python-version: ${{ matrix.pyver }}
activate-environment: true
@@ -354,7 +354,7 @@ jobs:
# important: do not use system python
env:
UV_PYTHON_PREFERENCE: only-managed
- uses: astral-sh/setup-uv@v8.3.0
+ uses: astral-sh/setup-uv@v8.3.1
with:
python-version: ${{ matrix.pyver }}
activate-environment: true
From 0c4ed9a7f1bfaa435923cb92ab9ba98412f85bb9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2026 11:37:10 +0000
Subject: [PATCH 074/137] Bump cffi from 2.0.0 to 2.1.0 (#13074)
Bumps [cffi](https://github.com/python-cffi/cffi) from 2.0.0 to 2.1.0.
Release notes
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/base-ft.txt | 2 +-
requirements/base.txt | 2 +-
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
requirements/runtime-deps.txt | 2 +-
requirements/test-common.txt | 2 +-
requirements/test-ft.txt | 2 +-
requirements/test-mobile.txt | 2 +-
requirements/test.txt | 2 +-
10 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/requirements/base-ft.txt b/requirements/base-ft.txt
index d52f9988399..0c4b784c87d 100644
--- a/requirements/base-ft.txt
+++ b/requirements/base-ft.txt
@@ -18,7 +18,7 @@ backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_v
# via -r requirements/runtime-deps.in
brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
-cffi==2.0.0
+cffi==2.1.0
# via pycares
frozenlist==1.8.0
# via
diff --git a/requirements/base.txt b/requirements/base.txt
index f1372b0b16d..dbaaea3f6a3 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -18,7 +18,7 @@ backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_v
# via -r requirements/runtime-deps.in
brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
-cffi==2.0.0
+cffi==2.1.0
# via pycares
frozenlist==1.8.0
# via
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index d1321767ce7..c80bed15e5c 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -42,7 +42,7 @@ build==1.5.0
# via pip-tools
certifi==2026.6.17
# via requests
-cffi==2.0.0
+cffi==2.1.0
# via
# cryptography
# pycares
diff --git a/requirements/dev.txt b/requirements/dev.txt
index d121afdc3f6..0104adb6f6a 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -42,7 +42,7 @@ build==1.5.0
# via pip-tools
certifi==2026.6.17
# via requests
-cffi==2.0.0
+cffi==2.1.0
# via
# cryptography
# pycares
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 2bb294b6da9..ee2bee6c731 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -16,7 +16,7 @@ backports-zstd==1.3.0 ; implementation_name == "cpython" and python_version < "3
# via -r requirements/lint.in
blockbuster==1.5.26
# via -r requirements/lint.in
-cffi==2.0.0
+cffi==2.1.0
# via
# cryptography
# pycares
diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt
index d1f98e8c7db..dd0a75aef3e 100644
--- a/requirements/runtime-deps.txt
+++ b/requirements/runtime-deps.txt
@@ -18,7 +18,7 @@ backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_v
# via -r requirements/runtime-deps.in
brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
-cffi==2.0.0
+cffi==2.1.0
# via pycares
frozenlist==1.8.0
# via
diff --git a/requirements/test-common.txt b/requirements/test-common.txt
index 13aaaa3ce94..0c08c85c327 100644
--- a/requirements/test-common.txt
+++ b/requirements/test-common.txt
@@ -10,7 +10,7 @@ ast-serialize==0.6.0
# via mypy
blockbuster==1.5.26
# via -r requirements/test-common.in
-cffi==2.0.0
+cffi==2.1.0
# via cryptography
click==8.4.2
# via wait-for-it
diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt
index 2ae5ea0b2ea..8d1bff8f023 100644
--- a/requirements/test-ft.txt
+++ b/requirements/test-ft.txt
@@ -24,7 +24,7 @@ blockbuster==1.5.26
# via -r requirements/test-common.in
brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
-cffi==2.0.0
+cffi==2.1.0
# via
# cryptography
# pycares
diff --git a/requirements/test-mobile.txt b/requirements/test-mobile.txt
index b0a6d9f4f34..3fa1a5db302 100644
--- a/requirements/test-mobile.txt
+++ b/requirements/test-mobile.txt
@@ -20,7 +20,7 @@ backports-zstd==1.3.0 ; platform_python_implementation == "CPython" and python_v
# via -r requirements/runtime-deps.in
brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
-cffi==2.0.0 ; sys_platform != "android" and sys_platform != "ios"
+cffi==2.1.0 ; sys_platform != "android" and sys_platform != "ios"
# via
# -r requirements/test-mobile.in
# pycares
diff --git a/requirements/test.txt b/requirements/test.txt
index 160b35275ea..d4c42f19b95 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -24,7 +24,7 @@ blockbuster==1.5.26
# via -r requirements/test-common.in
brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
-cffi==2.0.0
+cffi==2.1.0
# via
# cryptography
# pycares
From bc807b2c66e59d79e342e2df5e321d27eebbff8c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2026 11:50:03 +0000
Subject: [PATCH 075/137] Bump virtualenv from 21.5.1 to 21.6.0 (#13080)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [virtualenv](https://github.com/pypa/virtualenv) from 21.5.1 to
21.6.0.
Release notes
Stop installing the _virtualenv.{py,pth} distutils
import hook for Python 3.10 and later, where pip, setuptools
and CPython already ignore the install config keys it guards against;
this removes the hook's startup import cost. The
hook is still installed for Python 3.9 - :issue:3181.
(:issue:3181)
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index c80bed15e5c..e7b5a496d82 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -306,7 +306,7 @@ uvloop==0.22.1 ; platform_system != "Windows"
# -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.5.1
+virtualenv==21.6.0
# via pre-commit
wait-for-it==2.3.0
# via -r requirements/test-common-base.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 0104adb6f6a..b4edc960cf1 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -296,7 +296,7 @@ uvloop==0.22.1 ; platform_system != "Windows" and implementation_name == "cpytho
# -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.5.1
+virtualenv==21.6.0
# via pre-commit
wait-for-it==2.3.0
# via -r requirements/test-common-base.in
diff --git a/requirements/lint.txt b/requirements/lint.txt
index ee2bee6c731..bfa89f249d0 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -126,7 +126,7 @@ uvloop==0.22.1 ; platform_system != "Windows"
# via -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.5.1
+virtualenv==21.6.0
# via pre-commit
zlib-ng==1.0.0
# via -r requirements/lint.in
From 61a930fcb2a76b87e0aee261a1a4cb7038014142 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2026 11:52:56 +0000
Subject: [PATCH 076/137] Bump filelock from 3.29.5 to 3.29.6 (#13077)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.29.5 to
3.29.6.
Release notes
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index e7b5a496d82..ff994e72c6d 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -73,7 +73,7 @@ exceptiongroup==1.3.1
# via pytest
execnet==2.1.2
# via pytest-xdist
-filelock==3.29.5
+filelock==3.29.6
# via
# python-discovery
# virtualenv
diff --git a/requirements/dev.txt b/requirements/dev.txt
index b4edc960cf1..c2876d733cf 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -71,7 +71,7 @@ exceptiongroup==1.3.1
# via pytest
execnet==2.1.2
# via pytest-xdist
-filelock==3.29.5
+filelock==3.29.6
# via
# python-discovery
# virtualenv
diff --git a/requirements/lint.txt b/requirements/lint.txt
index bfa89f249d0..6788d69aa03 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -28,7 +28,7 @@ distlib==0.4.3
# via virtualenv
exceptiongroup==1.3.1
# via pytest
-filelock==3.29.5
+filelock==3.29.6
# via
# python-discovery
# virtualenv
From 309ad617b8ec50a639e69304c4dbf9666324e19b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2026 11:53:19 +0000
Subject: [PATCH 077/137] Bump charset-normalizer from 3.4.7 to 3.4.8 (#13078)
Bumps [charset-normalizer](https://github.com/jawah/charset_normalizer)
from 3.4.7 to 3.4.8.
Release notes
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/doc-spelling.txt | 2 +-
requirements/doc.txt | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index ff994e72c6d..4a992432d06 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -48,7 +48,7 @@ cffi==2.1.0
# pycares
cfgv==3.5.0
# via pre-commit
-charset-normalizer==3.4.7
+charset-normalizer==3.4.8
# via requests
click==8.4.2
# via
diff --git a/requirements/dev.txt b/requirements/dev.txt
index c2876d733cf..bde0838ebff 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -48,7 +48,7 @@ cffi==2.1.0
# pycares
cfgv==3.5.0
# via pre-commit
-charset-normalizer==3.4.7
+charset-normalizer==3.4.8
# via requests
click==8.4.2
# via
diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt
index 4c35bc3fc56..d54a504c8b2 100644
--- a/requirements/doc-spelling.txt
+++ b/requirements/doc-spelling.txt
@@ -12,7 +12,7 @@ babel==2.18.0
# via sphinx
certifi==2026.6.17
# via requests
-charset-normalizer==3.4.7
+charset-normalizer==3.4.8
# via requests
click==8.4.2
# via towncrier
diff --git a/requirements/doc.txt b/requirements/doc.txt
index c374c83ef83..17b1bef9fc4 100644
--- a/requirements/doc.txt
+++ b/requirements/doc.txt
@@ -12,7 +12,7 @@ babel==2.18.0
# via sphinx
certifi==2026.6.17
# via requests
-charset-normalizer==3.4.7
+charset-normalizer==3.4.8
# via requests
click==8.4.2
# via towncrier
From 4c7dc166c6bf3417a8b7481a46b01d7e4dc8e2a1 Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2026 17:55:15 +0100
Subject: [PATCH 078/137] [PR #12706/ce99eac8 backport][3.15] Threat model
chapter 4 (#13081)
**This is a backport of PR #12706 as merged into master
(ce99eac8db1073e4bbe4c8b3d0913900e0b3a758).**
Co-authored-by: Sam Bull
---
THREAT_MODEL.md | 152 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 143 insertions(+), 9 deletions(-)
diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md
index 4b5af95cbd4..f7b0aed090a 100644
--- a/THREAT_MODEL.md
+++ b/THREAT_MODEL.md
@@ -313,6 +313,17 @@ into `StreamReader`) is then handed to `web_protocol.RequestHandler` and
Werkzeug emit duplicate `Content-Type` / `Server`); fix disables the
check on the response parser (lax mode) while keeping it on the
request parser (strict).
+- **GHSA-63hw-fmq6-xxg2 (CVE-2026-54277)** (3.14.1) β the
+ Cython / llhttp request parser failed to enforce `max_line_size`
+ when a single request or header line arrived across multiple TCP
+ segments, letting an attacker stream an oversized line in small
+ fragments to exhaust memory.
+- **GHSA-4fvr-rgm6-gqmc (CVE-2026-54273)** (3.14.1) β no cap
+ on the number of HTTP/1 pipelined requests queued on a single
+ connection; the `_messages` deque in `web_protocol.RequestHandler`
+ could grow without bound. Memory was previously bounded only
+ transitively by `read_bufsize` ([Β§5.7](#57-server-connection-lifecycle) threat 7.4);
+ the fix adds an explicit pipeline-count cap.
These are all currently in place; this section assumes no regression.
@@ -546,20 +557,143 @@ client-side, the writer adds masks to outgoing frames.
`max_msg_size + 1` and rejects with `MESSAGE_TOO_BIG` (1009) on overflow.
This is the primary mitigation for zip-bomb-style attacks against
WebSocket peers.
+- **GHSA-xcgm-r5h9-7989 (CVE-2026-54274)** (3.14.1) β a peer could
+ send large WebSocket frames with incomplete payloads, bypassing the
+ per-frame memory limit because the buffer grew before the
+ `max_msg_size` check fired against the (still-unknown) final size.
+ Fixed by accounting for the in-flight buffer in the cap.
- **PR #12976** β the client created its `WebSocketReader`
without passing `compress`, so the reader defaulted to `compress=True` and
decompressed RSV1 frames even when PMCE was never negotiated (threat 3.3;
RFC 6455 Β§5.2 requires failing such frames). Fixed by passing
`compress=bool(compress)` in `client.py:_ws_connect` and removing the
`compress` / `decode_text` defaults on `WebSocketReader.__init__`.
-- No formal CVE has been published against the WebSocket framing layer to
- date.
-**Open questions.**
+---
+
+### 5.4. Multipart parsing & encoding
+
+**Scope.** Parsing of incoming `multipart/*` bodies (`MultipartReader`,
+`BodyPartReader`) and construction of outgoing multipart bodies
+(`MultipartWriter`, `FormData`). This includes boundary handling, per-part
+header parsing, `Content-Disposition` extraction, `Content-Transfer-Encoding`
+decoding, charset detection, and the `Request.post()` path that wraps
+`multipart/form-data`. Out of scope: the underlying compression codecs ([Β§5.5](#55-compression-codecs)),
+streams/payloads ([Β§5.6](#56-streams--payloads)), and the connection-level read/write timeouts that
+back-stop slow drips ([Β§5.7](#57-server-connection-lifecycle)).
+
+**Components covered.**
+
+- `aiohttp/multipart.py` β `MultipartReader`, `BodyPartReader`,
+ `MultipartWriter`, `parse_content_disposition`,
+ `content_disposition_filename`.
+- `aiohttp/formdata.py` β `FormData` builder.
+- `aiohttp/web_request.py` β `Request.post()`, `Request.multipart()`.
+- `aiohttp/payload.py` β multipart payload wrapping (overlap with [Β§5.6](#56-streams--payloads)).
+
+**Trust boundaries & data flow.**
+
+```mermaid
+flowchart LR
+ Wire([Untrusted body]) --> Reader[MultipartReader]
+ Reader --> Parts[BodyPartReader per part]
+ Parts -->|headers, name, filename| App([User handler])
+ Parts -->|streamed body| App
+ Reader -. nested .-> Reader
+
+ AppOut([User code]) --> FD[FormData] --> Writer[MultipartWriter]
+ Writer --> WireOut([Outbound body])
+```
+
+The reader's input is fully attacker-controlled on the server side and
+upstream-controlled on the client side. Per-part values surfaced to user
+code (`headers`, `name`, `filename`, body bytes) are all untrusted. The
+writer's input is trusted in the threat-model sense, but `FormData` is the
+boundary at which user-supplied strings can become wire bytes.
+
+**Assets at risk (chunk-specific).**
-1. Should server-side reader reject unmasked frames (and client-side reject
- masked ones) per RFC 6455 Β§5.1? (Threat 3.1 β recommended.)
-2. Is the PRNG mask source (`random.getrandbits`) sufficient, or should it be
- migrated to `secrets`/`os.urandom`? (Threat 3.2.)
-3. For long-lived WebSocket sessions, is there a use case for *forcing*
- `no_context_takeover` defaults to limit memory growth? (Threat 3.10.)
+- **Process memory** β number of parts, per-part body size, recursion depth
+ for nested multipart.
+- **Filesystem safety in user code** β `filename` round-trips from wire to
+ application without sanitisation.
+- **Outbound framing integrity** β boundary uniqueness, header validity in
+ `FormData.add_field`.
+
+**Threats (STRIDE).**
+
+| # | Component / Vector | STRIDE | Threat | Risk |
+| :--- | :--- | :--- | :--- | :--- |
+| 4.1 | Boundary parameter parsing | T | Malformed boundary parameter (oversized, missing, or containing bytes outside the RFC 2046 Β§5.1.1 safe set β digits, letters, and a small punctuation set) could enable multipart parser confusion or smuggling. | Low |
+| 4.2 | Number of parts per body | D | A peer submits a body packed with many tiny parts (e.g. ten thousand 100-byte parts inside a 1 MiB body). Each part allocates a `BodyPartReader` plus header dict, so the live-Python-object footprint is far larger than the on-wire byte count. `client_max_size` caps the wire bytes but not the per-part allocation amplification. | Low |
+| 4.3 | Nested multipart recursion | D | `MultipartReader.next()` recurses into nested multiparts without a depth cap; deeply nested input can hit `RecursionError`. `Request.post()` short-circuits this by rejecting any nested multipart it sees, but the bare API does not. | Medium |
+| 4.4 | Per-part header block size | D | A peer submits a part with an oversized header block (very long field values, or hundreds of headers per part) to drive memory growth at parse time, multiplied across many parts. | Low |
+| 4.5 | Per-part body size | D | A peer submits a single part with a body that grows arbitrarily large before any framing boundary β if size checking happens only after buffering the whole part, memory blows up before the cap fires. | Low |
+| 4.6 | `Content-Disposition` filename | T / I | Filename is parsed (incl. RFC 2231 `filename*=`) and returned verbatim to the application. Path-traversal strings (`../`), absolute paths, NUL/CR/LF all reach user code unsanitised. | Medium |
+| 4.7 | `Content-Disposition` name | T | Field name returned verbatim. CR/LF in a part's `name` doesn't cross the framing boundary (the multipart parser delimits parts by boundary, not by CR/LF in field names), but downstream loggers / dashboards may misrender. | Low |
+| 4.8 | `_charset_` magic form field | T | An attacker can include a form field named `_charset_` whose value retroactively becomes the *default charset* used to decode subsequent text fields (`multipart.py:MultipartReader.next _charset_ form handling`). Surprising behaviour; documented in the standard. | Low |
+| 4.9 | `Content-Transfer-Encoding` (base64 / quoted-printable) | T / D | A peer sets `Content-Transfer-Encoding: base64` (or `quoted-printable`) and submits malformed encoded data β the decoder raises mid-stream and the partially-decoded part is surfaced to the handler with an exception. | Low |
+| 4.10 | `Content-Encoding` (gzip/deflate within a part) | D | A peer submits a multipart part with `Content-Encoding: gzip` (or `deflate`) carrying a zip-bomb payload β a small compressed body that expands to a vastly larger decoded body, driving memory exhaustion in the receiving handler. | Low |
+| 4.11 | `MultipartWriter` boundary collision | T | If a part body contains a byte sequence matching the boundary string, the writer emits it as-is β a receiver parsing the multipart will see a fake part break early. Negligible against the default `uuid.uuid4().hex` boundary, but real if user code supplies a short or predictable one. | Low |
+| 4.12 | `FormData.add_field` argument injection | T | When user code opts into `FormData(quote_fields=False)`, attacker-controlled `name` or `filename` reach the outbound `Content-Disposition` value with only `\` / `"` escaped. CR/LF/NUL would let an attacker inject extra disposition parameters or a fake-part break-out; `add_field` now rejects these bytes in `name`, `filename`, and `content_type` so the opt-out path is also defended. | LowβMed |
+| 4.13 | Per-part header injection via `headers=` | T | Caller-supplied headers passed via `MultipartWriter.append(headers=...)` or `Payload(headers=...)` reach the wire through `Payload._binary_headers`, which serialises `f"{k}: {v}\r\n"` without CR/LF/NUL validation. An attacker-influenced header value can inject extra part headers or break out into a fake part. Distinct from 4.12, which only covers the `name` / `filename` parameters of `add_field`. | Medium |
+| 4.14 | `Request.post()` temp-file lifetime | I / D | Uploaded files are streamed to `tempfile.TemporaryFile()` and lifetime is bound to the `FileField` object. If user code drops the field reference without reading or closing, garbage collection eventually closes the temp file. | Low |
+
+**Mitigations.**
+
+| # | Threat | Existing | Recommended |
+| :--- | :--- | :--- | :--- |
+| 4.1 | Boundary parameter | 70-char cap; missing-boundary raises; HTTP header layer ([Β§5.1](#51-http1-parser)) catches CR/LF/NUL. | None. |
+| 4.2 | Many small parts | `client_max_size` caps total bytes. | Documented design decision: rely on `client_max_size` rather than introducing a `max_parts` knob. **User**: operators sensitive to live-object count should reduce `client_max_size`. |
+| 4.3 | Nested-multipart recursion | `Request.post()` rejects any nested multipart with `ValueError` ("To decode nested multipart you need to use custom reader") (`web_request.py:BaseRequest.post`). | **Direct `MultipartReader` users get unlimited recursion. Add a `max_nesting_depth` parameter (default e.g. 10) to fail cleanly before `RecursionError`.** |
+| 4.4 | Per-part headers bounded | `max_field_size` / `max_headers` plumbed since 5fe9dfb64 (Mar 2026). | None. |
+| 4.5 | Per-part body bounded | Per-iteration size check since 9cc4b917c (Mar 2026). | None. |
+| 4.6 | Filename path traversal | None; verbatim return is the documented behaviour. | **User**: sanitise `BodyPartReader.filename` before opening / saving / reflecting (strip path components, control bytes, known-bad sequences). |
+| 4.7 | Field-name pass-through | None. | **User**: `request.post()` keys come from attacker-controlled `name` parameters; do not feed them directly to filesystem / DB / template paths. |
+| 4.8 | `_charset_` magic field | Behaviour follows the HTML5 spec for multipart form charset selection. | **Document the surprising behaviour: an attacker who can post any field can change how subsequent fields are decoded.** **User**: if your form-handling code makes decisions based on string content, be aware that the decode charset is attacker-influenceable. |
+| 4.9 | CTE decoding | `BodyPartReader._decode_content_transfer` dispatches to `base64.b64decode` / `binascii.a2b_qp` / pass-through for `binary`/`8bit`/`7bit`, and raises `RuntimeError` on any other token. Both decoders raise on malformed input; decoded output is still subject to the per-part `client_max_size` cap. For base64, `BodyPartReader.read_chunk` extends each chunk read until its base64-significant character count is a multiple of 4, so mid-quartet splits cannot silently corrupt the decoded output. | None. |
+| 4.10 | Content-Encoding decompression | Per-chunk `max_decompress_size`, per-part `client_max_size` cap. | Inherits the [Β§5.5](#55-compression-codecs) caveat about backend `max_length` honouring. |
+| 4.11 | Outbound boundary collision | UUID4-hex default; HMAC-grade entropy means collision with random body bytes is statistically negligible. | **User**: callers supplying their own boundary must use a random, sufficiently long value. |
+| 4.12 | `FormData` argument injection | `add_field` runs `name`, `filename`, and `content_type` through `_safe_header` (`http_writer.py:_safe_header`), rejecting `\r` / `\n` / `\x00`. `name` / `filename` are additionally percent-encoded by `content_disposition_header` (`helpers.py:content_disposition_header`) when `quote_fields=True` (default), so the `add_field` check is what closes the `quote_fields=False` opt-out path. | None. |
+| 4.13 | Per-part header injection | `Payload._binary_headers` (`payload.py:_binary_headers`) now applies `_safe_header` to every name and value, raising `ValueError` if CR / LF / NUL is present before any byte reaches the wire. Covers all paths that mutate the headers dict (constructor, direct assignment, `set_content_disposition`). | None. |
+| 4.14 | Temp-file lifetime | `tempfile.TemporaryFile()` is unlinked at creation on POSIX; closing the FD (whether explicit or via GC) reclaims the disk. | **User**: explicitly close `FileField`s you don't intend to consume to release the temp file promptly under high load. |
+
+**Past advisories / hardening (recap).**
+
+- **GHSA-5m98-qgg9-wh84 (CVE-2024-30251)** (3.9.4) β infinite loop in multipart
+ `_read_chunk_from_length` on a malformed POST body.
+- **GHSA-jj3x-wxrx-4x23 (CVE-2025-69227)** (3.13.3) β `Request.post()` entered
+ an infinite loop on malformed bodies when `PYTHONOPTIMIZE` stripped `assert`
+ statements (asserts were doing real control-flow work in the
+ multipart reader). Fixed by replacing the assertions with explicit
+ checks. Code audit follow-up: any other `assert` used for control
+ flow should be identified and removed.
+- **GHSA-6jhg-hg63-jvvf (CVE-2025-69228)** (3.13.3) β DoS via large
+ `Request.post()` payloads.
+- **GHSA-2vrm-gr82-f7m5 (CVE-2026-34514)** (3.13.4) β
+ `FormData.add_field` rejects CR/LF in `content_type` (header
+ injection on outbound multipart).
+- **GHSA-m5qp-6w8w-w647 (CVE-2026-34516)** (3.13.4) β `MultipartReader`
+ now honours `max_field_size` and `max_headers` from the underlying
+ protocol, plugging an unbounded per-part header memory growth path.
+- **GHSA-3wq7-rqq7-wx6j (CVE-2026-34517)** (3.13.4) β `Request.post()` enforces
+ `client_max_size` during iteration rather than after buffering,
+ plugging a memory-blow-up on large form fields.
+- **GHSA-m6qw-4cw2-hm4m (CVE-2026-50269)** (3.14.0) β
+ `Payload._binary_headers` now rejects CR / LF / NUL in any per-part
+ header name or value via `_safe_header`, closing the outbound
+ multipart header-injection path through `MultipartWriter.append(headers=...)`
+ and `Payload(headers=...)` that the main HTTP writer's `_safe_header`
+ check did not cover (threat 4.13).
+- **PR #12721** (3.14.0) β companion to GHSA-m6qw-4cw2-hm4m:
+ `FormData.add_field` now rejects CR / LF / NUL in `name` and
+ `filename` (in addition to the previous `content_type` check), so the
+ `quote_fields=False` opt-out path can no longer be used to inject
+ extra `Content-Disposition` parameters or a fake-part break-out
+ (threat 4.12).
+- **PR #12794** (3.14.1) β precautionary hardening:
+ multipart body parts whose `Content-Length` header is not a plain
+ decimal sequence (e.g. `+5`, `-1`, `1_0`) are now rejected, matching
+ the main request parser's strictness per RFC 9110 Β§8.6.
+
+These are all currently in place; this section assumes no regression.
From 4a2169c2aa3d4e8183f7ab59c59625bf57c4e991 Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2026 18:54:03 +0100
Subject: [PATCH 079/137] [PR #13069/fd07a7a7 backport][3.15] Fix OWS in
Content-Disposition (#13083)
**This is a backport of PR #13069 as merged into master
(fd07a7a7d7cdff58ca2a98c6c7a26149562e13a7).**
Co-authored-by: Sam Bull
---
CHANGES/13002.bugfix.rst | 1 +
aiohttp/multipart.py | 5 +++--
tests/test_multipart_helpers.py | 10 ++++++++++
3 files changed, 14 insertions(+), 2 deletions(-)
create mode 100644 CHANGES/13002.bugfix.rst
diff --git a/CHANGES/13002.bugfix.rst b/CHANGES/13002.bugfix.rst
new file mode 100644
index 00000000000..1ac16a04fa7
--- /dev/null
+++ b/CHANGES/13002.bugfix.rst
@@ -0,0 +1 @@
+Fixed parsing optional whitespace in Content-Disposition -- by :user:`Dreamsorcerer`.
diff --git a/aiohttp/multipart.py b/aiohttp/multipart.py
index 9116171e8cb..7bdae9644ca 100644
--- a/aiohttp/multipart.py
+++ b/aiohttp/multipart.py
@@ -154,9 +154,10 @@ def unescape(text: str, *, chars: str = "".join(map(re.escape, CHAR))) -> str:
else:
failed = True
- if is_quoted(value):
+ rstripped = value.rstrip()
+ if is_quoted(rstripped):
failed = False
- value = unescape(value[1:-1].lstrip("\\/"))
+ value = unescape(rstripped[1:-1].lstrip("\\/"))
elif is_token(value):
failed = False
elif parts:
diff --git a/tests/test_multipart_helpers.py b/tests/test_multipart_helpers.py
index 67bbc86c223..87c33778559 100644
--- a/tests/test_multipart_helpers.py
+++ b/tests/test_multipart_helpers.py
@@ -30,6 +30,16 @@ def test_semicolon(self) -> None:
assert disptype == "form-data"
assert params == {"name": "data", "filename": "file ; name.mp4"}
+ def test_ows_before_separator(self) -> None:
+ # https://github.com/aio-libs/aiohttp/issues/13002
+ # Optional whitespace before the ";" separator (RFC 9110 Β§5.6.6) must
+ # not make the quoted-value repair heuristic swallow the next param.
+ disptype, params = parse_content_disposition(
+ 'attachment; filename="test.txt" ; name="field"'
+ )
+ assert disptype == "attachment"
+ assert params == {"filename": "test.txt", "name": "field"}
+
def test_inlwithasciifilename(self) -> None:
disptype, params = parse_content_disposition('inline; filename="foo.html"')
assert "inline" == disptype
From df1005069145decd94878c66f53dce39deb660d9 Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2026 18:54:15 +0100
Subject: [PATCH 080/137] [PR #13069/fd07a7a7 backport][3.14] Fix OWS in
Content-Disposition (#13082)
**This is a backport of PR #13069 as merged into master
(fd07a7a7d7cdff58ca2a98c6c7a26149562e13a7).**
Co-authored-by: Sam Bull
---
CHANGES/13002.bugfix.rst | 1 +
aiohttp/multipart.py | 5 +++--
tests/test_multipart_helpers.py | 10 ++++++++++
3 files changed, 14 insertions(+), 2 deletions(-)
create mode 100644 CHANGES/13002.bugfix.rst
diff --git a/CHANGES/13002.bugfix.rst b/CHANGES/13002.bugfix.rst
new file mode 100644
index 00000000000..1ac16a04fa7
--- /dev/null
+++ b/CHANGES/13002.bugfix.rst
@@ -0,0 +1 @@
+Fixed parsing optional whitespace in Content-Disposition -- by :user:`Dreamsorcerer`.
diff --git a/aiohttp/multipart.py b/aiohttp/multipart.py
index 9116171e8cb..7bdae9644ca 100644
--- a/aiohttp/multipart.py
+++ b/aiohttp/multipart.py
@@ -154,9 +154,10 @@ def unescape(text: str, *, chars: str = "".join(map(re.escape, CHAR))) -> str:
else:
failed = True
- if is_quoted(value):
+ rstripped = value.rstrip()
+ if is_quoted(rstripped):
failed = False
- value = unescape(value[1:-1].lstrip("\\/"))
+ value = unescape(rstripped[1:-1].lstrip("\\/"))
elif is_token(value):
failed = False
elif parts:
diff --git a/tests/test_multipart_helpers.py b/tests/test_multipart_helpers.py
index 67bbc86c223..87c33778559 100644
--- a/tests/test_multipart_helpers.py
+++ b/tests/test_multipart_helpers.py
@@ -30,6 +30,16 @@ def test_semicolon(self) -> None:
assert disptype == "form-data"
assert params == {"name": "data", "filename": "file ; name.mp4"}
+ def test_ows_before_separator(self) -> None:
+ # https://github.com/aio-libs/aiohttp/issues/13002
+ # Optional whitespace before the ";" separator (RFC 9110 Β§5.6.6) must
+ # not make the quoted-value repair heuristic swallow the next param.
+ disptype, params = parse_content_disposition(
+ 'attachment; filename="test.txt" ; name="field"'
+ )
+ assert disptype == "attachment"
+ assert params == {"filename": "test.txt", "name": "field"}
+
def test_inlwithasciifilename(self) -> None:
disptype, params = parse_content_disposition('inline; filename="foo.html"')
assert "inline" == disptype
From d697a9eb245f051b81264cd3705e5d515977bc5c Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2026 22:34:15 +0100
Subject: [PATCH 081/137] [PR #12984/7d789204 backport][3.15] Fix Digest
challenge with multiple schemes (#13091)
**This is a backport of PR #12984 as merged into master
(7d7892044c6fe663a566e78c58e5fd25ac6d57b1).**
Co-authored-by: Sam Bull
---
CHANGES/12984.bugfix.rst | 3 +
aiohttp/client_middleware_digest_auth.py | 63 ++++++++++++---------
tests/test_client_middleware_digest_auth.py | 31 +++++++++-
3 files changed, 69 insertions(+), 28 deletions(-)
create mode 100644 CHANGES/12984.bugfix.rst
diff --git a/CHANGES/12984.bugfix.rst b/CHANGES/12984.bugfix.rst
new file mode 100644
index 00000000000..a156216397b
--- /dev/null
+++ b/CHANGES/12984.bugfix.rst
@@ -0,0 +1,3 @@
+Fixed :class:`~aiohttp.DigestAuthMiddleware` corrupting the ``Digest``
+challenge when a ``WWW-Authenticate`` response offered more than one
+authentication scheme -- by :user:`Dreamsorcerer`.
diff --git a/aiohttp/client_middleware_digest_auth.py b/aiohttp/client_middleware_digest_auth.py
index 6c3e37f7c00..21675107401 100644
--- a/aiohttp/client_middleware_digest_auth.py
+++ b/aiohttp/client_middleware_digest_auth.py
@@ -52,27 +52,23 @@ class DigestAuthChallenge(TypedDict, total=False):
# Compile the regex pattern once at module level for performance
_HEADER_PAIRS_PATTERN = re.compile(
- r'(?:^|\s|,\s*)(\w+)\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^\s,]+))'
+ r'(?:^|\s|,\s*)(\w+)(?:\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^\s,]+)))?'
if sys.version_info < (3, 11)
- else r'(?:^|\s|,\s*)((?>\w+))\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^\s,]+))'
- # +------------|--------|--|-|-|--|----|------|----|--||-----|-> Match valid start/sep
- # +--------|--|-|-|--|----|------|----|--||-----|-> alphanumeric key (atomic
- # | | | | | | | | || | group reduces backtracking)
- # +--|-|-|--|----|------|----|--||-----|-> maybe whitespace
- # | | | | | | | || |
- # +-|-|--|----|------|----|--||-----|-> = (delimiter)
- # +-|--|----|------|----|--||-----|-> maybe whitespace
- # | | | | | || |
- # +--|----|------|----|--||-----|-> group quoted or unquoted
- # | | | | || |
- # +----|------|----|--||-----|-> if quoted...
- # +------|----|--||-----|-> anything but " or \
- # +----|--||-----|-> escaped characters allowed
- # +--||-----|-> or can be empty string
- # || |
- # +|-----|-> if unquoted...
- # +-----|-> anything but , or
- # +-> at least one char req'd
+ else r'(?:^|\s|,\s*)((?>\w+))(?:\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^\s,]+)))?'
+ # +------------|--------|--|--||--|--|----|------|---|---||-----|-> Match valid start/sep
+ # +--------|--|--||--|--|----|------|---|---||-----|-> alphanumeric key (atomic group reduces backtracking)
+ # +--|--||--|--|----|------|---|---||-----|-> optional value; absent => bare auth-scheme token
+ # +--||--|--|----|------|---|---||-----|-> maybe whitespace
+ # +|--|--|----|------|---|---||-----|-> = (delimiter)
+ # +--|--|----|------|---|---||-----|-> maybe whitespace
+ # +--|----|------|---|---||-----|-> group quoted or unquoted
+ # +----|------|---|---||-----|-> if quoted...
+ # +------|---|---||-----|-> anything but " or \
+ # +---|---||-----|-> escaped characters allowed
+ # +---||-----|-> or can be empty string
+ # +|-----|-> if unquoted...
+ # +-----|-> anything but , or
+ # +-> at least one char req'd
)
@@ -117,12 +113,17 @@ def unescape_quotes(value: str) -> str:
def parse_header_pairs(header: str) -> dict[str, str]:
"""
- Parse key-value pairs from WWW-Authenticate or similar HTTP headers.
+ Parse key-value pairs from the first challenge of a WWW-Authenticate header.
This function handles the complex format of WWW-Authenticate header values,
supporting both quoted and unquoted values, proper handling of commas in
quoted values, and whitespace variations per RFC 7616.
+ A single header may carry several challenges
+ (https://www.rfc-editor.org/rfc/rfc7235#section-4.1). Parsing
+ stops at the next auth-scheme token so a later challenge's parameters cannot
+ overwrite the first challenge's values; a leading scheme token is skipped.
+
Examples of supported formats:
- key1="value1", key2=value2
- key1 = "value1" , key2="value, with, commas"
@@ -135,11 +136,21 @@ def parse_header_pairs(header: str) -> dict[str, str]:
Returns:
Dictionary mapping parameter names to their values
"""
- return {
- stripped_key: unescape_quotes(quoted_val) if quoted_val else unquoted_val
- for key, quoted_val, unquoted_val in _HEADER_PAIRS_PATTERN.findall(header)
- if (stripped_key := key.strip())
- }
+ pairs: dict[str, str] = {}
+ for match in _HEADER_PAIRS_PATTERN.finditer(header):
+ key = match.group(1)
+ quoted_val, unquoted_val = match.group(2), match.group(3)
+ if quoted_val is None and unquoted_val is None:
+ # Bare token with no "=value": an auth-scheme name, not a parameter.
+ # Skip a leading scheme; once parameters exist, a new scheme marks
+ # the start of the next challenge, so stop here.
+ if pairs:
+ break
+ continue
+ pairs[key] = (
+ unescape_quotes(quoted_val) if quoted_val is not None else unquoted_val
+ )
+ return pairs
class DigestAuthMiddleware:
diff --git a/tests/test_client_middleware_digest_auth.py b/tests/test_client_middleware_digest_auth.py
index cdbedee980f..bca026a53f8 100644
--- a/tests/test_client_middleware_digest_auth.py
+++ b/tests/test_client_middleware_digest_auth.py
@@ -121,6 +121,18 @@ def mock_md5_digest() -> Generator[mock.MagicMock, None, None]:
True,
{"realm": "", "nonce": "abc", "qop": "auth"},
),
+ # Multi-scheme header: a second challenge (Basic) in the same
+ # WWW-Authenticate value must not overwrite the Digest realm/nonce.
+ # https://www.rfc-editor.org/rfc/rfc7235#section-4.1
+ (
+ 401,
+ {
+ "www-authenticate": 'Digest realm="protected", nonce="n1", '
+ 'qop="auth", Basic realm="other"'
+ },
+ True,
+ {"realm": "protected", "nonce": "n1", "qop": "auth"},
+ ),
# Non-401 status
(200, {}, False, {}), # No challenge should be set
],
@@ -469,6 +481,18 @@ async def test_digest_response_exact_match(
),
# Empty header
("", {}),
+ # Multi-scheme header: parsing stops at the next auth-scheme so a later
+ # challenge cannot overwrite the first's values.
+ # https://www.rfc-editor.org/rfc/rfc7235#section-4.1
+ (
+ 'realm="protected", nonce="n1", qop="auth", Basic realm="other"',
+ {"realm": "protected", "nonce": "n1", "qop": "auth"},
+ ),
+ # Multi-scheme header including the leading scheme token
+ (
+ 'Digest realm="protected", nonce="n1", Basic realm="other"',
+ {"realm": "protected", "nonce": "n1"},
+ ),
],
ids=[
"fully_quoted_header",
@@ -480,6 +504,8 @@ async def test_digest_response_exact_match(
"escaped_quotes",
"single_quotes_as_regular_chars",
"empty_header",
+ "multi_scheme_second_challenge_ignored",
+ "multi_scheme_with_leading_scheme",
],
)
def test_parse_header_pairs(header: str, expected_result: dict[str, str]) -> None:
@@ -1563,5 +1589,6 @@ def test_regex_performance() -> None:
f"Regex took {elapsed * 1000:.1f}ms, "
f"expected <{REGEX_TIME_THRESHOLD_SECONDS * 1000:.0f}ms - potential ReDoS issue"
)
- # This example shouldn't produce a match either.
- assert not matches
+ # The lone run of word characters matches as a bare auth-scheme token
+ # with empty value groups, so it never becomes a key=value pair.
+ assert all(not quoted and not unquoted for _, quoted, unquoted in matches)
From 4078e04f172d0b3d3c672cb2dd23eb98b4a79460 Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2026 22:34:29 +0100
Subject: [PATCH 082/137] [PR #12983/20cde095 backport][3.15] Fix empty domain
issue in DigestAuthMiddleware (#13089)
**This is a backport of PR #12983 as merged into master
(20cde095cf3e8f7fab2926a1bbbbe8e6ad189381).**
Co-authored-by: Sam Bull
---
CHANGES/12983.bugfix.rst | 1 +
aiohttp/client_middleware_digest_auth.py | 8 ++-
tests/test_client_middleware_digest_auth.py | 68 +++++++++++++++++++++
3 files changed, 74 insertions(+), 3 deletions(-)
create mode 100644 CHANGES/12983.bugfix.rst
diff --git a/CHANGES/12983.bugfix.rst b/CHANGES/12983.bugfix.rst
new file mode 100644
index 00000000000..03f4083d83a
--- /dev/null
+++ b/CHANGES/12983.bugfix.rst
@@ -0,0 +1 @@
+Fixed ``DigestAuthMiddleware`` raising an ``IndexError`` on empty domain -- by :user:`Dreamsorcerer`.
diff --git a/aiohttp/client_middleware_digest_auth.py b/aiohttp/client_middleware_digest_auth.py
index 21675107401..f4904e363d8 100644
--- a/aiohttp/client_middleware_digest_auth.py
+++ b/aiohttp/client_middleware_digest_auth.py
@@ -445,21 +445,23 @@ def _authenticate(self, response: ClientResponse) -> bool:
# Update protection space based on domain parameter or default to origin
origin = response.url.origin()
+ self._protection_space = []
if domain := self._challenge.get("domain"):
# Parse space-separated list of URIs
- self._protection_space = []
for uri in domain.split():
# Remove quotes if present
uri = uri.strip('"')
+ if not uri:
+ continue
if uri.startswith("/"):
# Path-absolute, relative to origin
self._protection_space.append(str(origin.join(URL(uri))))
else:
# Absolute URI
self._protection_space.append(str(URL(uri)))
- else:
- # No domain specified, protection space is entire origin
+
+ if not self._protection_space:
self._protection_space = [str(origin)]
# Return True only if we found at least one challenge parameter
diff --git a/tests/test_client_middleware_digest_auth.py b/tests/test_client_middleware_digest_auth.py
index bca026a53f8..f7094721e51 100644
--- a/tests/test_client_middleware_digest_auth.py
+++ b/tests/test_client_middleware_digest_auth.py
@@ -1523,6 +1523,74 @@ def test_in_protection_space_multiple_spaces(
assert digest_auth_mw._in_protection_space(URL("http://example.com/other")) is False
+@pytest.mark.parametrize(
+ "domain_value",
+ (r'"\""', '"'),
+ ids=("quoted_escaped_quote", "bare_quote"),
+)
+def test_authenticate_domain_only_quote_does_not_poison_protection_space(
+ digest_auth_mw: DigestAuthMiddleware,
+ domain_value: str,
+) -> None:
+ response = mock.create_autospec(ClientResponse, spec_set=True, instance=True)
+ response.status = 401
+ response.url = URL("http://example.com/resource")
+ response.headers = {
+ "www-authenticate": f'Digest realm="test", nonce="abc", domain={domain_value}'
+ }
+
+ assert digest_auth_mw._authenticate(response) is True
+ assert digest_auth_mw._challenge["domain"] == '"'
+ assert digest_auth_mw._protection_space == ["http://example.com"]
+ assert "" not in digest_auth_mw._protection_space
+ # Must not raise IndexError and must still scope to the anchor origin.
+ assert digest_auth_mw._in_protection_space(URL("http://example.com/other")) is True
+ assert digest_auth_mw._in_protection_space(URL("http://other.com/x")) is False
+
+
+async def test_double_quote_domain_does_not_break_future_requests(
+ aiohttp_server: AiohttpServer,
+) -> None:
+ """End-to-end regression for a ``domain`` directive of just a double quote.
+
+ The first request triggers the challenge and authenticates on retry. Before
+ the fix, the bogus ``domain`` left an empty string in the protection space,
+ so the next request's preemptive-auth check raised ``IndexError``.
+ """
+ digest_auth_mw = DigestAuthMiddleware("user", "pass", preemptive=True)
+ auth_headers: list[str | None] = []
+
+ async def handler(request: Request) -> Response:
+ auth_headers.append(request.headers.get(hdrs.AUTHORIZATION))
+ if request.headers.get(hdrs.AUTHORIZATION) is None:
+ challenge = (
+ 'Digest realm="test", nonce="abc123", qop="auth", '
+ 'algorithm=MD5, domain="\\""'
+ )
+ return Response(
+ status=401,
+ headers={"WWW-Authenticate": challenge},
+ text="Unauthorized",
+ )
+ return Response(text="OK")
+
+ app = Application()
+ app.router.add_get("/path1", handler)
+ app.router.add_get("/path2", handler)
+ server = await aiohttp_server(app)
+
+ async with ClientSession(middlewares=(digest_auth_mw,)) as session:
+ async with session.get(server.make_url("/path1")) as resp:
+ assert resp.status == 200
+ # Previously raised IndexError inside the preemptive-auth check.
+ async with session.get(server.make_url("/path2")) as resp:
+ assert resp.status == 200
+
+ assert auth_headers[0] is None # First request: no auth, gets challenge
+ assert auth_headers[1] is not None # Retry carries the digest response
+ assert auth_headers[2] is not None # Second request: preemptive auth
+
+
async def test_case_sensitive_algorithm_server(
aiohttp_server: AiohttpServer,
) -> None:
From bbcdce7258635bc8c22d1665a39e817c2d004330 Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2026 22:44:08 +0100
Subject: [PATCH 083/137] [PR #12984/7d789204 backport][3.14] Fix Digest
challenge with multiple schemes (#13090)
**This is a backport of PR #12984 as merged into master
(7d7892044c6fe663a566e78c58e5fd25ac6d57b1).**
Co-authored-by: Sam Bull
---
CHANGES/12984.bugfix.rst | 3 +
aiohttp/client_middleware_digest_auth.py | 63 ++++++++++++---------
tests/test_client_middleware_digest_auth.py | 31 +++++++++-
3 files changed, 69 insertions(+), 28 deletions(-)
create mode 100644 CHANGES/12984.bugfix.rst
diff --git a/CHANGES/12984.bugfix.rst b/CHANGES/12984.bugfix.rst
new file mode 100644
index 00000000000..a156216397b
--- /dev/null
+++ b/CHANGES/12984.bugfix.rst
@@ -0,0 +1,3 @@
+Fixed :class:`~aiohttp.DigestAuthMiddleware` corrupting the ``Digest``
+challenge when a ``WWW-Authenticate`` response offered more than one
+authentication scheme -- by :user:`Dreamsorcerer`.
diff --git a/aiohttp/client_middleware_digest_auth.py b/aiohttp/client_middleware_digest_auth.py
index 6c3e37f7c00..21675107401 100644
--- a/aiohttp/client_middleware_digest_auth.py
+++ b/aiohttp/client_middleware_digest_auth.py
@@ -52,27 +52,23 @@ class DigestAuthChallenge(TypedDict, total=False):
# Compile the regex pattern once at module level for performance
_HEADER_PAIRS_PATTERN = re.compile(
- r'(?:^|\s|,\s*)(\w+)\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^\s,]+))'
+ r'(?:^|\s|,\s*)(\w+)(?:\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^\s,]+)))?'
if sys.version_info < (3, 11)
- else r'(?:^|\s|,\s*)((?>\w+))\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^\s,]+))'
- # +------------|--------|--|-|-|--|----|------|----|--||-----|-> Match valid start/sep
- # +--------|--|-|-|--|----|------|----|--||-----|-> alphanumeric key (atomic
- # | | | | | | | | || | group reduces backtracking)
- # +--|-|-|--|----|------|----|--||-----|-> maybe whitespace
- # | | | | | | | || |
- # +-|-|--|----|------|----|--||-----|-> = (delimiter)
- # +-|--|----|------|----|--||-----|-> maybe whitespace
- # | | | | | || |
- # +--|----|------|----|--||-----|-> group quoted or unquoted
- # | | | | || |
- # +----|------|----|--||-----|-> if quoted...
- # +------|----|--||-----|-> anything but " or \
- # +----|--||-----|-> escaped characters allowed
- # +--||-----|-> or can be empty string
- # || |
- # +|-----|-> if unquoted...
- # +-----|-> anything but , or
- # +-> at least one char req'd
+ else r'(?:^|\s|,\s*)((?>\w+))(?:\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^\s,]+)))?'
+ # +------------|--------|--|--||--|--|----|------|---|---||-----|-> Match valid start/sep
+ # +--------|--|--||--|--|----|------|---|---||-----|-> alphanumeric key (atomic group reduces backtracking)
+ # +--|--||--|--|----|------|---|---||-----|-> optional value; absent => bare auth-scheme token
+ # +--||--|--|----|------|---|---||-----|-> maybe whitespace
+ # +|--|--|----|------|---|---||-----|-> = (delimiter)
+ # +--|--|----|------|---|---||-----|-> maybe whitespace
+ # +--|----|------|---|---||-----|-> group quoted or unquoted
+ # +----|------|---|---||-----|-> if quoted...
+ # +------|---|---||-----|-> anything but " or \
+ # +---|---||-----|-> escaped characters allowed
+ # +---||-----|-> or can be empty string
+ # +|-----|-> if unquoted...
+ # +-----|-> anything but , or
+ # +-> at least one char req'd
)
@@ -117,12 +113,17 @@ def unescape_quotes(value: str) -> str:
def parse_header_pairs(header: str) -> dict[str, str]:
"""
- Parse key-value pairs from WWW-Authenticate or similar HTTP headers.
+ Parse key-value pairs from the first challenge of a WWW-Authenticate header.
This function handles the complex format of WWW-Authenticate header values,
supporting both quoted and unquoted values, proper handling of commas in
quoted values, and whitespace variations per RFC 7616.
+ A single header may carry several challenges
+ (https://www.rfc-editor.org/rfc/rfc7235#section-4.1). Parsing
+ stops at the next auth-scheme token so a later challenge's parameters cannot
+ overwrite the first challenge's values; a leading scheme token is skipped.
+
Examples of supported formats:
- key1="value1", key2=value2
- key1 = "value1" , key2="value, with, commas"
@@ -135,11 +136,21 @@ def parse_header_pairs(header: str) -> dict[str, str]:
Returns:
Dictionary mapping parameter names to their values
"""
- return {
- stripped_key: unescape_quotes(quoted_val) if quoted_val else unquoted_val
- for key, quoted_val, unquoted_val in _HEADER_PAIRS_PATTERN.findall(header)
- if (stripped_key := key.strip())
- }
+ pairs: dict[str, str] = {}
+ for match in _HEADER_PAIRS_PATTERN.finditer(header):
+ key = match.group(1)
+ quoted_val, unquoted_val = match.group(2), match.group(3)
+ if quoted_val is None and unquoted_val is None:
+ # Bare token with no "=value": an auth-scheme name, not a parameter.
+ # Skip a leading scheme; once parameters exist, a new scheme marks
+ # the start of the next challenge, so stop here.
+ if pairs:
+ break
+ continue
+ pairs[key] = (
+ unescape_quotes(quoted_val) if quoted_val is not None else unquoted_val
+ )
+ return pairs
class DigestAuthMiddleware:
diff --git a/tests/test_client_middleware_digest_auth.py b/tests/test_client_middleware_digest_auth.py
index cdbedee980f..bca026a53f8 100644
--- a/tests/test_client_middleware_digest_auth.py
+++ b/tests/test_client_middleware_digest_auth.py
@@ -121,6 +121,18 @@ def mock_md5_digest() -> Generator[mock.MagicMock, None, None]:
True,
{"realm": "", "nonce": "abc", "qop": "auth"},
),
+ # Multi-scheme header: a second challenge (Basic) in the same
+ # WWW-Authenticate value must not overwrite the Digest realm/nonce.
+ # https://www.rfc-editor.org/rfc/rfc7235#section-4.1
+ (
+ 401,
+ {
+ "www-authenticate": 'Digest realm="protected", nonce="n1", '
+ 'qop="auth", Basic realm="other"'
+ },
+ True,
+ {"realm": "protected", "nonce": "n1", "qop": "auth"},
+ ),
# Non-401 status
(200, {}, False, {}), # No challenge should be set
],
@@ -469,6 +481,18 @@ async def test_digest_response_exact_match(
),
# Empty header
("", {}),
+ # Multi-scheme header: parsing stops at the next auth-scheme so a later
+ # challenge cannot overwrite the first's values.
+ # https://www.rfc-editor.org/rfc/rfc7235#section-4.1
+ (
+ 'realm="protected", nonce="n1", qop="auth", Basic realm="other"',
+ {"realm": "protected", "nonce": "n1", "qop": "auth"},
+ ),
+ # Multi-scheme header including the leading scheme token
+ (
+ 'Digest realm="protected", nonce="n1", Basic realm="other"',
+ {"realm": "protected", "nonce": "n1"},
+ ),
],
ids=[
"fully_quoted_header",
@@ -480,6 +504,8 @@ async def test_digest_response_exact_match(
"escaped_quotes",
"single_quotes_as_regular_chars",
"empty_header",
+ "multi_scheme_second_challenge_ignored",
+ "multi_scheme_with_leading_scheme",
],
)
def test_parse_header_pairs(header: str, expected_result: dict[str, str]) -> None:
@@ -1563,5 +1589,6 @@ def test_regex_performance() -> None:
f"Regex took {elapsed * 1000:.1f}ms, "
f"expected <{REGEX_TIME_THRESHOLD_SECONDS * 1000:.0f}ms - potential ReDoS issue"
)
- # This example shouldn't produce a match either.
- assert not matches
+ # The lone run of word characters matches as a bare auth-scheme token
+ # with empty value groups, so it never becomes a key=value pair.
+ assert all(not quoted and not unquoted for _, quoted, unquoted in matches)
From e1cad9706ec297ece656d4a099b2010f0738c83d Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2026 22:56:18 +0100
Subject: [PATCH 084/137] [PR #12983/20cde095 backport][3.14] Fix empty domain
issue in DigestAuthMiddleware (#13088)
**This is a backport of PR #12983 as merged into master
(20cde095cf3e8f7fab2926a1bbbbe8e6ad189381).**
Co-authored-by: Sam Bull
---
CHANGES/12983.bugfix.rst | 1 +
aiohttp/client_middleware_digest_auth.py | 8 ++-
tests/test_client_middleware_digest_auth.py | 68 +++++++++++++++++++++
3 files changed, 74 insertions(+), 3 deletions(-)
create mode 100644 CHANGES/12983.bugfix.rst
diff --git a/CHANGES/12983.bugfix.rst b/CHANGES/12983.bugfix.rst
new file mode 100644
index 00000000000..03f4083d83a
--- /dev/null
+++ b/CHANGES/12983.bugfix.rst
@@ -0,0 +1 @@
+Fixed ``DigestAuthMiddleware`` raising an ``IndexError`` on empty domain -- by :user:`Dreamsorcerer`.
diff --git a/aiohttp/client_middleware_digest_auth.py b/aiohttp/client_middleware_digest_auth.py
index 21675107401..f4904e363d8 100644
--- a/aiohttp/client_middleware_digest_auth.py
+++ b/aiohttp/client_middleware_digest_auth.py
@@ -445,21 +445,23 @@ def _authenticate(self, response: ClientResponse) -> bool:
# Update protection space based on domain parameter or default to origin
origin = response.url.origin()
+ self._protection_space = []
if domain := self._challenge.get("domain"):
# Parse space-separated list of URIs
- self._protection_space = []
for uri in domain.split():
# Remove quotes if present
uri = uri.strip('"')
+ if not uri:
+ continue
if uri.startswith("/"):
# Path-absolute, relative to origin
self._protection_space.append(str(origin.join(URL(uri))))
else:
# Absolute URI
self._protection_space.append(str(URL(uri)))
- else:
- # No domain specified, protection space is entire origin
+
+ if not self._protection_space:
self._protection_space = [str(origin)]
# Return True only if we found at least one challenge parameter
diff --git a/tests/test_client_middleware_digest_auth.py b/tests/test_client_middleware_digest_auth.py
index bca026a53f8..f7094721e51 100644
--- a/tests/test_client_middleware_digest_auth.py
+++ b/tests/test_client_middleware_digest_auth.py
@@ -1523,6 +1523,74 @@ def test_in_protection_space_multiple_spaces(
assert digest_auth_mw._in_protection_space(URL("http://example.com/other")) is False
+@pytest.mark.parametrize(
+ "domain_value",
+ (r'"\""', '"'),
+ ids=("quoted_escaped_quote", "bare_quote"),
+)
+def test_authenticate_domain_only_quote_does_not_poison_protection_space(
+ digest_auth_mw: DigestAuthMiddleware,
+ domain_value: str,
+) -> None:
+ response = mock.create_autospec(ClientResponse, spec_set=True, instance=True)
+ response.status = 401
+ response.url = URL("http://example.com/resource")
+ response.headers = {
+ "www-authenticate": f'Digest realm="test", nonce="abc", domain={domain_value}'
+ }
+
+ assert digest_auth_mw._authenticate(response) is True
+ assert digest_auth_mw._challenge["domain"] == '"'
+ assert digest_auth_mw._protection_space == ["http://example.com"]
+ assert "" not in digest_auth_mw._protection_space
+ # Must not raise IndexError and must still scope to the anchor origin.
+ assert digest_auth_mw._in_protection_space(URL("http://example.com/other")) is True
+ assert digest_auth_mw._in_protection_space(URL("http://other.com/x")) is False
+
+
+async def test_double_quote_domain_does_not_break_future_requests(
+ aiohttp_server: AiohttpServer,
+) -> None:
+ """End-to-end regression for a ``domain`` directive of just a double quote.
+
+ The first request triggers the challenge and authenticates on retry. Before
+ the fix, the bogus ``domain`` left an empty string in the protection space,
+ so the next request's preemptive-auth check raised ``IndexError``.
+ """
+ digest_auth_mw = DigestAuthMiddleware("user", "pass", preemptive=True)
+ auth_headers: list[str | None] = []
+
+ async def handler(request: Request) -> Response:
+ auth_headers.append(request.headers.get(hdrs.AUTHORIZATION))
+ if request.headers.get(hdrs.AUTHORIZATION) is None:
+ challenge = (
+ 'Digest realm="test", nonce="abc123", qop="auth", '
+ 'algorithm=MD5, domain="\\""'
+ )
+ return Response(
+ status=401,
+ headers={"WWW-Authenticate": challenge},
+ text="Unauthorized",
+ )
+ return Response(text="OK")
+
+ app = Application()
+ app.router.add_get("/path1", handler)
+ app.router.add_get("/path2", handler)
+ server = await aiohttp_server(app)
+
+ async with ClientSession(middlewares=(digest_auth_mw,)) as session:
+ async with session.get(server.make_url("/path1")) as resp:
+ assert resp.status == 200
+ # Previously raised IndexError inside the preemptive-auth check.
+ async with session.get(server.make_url("/path2")) as resp:
+ assert resp.status == 200
+
+ assert auth_headers[0] is None # First request: no auth, gets challenge
+ assert auth_headers[1] is not None # Retry carries the digest response
+ assert auth_headers[2] is not None # Second request: preemptive auth
+
+
async def test_case_sensitive_algorithm_server(
aiohttp_server: AiohttpServer,
) -> None:
From b2409db9ab3d2f2b4d1bffbc7b87d879c69b74ed Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Wed, 8 Jul 2026 01:20:23 +0100
Subject: [PATCH 085/137] [PR #12985/4416d89e backport][3.15] Close request
body Payload on exception (#13093)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
**This is a backport of PR #12985 as merged into master
(4416d89e73f4a09339eb08fabef3fa010ec9184f).**
Co-authored-by: Sam Bull
Co-authored-by: tonghuaroot (η«₯θ―)
---
CHANGES/12985.bugfix.rst | 1 +
aiohttp/client.py | 4 +++
tests/test_client_functional.py | 56 +++++++++++++++++++++++++++++++++
3 files changed, 61 insertions(+)
create mode 100644 CHANGES/12985.bugfix.rst
diff --git a/CHANGES/12985.bugfix.rst b/CHANGES/12985.bugfix.rst
new file mode 100644
index 00000000000..055b8572e42
--- /dev/null
+++ b/CHANGES/12985.bugfix.rst
@@ -0,0 +1 @@
+Fixed client not closing cleanly after an exception -- by :user:`Dreamsorcerer`.
diff --git a/aiohttp/client.py b/aiohttp/client.py
index b4c09d05e12..6f9d781a342 100644
--- a/aiohttp/client.py
+++ b/aiohttp/client.py
@@ -691,6 +691,7 @@ async def _request(
await trace.send_request_start(method, url.update_query(params), headers)
timer = tm.timer()
+ req: ClientRequest | None = None
try:
with timer:
# https://www.rfc-editor.org/rfc/rfc9112.html#name-retrying-requests
@@ -1014,6 +1015,9 @@ async def _connect_and_send_request(
handle.cancel()
handle = None
+ if req is not None and req._body is not None:
+ await req._body.close()
+
for trace in traces:
await trace.send_request_exception(
method, url.update_query(params), headers, e
diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py
index 524c4a2efcd..2064ec2aafe 100644
--- a/tests/test_client_functional.py
+++ b/tests/test_client_functional.py
@@ -5534,6 +5534,62 @@ async def redirect_handler(request: web.Request) -> web.Response:
), "Payload.close() was not called when InvalidUrlRedirectClientError (invalid origin) was raised"
+async def test_request_body_closed_on_server_disconnect() -> None:
+ async def drop(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
+ # Slam the connection shut without sending a response.
+ writer.close()
+
+ server = await asyncio.start_server(drop, "127.0.0.1", 0)
+ port = server.sockets[0].getsockname()[1]
+ payload = MockedBytesPayload(b"x" * 1024)
+ try:
+ async with aiohttp.ClientSession() as session:
+ with pytest.raises(aiohttp.ClientError):
+ await session.post(f"http://127.0.0.1:{port}/", data=payload)
+ finally:
+ server.close()
+ await server.wait_closed()
+
+ assert (
+ payload.close_called
+ ), "Payload.close() was not called after a mid-upload disconnect"
+
+
+async def test_request_body_closed_on_cancellation() -> None:
+ accepted = asyncio.Event()
+
+ async def stall(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
+ accepted.set()
+ try:
+ await reader.read() # wait for client EOF; never respond
+ finally:
+ writer.close()
+
+ server = await asyncio.start_server(stall, "127.0.0.1", 0)
+ port = server.sockets[0].getsockname()[1]
+ payload = MockedBytesPayload(b"y" * 1024)
+ try:
+ async with aiohttp.ClientSession() as session:
+ task = asyncio.create_task(
+ session.post(f"http://127.0.0.1:{port}/", data=payload)
+ )
+ await accepted.wait()
+ task.cancel()
+ with pytest.raises(asyncio.CancelledError):
+ await task
+ finally:
+ server.close()
+ await server.wait_closed()
+
+ assert payload.close_called, "Payload.close() was not called after cancellation"
+
+
+async def test_request_error_before_body_created_does_not_mask() -> None:
+ async with aiohttp.ClientSession() as session:
+ with pytest.raises(InvalidUrlClientError):
+ await session.get("http:///path")
+
+
async def test_amazon_like_cookie_scenario(aiohttp_client: AiohttpClient) -> None:
"""Test real-world cookie scenario similar to Amazon."""
From e1d5b87997f2d8b26aff9b34aed695968de405e2 Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Wed, 8 Jul 2026 01:20:39 +0100
Subject: [PATCH 086/137] [PR #12985/4416d89e backport][3.14] Close request
body Payload on exception (#13092)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
**This is a backport of PR #12985 as merged into master
(4416d89e73f4a09339eb08fabef3fa010ec9184f).**
Co-authored-by: Sam Bull
Co-authored-by: tonghuaroot (η«₯θ―)
---
CHANGES/12985.bugfix.rst | 1 +
aiohttp/client.py | 4 +++
tests/test_client_functional.py | 56 +++++++++++++++++++++++++++++++++
3 files changed, 61 insertions(+)
create mode 100644 CHANGES/12985.bugfix.rst
diff --git a/CHANGES/12985.bugfix.rst b/CHANGES/12985.bugfix.rst
new file mode 100644
index 00000000000..055b8572e42
--- /dev/null
+++ b/CHANGES/12985.bugfix.rst
@@ -0,0 +1 @@
+Fixed client not closing cleanly after an exception -- by :user:`Dreamsorcerer`.
diff --git a/aiohttp/client.py b/aiohttp/client.py
index 62bcd0ab585..1150e09a9dd 100644
--- a/aiohttp/client.py
+++ b/aiohttp/client.py
@@ -696,6 +696,7 @@ async def _request(
await trace.send_request_start(method, url.update_query(params), headers)
timer = tm.timer()
+ req: ClientRequest | None = None
try:
with timer:
# https://www.rfc-editor.org/rfc/rfc9112.html#name-retrying-requests
@@ -1019,6 +1020,9 @@ async def _connect_and_send_request(
handle.cancel()
handle = None
+ if req is not None and req._body is not None:
+ await req._body.close()
+
for trace in traces:
await trace.send_request_exception(
method, url.update_query(params), headers, e
diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py
index 524c4a2efcd..2064ec2aafe 100644
--- a/tests/test_client_functional.py
+++ b/tests/test_client_functional.py
@@ -5534,6 +5534,62 @@ async def redirect_handler(request: web.Request) -> web.Response:
), "Payload.close() was not called when InvalidUrlRedirectClientError (invalid origin) was raised"
+async def test_request_body_closed_on_server_disconnect() -> None:
+ async def drop(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
+ # Slam the connection shut without sending a response.
+ writer.close()
+
+ server = await asyncio.start_server(drop, "127.0.0.1", 0)
+ port = server.sockets[0].getsockname()[1]
+ payload = MockedBytesPayload(b"x" * 1024)
+ try:
+ async with aiohttp.ClientSession() as session:
+ with pytest.raises(aiohttp.ClientError):
+ await session.post(f"http://127.0.0.1:{port}/", data=payload)
+ finally:
+ server.close()
+ await server.wait_closed()
+
+ assert (
+ payload.close_called
+ ), "Payload.close() was not called after a mid-upload disconnect"
+
+
+async def test_request_body_closed_on_cancellation() -> None:
+ accepted = asyncio.Event()
+
+ async def stall(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
+ accepted.set()
+ try:
+ await reader.read() # wait for client EOF; never respond
+ finally:
+ writer.close()
+
+ server = await asyncio.start_server(stall, "127.0.0.1", 0)
+ port = server.sockets[0].getsockname()[1]
+ payload = MockedBytesPayload(b"y" * 1024)
+ try:
+ async with aiohttp.ClientSession() as session:
+ task = asyncio.create_task(
+ session.post(f"http://127.0.0.1:{port}/", data=payload)
+ )
+ await accepted.wait()
+ task.cancel()
+ with pytest.raises(asyncio.CancelledError):
+ await task
+ finally:
+ server.close()
+ await server.wait_closed()
+
+ assert payload.close_called, "Payload.close() was not called after cancellation"
+
+
+async def test_request_error_before_body_created_does_not_mask() -> None:
+ async with aiohttp.ClientSession() as session:
+ with pytest.raises(InvalidUrlClientError):
+ await session.get("http:///path")
+
+
async def test_amazon_like_cookie_scenario(aiohttp_client: AiohttpClient) -> None:
"""Test real-world cookie scenario similar to Amazon."""
From 9868c5f098918bcd624ef591c9b691f70feec3b5 Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Wed, 8 Jul 2026 02:23:52 +0100
Subject: [PATCH 087/137] [PR #13001/fe353185 backport][3.15] Fix IndexError in
parser on edge case (#13095)
**This is a backport of PR #13001 as merged into master
(fe353185eb5c6eb87cb3cca92213c7098aa42437).**
Co-authored-by: Sam Bull
---
CHANGES/13001.bugfix.rst | 1 +
aiohttp/http_parser.py | 29 ++++++++++++++---------------
tests/test_http_parser.py | 31 +++++++++++++++++++++++++++++++
3 files changed, 46 insertions(+), 15 deletions(-)
create mode 100644 CHANGES/13001.bugfix.rst
diff --git a/CHANGES/13001.bugfix.rst b/CHANGES/13001.bugfix.rst
new file mode 100644
index 00000000000..67511e3c95e
--- /dev/null
+++ b/CHANGES/13001.bugfix.rst
@@ -0,0 +1 @@
+Fixed an :exc:`IndexError` in the pure-Python HTTP parser -- by :user:`Dreamsorcerer`.
diff --git a/aiohttp/http_parser.py b/aiohttp/http_parser.py
index 212b82228ea..e08b4c11aca 100644
--- a/aiohttp/http_parser.py
+++ b/aiohttp/http_parser.py
@@ -1157,19 +1157,20 @@ def feed_data(self, chunk: bytes, size: int) -> bool:
self.size += size
self.out.total_compressed_bytes = self.size
- # RFC1950
- # bits 0..3 = CM = 0b1000 = 8 = "deflate"
- # bits 4..7 = CINFO = 1..7 = windows size.
- if (
- not self._started_decoding
- and self.encoding == "deflate"
- and chunk[0] & 0xF != 8
- ):
- # Change the decoder to decompress incorrectly compressed data
- # Actually we should issue a warning about non-RFC-compliant data.
- self.decompressor = ZLibDecompressor(
- encoding=self.encoding, suppress_deflate_header=True
- )
+ # Inspect the first real byte once to choose the decompressor. An empty
+ # chunk (e.g. a chunk-size line arriving without body bytes) has no
+ # header to sniff, so skip it and wait for the first data byte.
+ if not self._started_decoding and chunk:
+ # RFC1950
+ # bits 0..3 = CM = 0b1000 = 8 = "deflate"
+ # bits 4..7 = CINFO = 1..7 = windows size.
+ if self.encoding == "deflate" and chunk[0] & 0xF != 8:
+ # Change the decoder to decompress incorrectly compressed data
+ # Actually we should issue a warning about non-RFC-compliant data.
+ self.decompressor = ZLibDecompressor(
+ encoding=self.encoding, suppress_deflate_header=True
+ )
+ self._started_decoding = True
low_water = self.out._low_water
max_length = (
@@ -1182,8 +1183,6 @@ def feed_data(self, chunk: bytes, size: int) -> bool:
"Can not decode content-encoding: %s" % self.encoding
)
- self._started_decoding = True
-
if chunk:
self.out.feed_data(chunk, len(chunk))
return self.decompressor.data_available # type: ignore[no-any-return]
diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py
index 0ed254557f5..407292079d1 100644
--- a/tests/test_http_parser.py
+++ b/tests/test_http_parser.py
@@ -1321,6 +1321,37 @@ async def test_compressed_chunked_with_pending(response: HttpResponseParser) ->
assert result == original
+async def test_compressed_chunked_split_chunk_size_line(
+ response: HttpResponseParser,
+) -> None:
+ """First chunk-size line arrives in a feed that carries no body bytes.
+
+ Regression test for an ``IndexError`` in the pure-Python parser: the
+ chunked parser fed an empty chunk to ``DeflateBuffer`` before any data
+ had been decoded, and the deflate header sniff indexed ``chunk[0]`` on it.
+ """
+ original = b"Hello, world! " * 4
+ compressed = zlib.compress(original)
+ size_line = hex(len(compressed))[2:].encode() + b"\r\n"
+ headers = (
+ b"HTTP/1.1 200 OK\r\n"
+ b"Transfer-Encoding: chunked\r\n"
+ b"Content-Encoding: deflate\r\n"
+ b"\r\n"
+ )
+
+ msgs, upgrade, tail = response.feed_data(headers)
+ payload = msgs[0][-1]
+ # The chunk-size line lands with no body bytes in the same feed, so the
+ # parser transitions into the chunk body with an empty buffer.
+ response.feed_data(size_line)
+ response.feed_data(compressed + b"\r\n0\r\n\r\n")
+
+ result = await payload.read()
+ assert result == original
+ assert payload.exception() is None
+
+
async def test_compressed_until_eof_with_pending(response: HttpResponseParser) -> None:
"""Test read-until-eof + compressed with pause."""
# Must be large enough to exceed high water mark.
From 199628943cb6eab8032ce57fb163c98f10d8c53b Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Wed, 8 Jul 2026 02:24:37 +0100
Subject: [PATCH 088/137] [PR #13001/fe353185 backport][3.14] Fix IndexError in
parser on edge case (#13094)
**This is a backport of PR #13001 as merged into master
(fe353185eb5c6eb87cb3cca92213c7098aa42437).**
Co-authored-by: Sam Bull
---
CHANGES/13001.bugfix.rst | 1 +
aiohttp/http_parser.py | 29 ++++++++++++++---------------
tests/test_http_parser.py | 31 +++++++++++++++++++++++++++++++
3 files changed, 46 insertions(+), 15 deletions(-)
create mode 100644 CHANGES/13001.bugfix.rst
diff --git a/CHANGES/13001.bugfix.rst b/CHANGES/13001.bugfix.rst
new file mode 100644
index 00000000000..67511e3c95e
--- /dev/null
+++ b/CHANGES/13001.bugfix.rst
@@ -0,0 +1 @@
+Fixed an :exc:`IndexError` in the pure-Python HTTP parser -- by :user:`Dreamsorcerer`.
diff --git a/aiohttp/http_parser.py b/aiohttp/http_parser.py
index 212b82228ea..e08b4c11aca 100644
--- a/aiohttp/http_parser.py
+++ b/aiohttp/http_parser.py
@@ -1157,19 +1157,20 @@ def feed_data(self, chunk: bytes, size: int) -> bool:
self.size += size
self.out.total_compressed_bytes = self.size
- # RFC1950
- # bits 0..3 = CM = 0b1000 = 8 = "deflate"
- # bits 4..7 = CINFO = 1..7 = windows size.
- if (
- not self._started_decoding
- and self.encoding == "deflate"
- and chunk[0] & 0xF != 8
- ):
- # Change the decoder to decompress incorrectly compressed data
- # Actually we should issue a warning about non-RFC-compliant data.
- self.decompressor = ZLibDecompressor(
- encoding=self.encoding, suppress_deflate_header=True
- )
+ # Inspect the first real byte once to choose the decompressor. An empty
+ # chunk (e.g. a chunk-size line arriving without body bytes) has no
+ # header to sniff, so skip it and wait for the first data byte.
+ if not self._started_decoding and chunk:
+ # RFC1950
+ # bits 0..3 = CM = 0b1000 = 8 = "deflate"
+ # bits 4..7 = CINFO = 1..7 = windows size.
+ if self.encoding == "deflate" and chunk[0] & 0xF != 8:
+ # Change the decoder to decompress incorrectly compressed data
+ # Actually we should issue a warning about non-RFC-compliant data.
+ self.decompressor = ZLibDecompressor(
+ encoding=self.encoding, suppress_deflate_header=True
+ )
+ self._started_decoding = True
low_water = self.out._low_water
max_length = (
@@ -1182,8 +1183,6 @@ def feed_data(self, chunk: bytes, size: int) -> bool:
"Can not decode content-encoding: %s" % self.encoding
)
- self._started_decoding = True
-
if chunk:
self.out.feed_data(chunk, len(chunk))
return self.decompressor.data_available # type: ignore[no-any-return]
diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py
index 9e115620eba..b3e0ab67cff 100644
--- a/tests/test_http_parser.py
+++ b/tests/test_http_parser.py
@@ -1293,6 +1293,37 @@ async def test_compressed_chunked_with_pending(response: HttpResponseParser) ->
assert result == original
+async def test_compressed_chunked_split_chunk_size_line(
+ response: HttpResponseParser,
+) -> None:
+ """First chunk-size line arrives in a feed that carries no body bytes.
+
+ Regression test for an ``IndexError`` in the pure-Python parser: the
+ chunked parser fed an empty chunk to ``DeflateBuffer`` before any data
+ had been decoded, and the deflate header sniff indexed ``chunk[0]`` on it.
+ """
+ original = b"Hello, world! " * 4
+ compressed = zlib.compress(original)
+ size_line = hex(len(compressed))[2:].encode() + b"\r\n"
+ headers = (
+ b"HTTP/1.1 200 OK\r\n"
+ b"Transfer-Encoding: chunked\r\n"
+ b"Content-Encoding: deflate\r\n"
+ b"\r\n"
+ )
+
+ msgs, upgrade, tail = response.feed_data(headers)
+ payload = msgs[0][-1]
+ # The chunk-size line lands with no body bytes in the same feed, so the
+ # parser transitions into the chunk body with an empty buffer.
+ response.feed_data(size_line)
+ response.feed_data(compressed + b"\r\n0\r\n\r\n")
+
+ result = await payload.read()
+ assert result == original
+ assert payload.exception() is None
+
+
async def test_compressed_until_eof_with_pending(response: HttpResponseParser) -> None:
"""Test read-until-eof + compressed with pause."""
# Must be large enough to exceed high water mark.
From 63157f503cd75c569a1cf96efd0af3015f276a16 Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Wed, 8 Jul 2026 02:25:06 +0100
Subject: [PATCH 089/137] [PR #13010/9e00085c backport][3.15] Fix
parse_mimetype producing spurious empty-key parameter for whitespace-only
segments after semicolons (#13097)
**This is a backport of PR #13010 as merged into master
(9e00085c958fbcd40de37867e773bf7cd3bbee0c).**
Co-authored-by: JSap0914 <116227558+JSap0914@users.noreply.github.com>
---
aiohttp/helpers.py | 2 +-
tests/test_helpers.py | 21 +++++++++++++++++++++
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py
index 486b4976cac..fbdd6653fd0 100644
--- a/aiohttp/helpers.py
+++ b/aiohttp/helpers.py
@@ -373,7 +373,7 @@ def parse_mimetype(mimetype: str) -> MimeType:
parts = mimetype.split(";")
params: MultiDict[str] = MultiDict()
for item in parts[1:]:
- if not item:
+ if not item.strip():
continue
key, _, value = item.partition("=")
params.add(key.lower().strip(), value.strip(' "'))
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 9c1193a7a87..197cbfaf61c 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -60,6 +60,27 @@
"text/plain;base64",
helpers.MimeType("text", "plain", "", MultiDict({"base64": ""})),
),
+ (
+ "text/html; ",
+ helpers.MimeType("text", "html", "", MultiDictProxy(MultiDict())),
+ ),
+ (
+ "text/html; ",
+ helpers.MimeType("text", "html", "", MultiDictProxy(MultiDict())),
+ ),
+ (
+ "text/html;\t",
+ helpers.MimeType("text", "html", "", MultiDictProxy(MultiDict())),
+ ),
+ (
+ "text/html; charset=utf-8; ",
+ helpers.MimeType(
+ "text",
+ "html",
+ "",
+ MultiDictProxy(MultiDict({"charset": "utf-8"})),
+ ),
+ ),
],
)
def test_parse_mimetype(mimetype, expected) -> None:
From d0b97523e4880faa34f1c83cb31a7bbf6e0de712 Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Wed, 8 Jul 2026 11:30:45 +0100
Subject: [PATCH 090/137] [PR #13010/9e00085c backport][3.14] Fix
parse_mimetype producing spurious empty-key parameter for whitespace-only
segments after semicolons (#13096)
**This is a backport of PR #13010 as merged into master
(9e00085c958fbcd40de37867e773bf7cd3bbee0c).**
Co-authored-by: JSap0914 <116227558+JSap0914@users.noreply.github.com>
---
aiohttp/helpers.py | 2 +-
tests/test_helpers.py | 21 +++++++++++++++++++++
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py
index 0fa0e8fbf92..bf905ce4716 100644
--- a/aiohttp/helpers.py
+++ b/aiohttp/helpers.py
@@ -366,7 +366,7 @@ def parse_mimetype(mimetype: str) -> MimeType:
parts = mimetype.split(";")
params: MultiDict[str] = MultiDict()
for item in parts[1:]:
- if not item:
+ if not item.strip():
continue
key, _, value = item.partition("=")
params.add(key.lower().strip(), value.strip(' "'))
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 9c1193a7a87..197cbfaf61c 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -60,6 +60,27 @@
"text/plain;base64",
helpers.MimeType("text", "plain", "", MultiDict({"base64": ""})),
),
+ (
+ "text/html; ",
+ helpers.MimeType("text", "html", "", MultiDictProxy(MultiDict())),
+ ),
+ (
+ "text/html; ",
+ helpers.MimeType("text", "html", "", MultiDictProxy(MultiDict())),
+ ),
+ (
+ "text/html;\t",
+ helpers.MimeType("text", "html", "", MultiDictProxy(MultiDict())),
+ ),
+ (
+ "text/html; charset=utf-8; ",
+ helpers.MimeType(
+ "text",
+ "html",
+ "",
+ MultiDictProxy(MultiDict({"charset": "utf-8"})),
+ ),
+ ),
],
)
def test_parse_mimetype(mimetype, expected) -> None:
From f312e0beea82967317a1d44c134c2424e3fe6c60 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 8 Jul 2026 11:32:22 +0000
Subject: [PATCH 091/137] Bump filelock from 3.29.6 to 3.29.7 (#13103)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.29.6 to
3.29.7.
Release notes
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 4a992432d06..3bf6cca6edc 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -73,7 +73,7 @@ exceptiongroup==1.3.1
# via pytest
execnet==2.1.2
# via pytest-xdist
-filelock==3.29.6
+filelock==3.29.7
# via
# python-discovery
# virtualenv
diff --git a/requirements/dev.txt b/requirements/dev.txt
index bde0838ebff..d6104be24b9 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -71,7 +71,7 @@ exceptiongroup==1.3.1
# via pytest
execnet==2.1.2
# via pytest-xdist
-filelock==3.29.6
+filelock==3.29.7
# via
# python-discovery
# virtualenv
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 6788d69aa03..5c5022c9d9d 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -28,7 +28,7 @@ distlib==0.4.3
# via virtualenv
exceptiongroup==1.3.1
# via pytest
-filelock==3.29.6
+filelock==3.29.7
# via
# python-discovery
# virtualenv
From 3eafeb06d96430ab369b0058e3892f4320a86380 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 8 Jul 2026 11:37:57 +0000
Subject: [PATCH 092/137] Bump astral-sh/setup-uv from 8.3.1 to 8.3.2 (#13101)
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from
8.3.1 to 8.3.2.
Commits
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci-cd.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 9ce995594e6..24abc33bf70 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -175,7 +175,7 @@ jobs:
# important: do not use system python
env:
UV_PYTHON_PREFERENCE: only-managed
- uses: astral-sh/setup-uv@v8.3.1
+ uses: astral-sh/setup-uv@v8.3.2
with:
python-version: ${{ matrix.pyver }}
activate-environment: true
@@ -282,7 +282,7 @@ jobs:
# important: do not use system python
env:
UV_PYTHON_PREFERENCE: only-managed
- uses: astral-sh/setup-uv@v8.3.1
+ uses: astral-sh/setup-uv@v8.3.2
with:
python-version: ${{ matrix.pyver }}
activate-environment: true
@@ -354,7 +354,7 @@ jobs:
# important: do not use system python
env:
UV_PYTHON_PREFERENCE: only-managed
- uses: astral-sh/setup-uv@v8.3.1
+ uses: astral-sh/setup-uv@v8.3.2
with:
python-version: ${{ matrix.pyver }}
activate-environment: true
From 946bcd5d3cbc285bc8d87861f80dd6b23e095234 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 8 Jul 2026 11:51:31 +0000
Subject: [PATCH 093/137] Bump charset-normalizer from 3.4.8 to 3.4.9 (#13108)
Bumps [charset-normalizer](https://github.com/jawah/charset_normalizer)
from 3.4.8 to 3.4.9.
Release notes
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/doc-spelling.txt | 2 +-
requirements/doc.txt | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 3bf6cca6edc..ef1104faa9b 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -48,7 +48,7 @@ cffi==2.1.0
# pycares
cfgv==3.5.0
# via pre-commit
-charset-normalizer==3.4.8
+charset-normalizer==3.4.9
# via requests
click==8.4.2
# via
diff --git a/requirements/dev.txt b/requirements/dev.txt
index d6104be24b9..eda31e5feee 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -48,7 +48,7 @@ cffi==2.1.0
# pycares
cfgv==3.5.0
# via pre-commit
-charset-normalizer==3.4.8
+charset-normalizer==3.4.9
# via requests
click==8.4.2
# via
diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt
index d54a504c8b2..ade053c613a 100644
--- a/requirements/doc-spelling.txt
+++ b/requirements/doc-spelling.txt
@@ -12,7 +12,7 @@ babel==2.18.0
# via sphinx
certifi==2026.6.17
# via requests
-charset-normalizer==3.4.8
+charset-normalizer==3.4.9
# via requests
click==8.4.2
# via towncrier
diff --git a/requirements/doc.txt b/requirements/doc.txt
index 17b1bef9fc4..25ebe0e4a06 100644
--- a/requirements/doc.txt
+++ b/requirements/doc.txt
@@ -12,7 +12,7 @@ babel==2.18.0
# via sphinx
certifi==2026.6.17
# via requests
-charset-normalizer==3.4.8
+charset-normalizer==3.4.9
# via requests
click==8.4.2
# via towncrier
From d26417fb5eb0f9bef1f8ccccbda9dc250a3e13e8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 9 Jul 2026 10:54:43 +0000
Subject: [PATCH 094/137] Bump build from 1.5.0 to 1.5.1 (#13112)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [build](https://github.com/pypa/build) from 1.5.0 to 1.5.1.
Release notes
Add --report=PATH to write a machine-readable JSON
report of built artifacts; --metadata now also accepts
.whl files - by :user:gaborbernat
(:issue:198)
The srcdir argument now accepts .tar.gz
source distributions, extracting and building from them - by
:user:gaborbernat (:issue:311)
The "Unmet dependencies" error from
--no-isolation builds now shows the wanted version, found
version, and
interpreter - by :user:gaborbernat
(:issue:504)
Add --sdist-extract-dir to extract the intermediate
sdist into a persistent directory, enabling compiler cache reuse
across rebuilds - by :user:gaborbernat
(:issue:614)
Add --env-dir to place the isolated build environment
at a fixed path, enabling compiler cache reuse across builds -
by :user:gaborbernat (:issue:655)
Print a summary of resolved dependency versions
(name==version) after installing them in isolated builds -
by
:user:gaborbernat (:issue:959)
On build failure, print a tip pointing to --env-dir and
--sdist-extract-dir for debugging and link to the
"Debug
a failed build" how-to - reported by :user:dimpase,
implemented by :user:gaborbernat
(:issue:966)
Bugfixes
Drain verbose subprocess output inline instead of using a
ThreadPoolExecutor, which silently swallowed logging
errors - by :user:henryiii (:issue:1098)
Reject a file passed as --env-dir with a clear error
instead of a raw FileExistsError - by
:user:henryiii (:issue:1100)
Emit CLI warnings to stderr instead of stdout, so they no longer
corrupt --metadata JSON output on stdout - by
:user:ymyzk (:issue:1111)
Fix the Windows symlink support probe always returning
False due to a stale object interpolated into the
destination
path - by :user:henryiii (:issue:1118)
Fix metadata_path's build-backend fallback returning a
nonexistent dist-info path for wheels with a build tag - by
:user:henryiii (:issue:1119)
Write pip/uv requirements and constraints files with \n
instead of os.linesep, avoiding doubled \r\r\n
line
endings on Windows - by :user:henryiii
(:issue:1120)
Batch of small robustness fixes: correct macOS release parsing for
the minimum pip version, avoid sharing the mutable
default build-system table between builders, keep the original error
when isolated-environment setup fails early, and
raise BuildException for an invalid wheel - by
:user:henryiii (:issue:1121)
Decide color support independently for stdout and stderr instead of
only checking stdout.isatty(), so redirecting
one stream no longer disables or leaks ANSI colors on the other - by
:user:henryiii (:issue:1123)
Deprecations and Removals
Deprecate :func:build.util.project_wheel_metadata; use
python -m build --metadata instead - by
:user:gaborbernat (:issue:557)
Warn on config settings without a value (e.g.
-C--my-flag); use -C--my-flag= instead for
compatibility with pip
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index ef1104faa9b..97f1a8fe6e5 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -38,7 +38,7 @@ blockbuster==1.5.26
# -r requirements/test-common.in
brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
-build==1.5.0
+build==1.5.1
# via pip-tools
certifi==2026.6.17
# via requests
diff --git a/requirements/dev.txt b/requirements/dev.txt
index eda31e5feee..627f066b7a7 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -38,7 +38,7 @@ blockbuster==1.5.26
# -r requirements/test-common.in
brotli==1.2.0 ; platform_python_implementation == "CPython" and sys_platform != "android" and sys_platform != "ios"
# via -r requirements/runtime-deps.in
-build==1.5.0
+build==1.5.1
# via pip-tools
certifi==2026.6.17
# via requests
From 0a507cbf3def56c8024890816be5f9c0344384eb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 9 Jul 2026 11:24:17 +0000
Subject: [PATCH 095/137] Bump librt from 0.12.0 to 0.13.0 (#13115)
Bumps [librt](https://github.com/mypyc/librt) from 0.12.0 to 0.13.0.
Commits
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
requirements/test-common.txt | 2 +-
requirements/test-ft.txt | 2 +-
requirements/test.txt | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 97f1a8fe6e5..b29514ea01a 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -110,7 +110,7 @@ jinja2==3.1.6
# sphinx
# sphinxcontrib-mermaid
# towncrier
-librt==0.12.0
+librt==0.13.0
# via mypy
markdown-it-py==3.0.0
# via
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 627f066b7a7..15df888941e 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -108,7 +108,7 @@ jinja2==3.1.6
# sphinx
# sphinxcontrib-mermaid
# towncrier
-librt==0.12.0
+librt==0.13.0
# via mypy
markdown-it-py==3.0.0
# via
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 5c5022c9d9d..d91198254e5 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -44,7 +44,7 @@ iniconfig==2.3.0
# via pytest
isal==1.8.0
# via -r requirements/lint.in
-librt==0.12.0
+librt==0.13.0
# via mypy
markdown-it-py==4.2.0
# via rich
diff --git a/requirements/test-common.txt b/requirements/test-common.txt
index 0c08c85c327..7fcc689fb43 100644
--- a/requirements/test-common.txt
+++ b/requirements/test-common.txt
@@ -34,7 +34,7 @@ iniconfig==2.3.0
# via pytest
isal==1.8.0 ; python_version < "3.14" and implementation_name == "cpython"
# via -r requirements/test-common.in
-librt==0.12.0
+librt==0.13.0
# via mypy
markdown-it-py==4.2.0
# via rich
diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt
index 8d1bff8f023..e56ae8699b0 100644
--- a/requirements/test-ft.txt
+++ b/requirements/test-ft.txt
@@ -58,7 +58,7 @@ iniconfig==2.3.0
# via pytest
isal==1.8.0 ; python_version < "3.14" and implementation_name == "cpython"
# via -r requirements/test-common.in
-librt==0.12.0
+librt==0.13.0
# via mypy
markdown-it-py==4.2.0
# via rich
diff --git a/requirements/test.txt b/requirements/test.txt
index d4c42f19b95..cd2b1ce76ec 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -58,7 +58,7 @@ iniconfig==2.3.0
# via pytest
isal==1.8.0 ; python_version < "3.14" and implementation_name == "cpython"
# via -r requirements/test-common.in
-librt==0.12.0
+librt==0.13.0
# via mypy
markdown-it-py==4.2.0
# via rich
From b9e0919dd00f0e1d24111fe9fa64b9d9f6656ec6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 9 Jul 2026 11:25:41 +0000
Subject: [PATCH 096/137] Bump python-discovery from 1.4.3 to 1.4.4 (#13116)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [python-discovery](https://github.com/tox-dev/python-discovery)
from 1.4.3 to 1.4.4.
Release notes
Parse the debug build flag in interpreter specs -
python3.13d and Debian's python3.13-dbg /
python3.13-debug now select a Py_DEBUG
interpreter instead of being misread as an ISA named dbg.
Resolving a virtualenv to its base interpreter also checks the
free-threaded and debug ABI flags, so a debug or
free-threaded environment no longer resolves to a release build of the
same version - by :user:gaborbernat.
(:issue:96)
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index b29514ea01a..91415beb7a3 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -213,7 +213,7 @@ pytest-xdist==3.8.0
# via -r requirements/test-common.in
python-dateutil==2.9.0.post0
# via freezegun
-python-discovery==1.4.3
+python-discovery==1.4.4
# via virtualenv
python-on-whales==0.81.0
# via
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 15df888941e..ce02a3087c8 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -208,7 +208,7 @@ pytest-xdist==3.8.0
# via -r requirements/test-common.in
python-dateutil==2.9.0.post0
# via freezegun
-python-discovery==1.4.3
+python-discovery==1.4.4
# via virtualenv
python-on-whales==0.81.0
# via
diff --git a/requirements/lint.txt b/requirements/lint.txt
index d91198254e5..b3291b2af9d 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -91,7 +91,7 @@ pytest-mock==3.15.1
# via -r requirements/lint.in
python-dateutil==2.9.0.post0
# via freezegun
-python-discovery==1.4.3
+python-discovery==1.4.4
# via virtualenv
python-on-whales==0.81.0
# via -r requirements/lint.in
From 0494d61eaaf74f1c65bc2df52c7622e5f54c41fa Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 9 Jul 2026 11:44:25 +0000
Subject: [PATCH 097/137] Bump sphinxcontrib-mermaid from 2.0.2 to 2.0.3
(#13109)
Bumps
[sphinxcontrib-mermaid](https://github.com/mgaitan/sphinxcontrib-mermaid)
from 2.0.2 to 2.0.3.
Changelog
Make typing_extensions.TypeAliasType's
__module__ attribute writable. Backport of CPython PR #149172.
Fix setting of __required_keys__ and
__optional_keys__ when inheriting keys with the same
name.
Add support for AsyncIterator, io.Reader,
io.Writer and os.PathLike protocols as bases
for other protocols.
Fix incorrect behaviour on Python 3.9 and Python 3.10 that meant
that calling isinstance with
typing_extensions.Concatenate[...] or
typing_extensions.Unpack[...] as the first argument could
have a different result in some situations depending on whether or not a
profiling function had been set using sys.setprofile. This
affected both CPython and PyPy implementations. Patch by Brian
Schubert.
Fix __init_subclass__() behavior in the presence of
multiple inheritance involving an @deprecated-decorated
base class. Backport of CPython PR #138210
by Brian Schubert.
Raise TypeError when attempting to subclass
typing_extensions.ParamSpec on Python 3.9. The
typing implementation has always raised an error, and the
typing_extensions implementation has raised an error on
Python 3.10+ since typing_extensions v4.6.0. Patch by Brian
Schubert.
Add the bound, covariant,
contravariant, and infer_variance parameters
to TypeVarTuple.
Officially support the bound, covariant,
contravariant and infer_variance parameters to
ParamSpec. Improve the validation of these parameters at
runtime.
Rename typing_extensions.Sentinel to
typing_extensions.sentinel, following the name that has
been adopted for builtins.sentinel on Python 3.15.
typing_extensions.Sentinel is retained as a soft-deprecated
alias for backwards compatibility.
Add support for pickling sentinels.
Sentinels now preserve their identity when copied or
deep-copied.
Deprecate passing name as a keyword argument or
repr as a positional argument to the sentinel
constructor.
The default repr of a sentinel X =
sentinel("X") is now X rather than
<X>.
Deprecate arbitrary attribute assignments to sentinels.
Deprecate subclassing sentinels.
Add support for Python 3.15.
Avoid a DeprecationWarning when deprecated
is applied to a coroutine function on Python 3.14.0.
4.16.0rc2
Changes since 4.16.0rc1:
Avoid a DeprecationWarning when deprecated
is applied to a coroutine function on Python 3.14.0.
Changes since 4.15.0:
Make typing_extensions.TypeAliasType's
__module__ attribute writable. Backport of CPython PR #149172.
Fix setting of __required_keys__ and
__optional_keys__ when inheriting keys with the same
name.
Add support for AsyncIterator, io.Reader,
io.Writer and os.PathLike protocols as bases
for other protocols.
Fix incorrect behaviour on Python 3.9 and Python 3.10 that meant
that calling isinstance with
typing_extensions.Concatenate[...] or
typing_extensions.Unpack[...] as the first argument could
have a different result in some situations depending on whether or not a
profiling function had been set using sys.setprofile. This
affected both CPython and PyPy implementations. Patch by Brian
Schubert.
Fix __init_subclass__() behavior in the presence of
multiple inheritance involving an @deprecated-decorated
base class. Backport of CPython PR #138210
by Brian Schubert.
Raise TypeError when attempting to subclass
typing_extensions.ParamSpec on Python 3.9. The
typing implementation has always raised an error, and the
typing_extensions implementation has raised an error on
Python 3.10+ since typing_extensions v4.6.0. Patch by Brian
Schubert.
Add the bound, covariant,
contravariant, and infer_variance parameters
to TypeVarTuple.
Officially support the bound, covariant,
contravariant and infer_variance parameters to
ParamSpec. Improve the validation of these parameters at
runtime.
Rename typing_extensions.Sentinel to
typing_extensions.sentinel, following the name that has
been adopted for builtins.sentinel on Python 3.15.
typing_extensions.Sentinel is retained as a soft-deprecated
alias for backwards compatibility.
Add support for pickling sentinels.
Sentinels now preserve their identity when copied or
deep-copied.
Deprecate passing name as a keyword argument or
repr as a positional argument to the sentinel
constructor.
The default repr of a sentinel X =
sentinel("X") is now X rather than
<X>.
Deprecate arbitrary attribute assignments to sentinels.
Deprecate subclassing sentinels.
Add support for Python 3.15.
4.16.0rc1
Make typing_extensions.TypeAliasType's
__module__ attribute writable. Backport of CPython PR #149172.
Fix setting of __required_keys__ and
__optional_keys__ when inheriting keys with the same
name.
Avoid a DeprecationWarning when deprecated
is applied to a coroutine function on
Python 3.14.0.
Release 4.16.0rc1 (June 24, 2026)
Make typing_extensions.TypeAliasType's
__module__ attribute writable.
Backport of CPython PR
#149172.
Fix setting of __required_keys__ and
__optional_keys__ when inheriting
keys with the same name.
Add support for AsyncIterator, io.Reader,
io.Writer and os.PathLike protocols
as bases for other protocols.
Fix incorrect behaviour on Python 3.9 and Python 3.10 that meant
that
calling isinstance with
typing_extensions.Concatenate[...] or
typing_extensions.Unpack[...] as the first argument could
have a different
result in some situations depending on whether or not a profiling
function had been
set using sys.setprofile. This affected both CPython and
PyPy implementations.
Patch by Brian Schubert.
Fix __init_subclass__() behavior in the presence of
multiple inheritance involving
an @deprecated-decorated base class. Backport of CPython PR
#138210
by Brian Schubert.
Raise TypeError when attempting to subclass
typing_extensions.ParamSpec on
Python 3.9. The typing implementation has always raised an
error, and the
typing_extensions implementation has raised an error on
Python 3.10+ since
typing_extensions v4.6.0. Patch by Brian Schubert.
Add the bound, covariant,
contravariant, and infer_variance parameters
to TypeVarTuple.
Officially support the bound, covariant,
contravariant and infer_variance
parameters to ParamSpec. Improve the validation of these
parameters at runtime.
Rename typing_extensions.Sentinel to
typing_extensions.sentinel, following the
name that has been adopted for builtins.sentinel on Python
3.15.
typing_extensions.Sentinel is retained as a soft-deprecated
alias for backwards
compatibility.
Add support for pickling sentinels.
Sentinels now preserve their identity when copied or
deep-copied.
Deprecate passing name as a keyword argument or
repr as a positional argument
to the sentinel constructor.
The default repr of a sentinel X =
sentinel("X") is now X rather than
<X>.
Deprecate arbitrary attribute assignments to sentinels.
3.0.0 is a major release that moves the action runtime
from Node 20 to Node 24.
Use v3 on GitHub-hosted runners and self-hosted fleets that
already support the
Node 24 Actions runtime. v2.6.2 was the final Node
20-compatible release and is
no longer maintained or supported.
What's Changed
Other Changes π
Move the action runtime and bundle target to Node 24
Update @types/node to the Node 24 line and allow future
Dependabot updates
Keep the floating major tag on v3; freeze
v2 at the final v2.6.2 release
2.6.2
2.6.2 is the final v2 release and is no
longer maintained or supported. Upgrade
to v3 for the supported Node 24 runtime and current
fixes.
2.6.1 is a patch release focused on restoring linked
discussion thread creation when
discussion_category_name is set. It fixes
[#764](https://github.com/softprops/action-gh-release/issues/764),
where the draft-first publish flow
stopped carrying the discussion category through the final publish
step.
If you still hit an issue after upgrading, please open a report with
the bug template and include a minimal repro or sanitized workflow
snippet where possible.
2.6.0 is a minor release centered on
previous_tag support for
generate_release_notes,
which lets workflows pin GitHub's comparison base explicitly instead of
relying on the default range.
... (truncated)
Commits
b430933
release: cut v3.0.0 for Node 24 upgrade (#670)
c2e35e0
chore(deps): bump the npm group across 1 directory with 7 updates (#783)
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci-cd.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 7566a16c7a7..a97d0f8ed01 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -800,7 +800,7 @@ jobs:
# Confusingly, this action also supports updating releases, not
# just creating them. This is what we want here, since we've manually
# created the release above.
- uses: softprops/action-gh-release@v3
+ uses: softprops/action-gh-release@v3.0.1
with:
# dist/ contains the built packages, which smoketest-artifacts/
# contains the signatures and certificates.
From 5d8fa372da633573b6ebd2cfe652218ec51e2ab3 Mon Sep 17 00:00:00 2001
From: Sam Bull
Date: Tue, 14 Jul 2026 15:08:52 +0100
Subject: [PATCH 110/137] Fix Python parser not rejecting LF early (#13136)
(#13147)
(cherry picked from commit cba121d5864966d95b48f4a790ff0cefad7f041f)
---
CHANGES/13136.bugfix.rst | 1 +
THREAT_MODEL.md | 11 +++++++++--
aiohttp/http_parser.py | 17 +++++++++++++++++
tests/test_http_parser.py | 37 ++++++++++++++++++++++++++++++++++++-
4 files changed, 63 insertions(+), 3 deletions(-)
create mode 100644 CHANGES/13136.bugfix.rst
diff --git a/CHANGES/13136.bugfix.rst b/CHANGES/13136.bugfix.rst
new file mode 100644
index 00000000000..55fdd4228ca
--- /dev/null
+++ b/CHANGES/13136.bugfix.rst
@@ -0,0 +1 @@
+Fixed Python parser not rejecting a bare ``LF`` in the request line -- by :user:`Dreamsorcerer`.
diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md
index f7b0aed090a..422cb7a2993 100644
--- a/THREAT_MODEL.md
+++ b/THREAT_MODEL.md
@@ -267,8 +267,8 @@ into `StreamReader`) is then handed to `web_protocol.RequestHandler` and
| 1.8 | `Transfer-Encoding` lenience | `_is_chunked_te` requires `chunked` to be the last value; duplicate `chunked` rejected (`#10611`). Request parser strict. | None. |
| 1.9 | Chunk-size DoS | The parser doesn't cap chunk size, but **server-side body length is bounded by `client_max_size` (default `1 MiB`)** in `web_request.py:BaseRequest.read`. Client-side responses are bounded by user-supplied `max_body_size` / streaming reads. | None. If a cap is ever needed at the parser level, plumb it through `HttpPayloadParser`. |
| 1.10 | Chunk-extension DoS | Chunk-extension content is bounded by the same wire-level size constraints (it shares the chunk-size line with `max_line_size`). | **Add an explicit test that chunk-extension flooding cannot blow past `max_line_size`.** |
-| 1.11 | Parser error reflection | `http_parser.py` truncates to `[:100]` for line errors. Server-side error path renders 4xx with the exception message; tracebacks only when `DEBUG=True`. | **Audit any aiohttp path where `BadHttpMessage` content is reflected to the client unsanitised.** **User**: Review custom `web_log` configurations and any middleware that reflects parser exception messages back to the peer. |
-| 1.12 | Cython β pure-Python divergence | `tests/test_http_parser.py` parameterises tests over `REQUEST_PARSERS` / `RESPONSE_PARSERS` (pure-Python always; Cython when the extension imports). The high-leverage attack vectors are already covered under both backends: CL+TE (`test_content_length_transfer_encoding`), CLΓN (`test_duplicate_singleton_header_rejected`), obs-fold (`test_reject_obsolete_line_folding`, `test_http_response_parser_obs_line_folding*`), CR/LF/NUL (`test_bad_headers`, `test_http_response_parser_null_byte_in_header_value`, `test_http_response_parser_bad_crlf`), version regex (`test_http_request_parser_bad_version*`, `test_http_response_parser_bad_version*`). | None. When new attack vectors emerge, add them to the parameterised tests. |
+| 1.11 | Parser error reflection | `http_parser.py` truncates to `[:100]` only for `LineTooLong`; `BadStatusLine` / `InvalidHeader` / `TransferEncodingError` carry the offending line up to `max_line_size` / `max_field_size`. | **Audit any aiohttp path where `BadHttpMessage` content is reflected to the client unsanitised.** **User**: Review custom `web_log` configurations and any middleware that reflects parser exception messages back to the peer. |
+| 1.12 | Cython β pure-Python divergence | `tests/test_http_parser.py` parameterises tests over `REQUEST_PARSERS` / `RESPONSE_PARSERS` (pure-Python always; Cython when the extension imports). The high-leverage attack vectors are already covered under both backends: CL+TE (`test_content_length_transfer_encoding`), CLΓN (`test_duplicate_singleton_header_rejected`), obs-fold (`test_reject_obsolete_line_folding`, `test_http_response_parser_obs_line_folding*`), CR/LF/NUL (`test_bad_headers`, `test_http_response_parser_null_byte_in_header_value`, `test_http_response_parser_bad_crlf`), version regex (`test_http_request_parser_bad_version*`, `test_http_response_parser_bad_version*`), bare-LF line endings (`test_reject_bare_lf_no_cross_request_leak`). | None. When new attack vectors emerge, add them to the parameterised tests. |
| 1.13 | llhttp version drift | Manual upgrade via `make generate-llhttp`; vendor pinned in `vendor/llhttp/package.json`. | Track upstream releases (e.g. via Dependabot rule for `vendor/llhttp/package.json`), bump on every llhttp release, regenerate in CI. |
| 1.14 | npm-side compromise of `llhttp` | The vendored output is checked into git, so a compromise during a future regen would be detectable in PR review. See [Β§5.19](#519-build--release-supply-chain). | **Make the llhttp build reproducible: pin Node.js version, commit the npm lockfile, and on every bump verify the regenerated C against upstream's release tarballs before committing.** |
@@ -324,6 +324,13 @@ into `StreamReader`) is then handed to `web_protocol.RequestHandler` and
could grow without bound. Memory was previously bounded only
transitively by `read_bufsize` ([Β§5.7](#57-server-connection-lifecycle) threat 7.4);
the fix adds an explicit pipeline-count cap.
+- **PR #13136** (3.14.2) β the pure-Python request parser buffered a
+ bare `LF` line ending (where `CRLF` is required) across reads instead of
+ rejecting it, so bytes from a following request on the same connection
+ could be concatenated into the failed parse and disclosed in the error.
+ Now rejected at the point it is seen, matching llhttp (request line,
+ headers, chunk-size, trailers). Not a vulnerability because no proxy would
+ pipeline requests from multiple clients and forward raw LFs to the backend.
These are all currently in place; this section assumes no regression.
diff --git a/aiohttp/http_parser.py b/aiohttp/http_parser.py
index e08b4c11aca..0c9fd086b3b 100644
--- a/aiohttp/http_parser.py
+++ b/aiohttp/http_parser.py
@@ -527,6 +527,11 @@ def get_content_length() -> int | None:
should_close = msg.should_close
else:
self._tail = data[start_pos:]
+ # A bare LF here means CRLF was required:
+ # reject instead of buffering, else a following request's
+ # bytes get appended to this line and leak in the error.
+ if b"\n" in self._tail:
+ raise BadHttpMessage("Bad line ending, expected CRLF")
if len(self._tail) > self.max_line_size:
raise LineTooLong(self._tail[:100] + b"...", self.max_line_size)
data = EMPTY
@@ -1016,6 +1021,12 @@ def feed_data(
self._chunk_size = size
self.payload.begin_http_chunk_receiving()
else:
+ if b"\n" in chunk:
+ exc = TransferEncodingError(
+ "Bad chunk-size line ending, expected CRLF"
+ )
+ set_exception(self.payload, exc)
+ raise exc
self._chunk_tail = chunk
return PayloadState.PAYLOAD_NEEDS_INPUT, b""
@@ -1062,6 +1073,12 @@ def feed_data(
if self._chunk == ChunkState.PARSE_TRAILERS:
pos = chunk.find(SEP)
if pos < 0: # No line found
+ if b"\n" in chunk:
+ exc = TransferEncodingError(
+ "Bad trailer line ending, expected CRLF"
+ )
+ set_exception(self.payload, exc)
+ raise exc
self._chunk_tail = chunk
return PayloadState.PAYLOAD_NEEDS_INPUT, b""
diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py
index 407292079d1..c9e9ca51407 100644
--- a/tests/test_http_parser.py
+++ b/tests/test_http_parser.py
@@ -323,7 +323,42 @@ def test_invalid_linebreak(
parser.feed_data(text)
-def test_cve_2023_37276(parser: Any) -> None:
+def test_partial_request_split_still_parses(parser: HttpRequestParser) -> None:
+ """A legitimate request split mid-line must still stream."""
+ messages, _, _ = parser.feed_data(b"GET /split HTTP/1.1\r\nHo")
+ assert not messages
+ messages, _, _ = parser.feed_data(b"st: a\r\n\r\n")
+ assert len(messages) == 1
+ assert messages[0][0].path == "/split"
+
+
+@pytest.mark.parametrize(
+ "attacker",
+ (
+ pytest.param(
+ b"GET /attacker HTTP/1.1\nHost: a\nX-Foo: bar\n\n", id="request_line"
+ ),
+ pytest.param(
+ b"POST /attacker HTTP/1.1\r\nHost: a\r\n"
+ b"Transfer-Encoding: chunked\r\n\r\n5\n",
+ id="chunk_size",
+ ),
+ pytest.param(
+ b"POST /attacker HTTP/1.1\r\nHost: a\r\n"
+ b"Transfer-Encoding: chunked\r\n\r\n0\r\nX-Trailer: bad\n",
+ id="trailer",
+ ),
+ ),
+)
+def test_reject_bare_lf_at_point_seen(
+ parser: HttpRequestParser, attacker: bytes
+) -> None:
+ """A bare LF where CRLF is required is rejected at the point it's seen."""
+ with pytest.raises(http_exceptions.BadHttpMessage):
+ parser.feed_data(attacker)
+
+
+def test_cve_2023_37276(parser: HttpRequestParser) -> None:
text = b"""POST / HTTP/1.1\r\nHost: localhost:8080\r\nX-Abc: \rxTransfer-Encoding: chunked\r\n\r\n"""
with pytest.raises(http_exceptions.BadHttpMessage):
parser.feed_data(text)
From c620e623db233f245ab9ce6990ed7ac014ac6d13 Mon Sep 17 00:00:00 2001
From: Sam Bull
Date: Tue, 14 Jul 2026 15:09:01 +0100
Subject: [PATCH 111/137] Fix Python parser not rejecting LF early (#13136)
(#13148)
(cherry picked from commit cba121d5864966d95b48f4a790ff0cefad7f041f)
---
CHANGES/13136.bugfix.rst | 1 +
aiohttp/http_parser.py | 17 +++++++++++++++++
tests/test_http_parser.py | 37 ++++++++++++++++++++++++++++++++++++-
3 files changed, 54 insertions(+), 1 deletion(-)
create mode 100644 CHANGES/13136.bugfix.rst
diff --git a/CHANGES/13136.bugfix.rst b/CHANGES/13136.bugfix.rst
new file mode 100644
index 00000000000..55fdd4228ca
--- /dev/null
+++ b/CHANGES/13136.bugfix.rst
@@ -0,0 +1 @@
+Fixed Python parser not rejecting a bare ``LF`` in the request line -- by :user:`Dreamsorcerer`.
diff --git a/aiohttp/http_parser.py b/aiohttp/http_parser.py
index e08b4c11aca..0c9fd086b3b 100644
--- a/aiohttp/http_parser.py
+++ b/aiohttp/http_parser.py
@@ -527,6 +527,11 @@ def get_content_length() -> int | None:
should_close = msg.should_close
else:
self._tail = data[start_pos:]
+ # A bare LF here means CRLF was required:
+ # reject instead of buffering, else a following request's
+ # bytes get appended to this line and leak in the error.
+ if b"\n" in self._tail:
+ raise BadHttpMessage("Bad line ending, expected CRLF")
if len(self._tail) > self.max_line_size:
raise LineTooLong(self._tail[:100] + b"...", self.max_line_size)
data = EMPTY
@@ -1016,6 +1021,12 @@ def feed_data(
self._chunk_size = size
self.payload.begin_http_chunk_receiving()
else:
+ if b"\n" in chunk:
+ exc = TransferEncodingError(
+ "Bad chunk-size line ending, expected CRLF"
+ )
+ set_exception(self.payload, exc)
+ raise exc
self._chunk_tail = chunk
return PayloadState.PAYLOAD_NEEDS_INPUT, b""
@@ -1062,6 +1073,12 @@ def feed_data(
if self._chunk == ChunkState.PARSE_TRAILERS:
pos = chunk.find(SEP)
if pos < 0: # No line found
+ if b"\n" in chunk:
+ exc = TransferEncodingError(
+ "Bad trailer line ending, expected CRLF"
+ )
+ set_exception(self.payload, exc)
+ raise exc
self._chunk_tail = chunk
return PayloadState.PAYLOAD_NEEDS_INPUT, b""
diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py
index b3e0ab67cff..f1ef31f1df6 100644
--- a/tests/test_http_parser.py
+++ b/tests/test_http_parser.py
@@ -323,7 +323,42 @@ def test_invalid_linebreak(
parser.feed_data(text)
-def test_cve_2023_37276(parser: Any) -> None:
+def test_partial_request_split_still_parses(parser: HttpRequestParser) -> None:
+ """A legitimate request split mid-line must still stream."""
+ messages, _, _ = parser.feed_data(b"GET /split HTTP/1.1\r\nHo")
+ assert not messages
+ messages, _, _ = parser.feed_data(b"st: a\r\n\r\n")
+ assert len(messages) == 1
+ assert messages[0][0].path == "/split"
+
+
+@pytest.mark.parametrize(
+ "attacker",
+ (
+ pytest.param(
+ b"GET /attacker HTTP/1.1\nHost: a\nX-Foo: bar\n\n", id="request_line"
+ ),
+ pytest.param(
+ b"POST /attacker HTTP/1.1\r\nHost: a\r\n"
+ b"Transfer-Encoding: chunked\r\n\r\n5\n",
+ id="chunk_size",
+ ),
+ pytest.param(
+ b"POST /attacker HTTP/1.1\r\nHost: a\r\n"
+ b"Transfer-Encoding: chunked\r\n\r\n0\r\nX-Trailer: bad\n",
+ id="trailer",
+ ),
+ ),
+)
+def test_reject_bare_lf_at_point_seen(
+ parser: HttpRequestParser, attacker: bytes
+) -> None:
+ """A bare LF where CRLF is required is rejected at the point it's seen."""
+ with pytest.raises(http_exceptions.BadHttpMessage):
+ parser.feed_data(attacker)
+
+
+def test_cve_2023_37276(parser: HttpRequestParser) -> None:
text = b"""POST / HTTP/1.1\r\nHost: localhost:8080\r\nX-Abc: \rxTransfer-Encoding: chunked\r\n\r\n"""
with pytest.raises(http_exceptions.BadHttpMessage):
parser.feed_data(text)
From e79e0da4c74a55cc3c6abc234b22bd8e5145ee2b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 16 Jul 2026 11:13:26 +0000
Subject: [PATCH 112/137] Bump actions/setup-node from 6 to 7 (#13156)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6
to 7.
Release notes
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci-cd.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index a97d0f8ed01..7d64de5c670 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -124,7 +124,7 @@ jobs:
path: vendor/llhttp/build
- name: Setup NodeJS
if: steps.cache.outputs.cache-hit != 'true'
- uses: actions/setup-node@v6
+ uses: actions/setup-node@v7
with:
node-version: 18
- name: Generate llhttp sources
From 8069270a4cf9e739589e4b9a4196409f1251e1f6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 16 Jul 2026 11:25:07 +0000
Subject: [PATCH 113/137] Bump coverage from 7.15.0 to 7.15.1 (#13158)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.15.0
to 7.15.1.
Release notes
Fix: in the HTML report with show_contexts enabled, a
context label containing </script> (for example a
parametrized pytest node id) could close the inline
<script> element in a file page early, injecting
markup. Context labels are now fully escaped. Thanks, Rajath
Mohare.
A number of performance improvements thanks to Paul Kehrer, in pull
requests 2213,
2214,
2215,
2216,
2218,
2220,
and 2221.
Fix: in the HTML report with show_contexts enabled, a
context label
containing </script> (for example a parametrized
pytest node id) could
close the inline <script> element in a file page
early, injecting markup.
Context labels are now fully escaped. Thanks, Rajath Mohare
<pull 2224_>_.
A number of performance improvements thanks to Paul Kehrer, in pull
requests
2213 <pull 2213_>, 2214 <pull
2214_>, 2215 <pull 2215_>,
2216 <pull 2216_>, 2218 <pull
2218_>, 2220 <pull 2220_>, and
2221 <pull 2221_>_.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/test-common-base.txt | 2 +-
requirements/test-common.txt | 2 +-
requirements/test-ft.txt | 2 +-
requirements/test-mobile.txt | 2 +-
requirements/test.txt | 2 +-
7 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 09f245d8abe..cae1f051326 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -59,7 +59,7 @@ click==8.4.2
# pip-tools
# towncrier
# wait-for-it
-coverage==7.15.0
+coverage==7.15.1
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 8d1f05af83a..cec89340be6 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -59,7 +59,7 @@ click==8.4.2
# pip-tools
# towncrier
# wait-for-it
-coverage==7.15.0
+coverage==7.15.1
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/test-common-base.txt b/requirements/test-common-base.txt
index ba6ce4e1573..d2e66067bbe 100644
--- a/requirements/test-common-base.txt
+++ b/requirements/test-common-base.txt
@@ -6,7 +6,7 @@
#
click==8.4.2
# via wait-for-it
-coverage==7.15.0
+coverage==7.15.1
# via pytest-cov
exceptiongroup==1.3.1
# via pytest
diff --git a/requirements/test-common.txt b/requirements/test-common.txt
index 2d75098ef7e..169b0a411c6 100644
--- a/requirements/test-common.txt
+++ b/requirements/test-common.txt
@@ -14,7 +14,7 @@ cffi==2.1.0
# via cryptography
click==8.4.2
# via wait-for-it
-coverage==7.15.0
+coverage==7.15.1
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt
index ad3b32059b7..92208b78ccb 100644
--- a/requirements/test-ft.txt
+++ b/requirements/test-ft.txt
@@ -32,7 +32,7 @@ cffi==2.1.0
# pycares
click==8.4.2
# via wait-for-it
-coverage==7.15.0
+coverage==7.15.1
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/test-mobile.txt b/requirements/test-mobile.txt
index f8b30954946..c04b3f9398a 100644
--- a/requirements/test-mobile.txt
+++ b/requirements/test-mobile.txt
@@ -28,7 +28,7 @@ cffi==2.1.0 ; sys_platform != "android" and sys_platform != "ios"
# pycares
click==8.4.2
# via wait-for-it
-coverage==7.15.0
+coverage==7.15.1
# via pytest-cov
exceptiongroup==1.3.1
# via
diff --git a/requirements/test.txt b/requirements/test.txt
index e2026736651..869ecc64290 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -32,7 +32,7 @@ cffi==2.1.0
# pycares
click==8.4.2
# via wait-for-it
-coverage==7.15.0
+coverage==7.15.1
# via
# -r requirements/test-common.in
# pytest-cov
From c727b9c4e1bc4e039a1169060dc44c841f7f8913 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 17 Jul 2026 11:05:10 +0000
Subject: [PATCH 114/137] Bump github/codeql-action from 4 to 4.37.0 (#13163)
Bumps [github/codeql-action](https://github.com/github/codeql-action)
from 4 to 4.37.0.
Release notes
Update default CodeQL bundle version to 2.26.0.
#3995
In addition to the existing input format, the
config-file input for the codeql-action/init
step will soon support a new [owner/]repo[@ref][:path]
format. All components except the repository name are optional. If
omitted, owner defaults to the same owner as the repository
the analysis is running for, ref to main, and
path to .github/codeql-action.yaml. Support
for this format ships in this version of the CodeQL Action, but will
only be enabled over the coming weeks. #3973
v4.36.3
No user facing changes.
v4.36.2
Cache CodeQL CLI version information across Actions steps. #3943
Reduce requests while waiting for analysis processing by using
exponential backoff when polling SARIF processing status. #3937
Update default CodeQL bundle version to 2.25.6.
#3948
v4.36.1
No user facing changes.
v4.36.0
Breaking change: Bump the minimum required CodeQL bundle
version to 2.19.4. #3894
Update default CodeQL bundle version to 2.25.5.
#3926
v4.35.5
We have improved how the JavaScript bundles for the CodeQL Action
are generated to avoid duplication across bundles and reduce the size of
the repository by around 70%. This should have no effect on the runtime
behaviour of the CodeQL Action. #3899
For performance and accuracy reasons, improved
incremental analysis will now only be enabled on a pull request when
diff-informed analysis is also enabled for that run. If diff-informed
analysis is unavailable (for example, because the PR diff ranges could
not be computed), the action will fall back to a full analysis. #3791
If multiple inputs are provided for the GitHub-internal
analysis-kinds input, only code-scanning will
be enabled. The analysis-kinds input is experimental, for
GitHub-internal use only, and may change without notice at any time. #3892
Added an experimental change which, when running a Code Scanning
analysis for a PR with improved
incremental analysis enabled, prefers CodeQL CLI versions that have
a cached overlay-base database for the configured languages. This speeds
up analysis for a repository when there is not yet a cached overlay-base
database for the latest CLI version. We expect to roll this change out
to everyone in May. #3880
v4.35.4
Update default CodeQL bundle version to 2.25.4.
#3881
v4.35.3
Upcoming breaking change: Add a deprecation warning for
customers using CodeQL version 2.19.3 and earlier. These versions of
CodeQL were discontinued on 9 April 2026 alongside GitHub Enterprise
Server 3.15, and will be unsupported by the next minor release of the
CodeQL Action. #3837
Configurations for private registries that use Cloudsmith or GCP
OIDC are now accepted. #3850
Best-effort connection tests for private registries now use
GET requests instead of HEAD for better
compatibility with various registry implementations. For NuGet feeds,
the test is now always performed against the service index. #3853
Fixed a bug where two diagnostics produced within the same
millisecond could overwrite each other on disk, causing one of them to
be lost. #3852
Update default CodeQL bundle version to 2.25.3.
#3865
v4.35.2
The undocumented TRAP cache cleanup feature that could be enabled
using the CODEQL_ACTION_CLEANUP_TRAP_CACHES environment
variable is deprecated and will be removed in May 2026. If you are
affected by this, we recommend disabling TRAP caching by passing the
trap-caching: false input to the init Action.
#3795
The Git version 2.36.0 requirement for improved incremental analysis
now only applies to repositories that contain submodules. #3789
Python analysis on GHES no longer extracts the standard library,
relying instead on models of the standard library. This should result in
significantly faster extraction and analysis times, while the effect on
alerts should be minimal. #3794
Fixed a bug in the validation of OIDC configurations for private
registries that was added in CodeQL Action 4.33.0 / 3.33.0. #3807
Update default CodeQL bundle version to 2.25.2.
#3823
Update default CodeQL bundle version to 2.26.0.
#3995
In addition to the existing input format, the
config-file input for the codeql-action/init
step will soon support a new [owner/]repo[@ref][:path]
format. All components except the repository name are optional. If
omitted, owner defaults to the same owner as the repository
the analysis is running for, ref to main, and
path to .github/codeql-action.yaml. Support
for this format ships in this version of the CodeQL Action, but will
only be enabled over the coming weeks. #3973
4.36.3 - 01 Jul 2026
No user facing changes.
Commits
99df26d
Merge pull request #3996
from github/update-v4.37.0-c7c896d71
Harden the fish activator prompt against user functions that shadow
builtins. Routing functions, printf,
string, echo and
source/. through builtin stops a
shadowing function (such as a dot-style
directory navigator that redefines .) from hijacking the
prompt and dropping the previous command's exit status,
matching the CPython fix in
[gh-140006](https://github.com/pypa/virtualenv/issues/140006)
<https://github.com/python/cpython/issues/140006>_.
(:issue:3185)
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 6 +-----
3 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index cae1f051326..31c29e2b1f9 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -312,7 +312,7 @@ uvloop==0.22.1 ; platform_system != "Windows"
# -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.6.0
+virtualenv==21.6.1
# via pre-commit
wait-for-it==2.3.0
# via -r requirements/test-common-base.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index cec89340be6..d1c77329add 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -302,7 +302,7 @@ uvloop==0.22.1 ; platform_system != "Windows" and implementation_name == "cpytho
# -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.6.0
+virtualenv==21.6.1
# via pre-commit
wait-for-it==2.3.0
# via -r requirements/test-common-base.in
diff --git a/requirements/lint.txt b/requirements/lint.txt
index a0cf5a5ff70..72aefded7ca 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -8,10 +8,6 @@ aiodns==4.0.4
# via -r requirements/lint.in
aiofastnet==0.19.0
# via -r requirements/lint.in
-aiohappyeyeballs==2.7.1
- # via aiohttp
-aiosignal==1.4.0
- # via aiohttp
annotated-types==0.7.0
# via pydantic
ast-serialize==0.6.0
@@ -134,7 +130,7 @@ uvloop==0.22.1 ; platform_system != "Windows"
# via -r requirements/lint.in
valkey==6.1.1
# via -r requirements/lint.in
-virtualenv==21.6.0
+virtualenv==21.6.1
# via pre-commit
zlib-ng==1.0.0
# via -r requirements/lint.in
From 4aaef755ba3deab4b87e6aca8bf6fd01fb33b075 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 18 Jul 2026 15:02:28 +0100
Subject: [PATCH 118/137] Bump softprops/action-gh-release from 3.0.1 to 3.0.2
(#13157)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps
[softprops/action-gh-release](https://github.com/softprops/action-gh-release)
from 3.0.1 to 3.0.2.
Release notes
3.0.2 is a patch release focused on release reliability
and compatibility. It
reuses existing draft releases when publishing prereleases, supports
replacing
release assets on Gitea, hardens streamed asset uploads, and provides
clearer
release-creation diagnostics. It also includes TypeScript, coverage, and
tooling
maintenance merged since 3.0.1.
This release fixes #795,
#438,
and #803.
The upload transport hardening covers the
historical failure reported in #790,
although current hosted Node 24 runners did
not reproduce it naturally. The diagnostics work is related to #786
and does not
claim a reproducible release-creation fix.
3.0.2 is a patch release focused on release reliability
and compatibility. It
reuses existing draft releases when publishing prereleases, supports
replacing
release assets on Gitea, hardens streamed asset uploads, and provides
clearer
release-creation diagnostics. It also includes TypeScript, coverage, and
tooling
maintenance merged since 3.0.1.
This release fixes #795,
#438,
and #803.
The upload transport hardening covers the
historical failure reported in #790,
although current hosted Node 24 runners did
not reproduce it naturally. The diagnostics work is related to #786
and does not
claim a reproducible release-creation fix.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.github/workflows/ci-cd.yml | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index dcdd2fccd73..411d60c2931 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -67,7 +67,7 @@ jobs:
make sync-direct-runtime-deps
git diff --exit-code -- requirements/runtime-deps.in
- name: Setup Python
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v6.3.0
with:
python-version: 3.11
- name: Cache PyPI
@@ -430,7 +430,7 @@ jobs:
submodules: true
- name: Setup Python 3.13.2
id: python-install
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v6.3.0
with:
python-version: 3.13.2
cache: pip
@@ -482,7 +482,7 @@ jobs:
submodules: true
- name: Setup Python
id: python-install
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v6.3.0
with:
python-version: '3.12'
- name: Install dependencies
@@ -568,7 +568,7 @@ jobs:
with:
submodules: true
- name: Setup Python
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v6.3.0
- name: Install build tooling and cython
run: >-
python -m
@@ -666,7 +666,7 @@ jobs:
fi
shell: bash
- name: Setup Python
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v6.3.0
with:
python-version: 3.x
- name: Install build tooling and cython
From 157cdb24937b6bfa5b8648633c0a49ed2181b81a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 20 Jul 2026 11:45:05 +0000
Subject: [PATCH 123/137] Bump filelock from 3.29.7 to 3.30.2 (#13186)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.29.7 to
3.30.2.
Release notes
A SoftFileLease acquired on one thread keeps its claim
when another thread fails to acquire the same lease object,
so its heartbeat carries on refreshing the marker instead of being torn
down and letting a peer take the live claim. :pr:680
3.31.0 (2026-07-18)
Support Termux/Android, whose CPython ships without
os.link and reports sys.platform ==
"android". import filelock and both
FileLock and SoftFileLock now work there,
StrictSoftFileLock reports its missing
hard-link support only when acquired, and process liveness reads
/proc on Android instead of PID-only checks.
:pr:678
StrictSoftFileLock no longer lets two processes hold
the lock at once under heavy contention. A holder now keeps
its intent claim for the whole hold, so a contender whose directory scan
races the holder's freshly linked claim can no
longer miss it and win alongside it. A held lock now exposes both an
intent and a held claim.
:pr:678
3.30.3 (2026-07-17)
AsyncFileLock and AsyncSoftFileLock no
longer raise a deadlock RuntimeError when a different
asyncio task
waits for a lock they hold; the check now scopes holders to the owning
task, so only a same-task reacquire is refused.
:pr:676
Keep both tables of contents on screen at any browser font size. The
widened body column sized itself in em against the reader's
font size while the breakpoints that fold the layout resolve
em against a fixed 16px, so a reader whose default font is
larger than 16px had the right-hand table of contents clipped off the
edge. The body column now flexes instead, and the right-hand table of
contents hides only at the mobile breakpoint. :pr:673
Color the mermaid diagrams from whichever theme is active. They
previously carried a light palette baked into each
diagram's source, which stayed light on a dark page.
:pr:674
3.30.2 (2026-07-16)
Stop :class:~filelock.SoftFileLease deleting a live
marker whose mode it does not implement. An unrecognized
mode now names its owner instead of reading as malformed, so a record
written by a newer filelock survives the grace window rather than being
evicted after two seconds. :pr:672
Stop :class:~filelock.StrictSoftFileLock and
:class:~filelock.AsyncStrictSoftFileLock calling themselves
a native OS lock when they warn that they ignore lifetime;
they now say a strict claim is only ever cleared by
force_break(). :pr:672
Cover every lock type in the tutorials and how-to guides, with
examples drawn from projects that use filelock, and color the mermaid
diagrams.
Correct the claims that StrictSoftFileLock exposes
owner, that :class:~filelock.SoftFileLock
evicts a strict sentinel, that
:class:~filelock.ReadWriteLock requires a .db
extension, and that every log record is DEBUG.
:pr:672
3.30.1 (2026-07-16)
StrictSoftFileLock and
AsyncStrictSoftFileLock no longer abort acquisition when a
peer's claim vanishes as an
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/lint.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 31c29e2b1f9..33860a9066d 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -79,7 +79,7 @@ exceptiongroup==1.3.1
# pytest
execnet==2.1.2
# via pytest-xdist
-filelock==3.29.7
+filelock==3.30.2
# via
# python-discovery
# virtualenv
diff --git a/requirements/dev.txt b/requirements/dev.txt
index d1c77329add..f2507eed0af 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -77,7 +77,7 @@ exceptiongroup==1.3.1
# pytest
execnet==2.1.2
# via pytest-xdist
-filelock==3.29.7
+filelock==3.30.2
# via
# python-discovery
# virtualenv
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 72aefded7ca..da04bfc171e 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -32,7 +32,7 @@ exceptiongroup==1.3.1
# via
# aiofastnet
# pytest
-filelock==3.29.7
+filelock==3.30.2
# via
# python-discovery
# virtualenv
From 8d47297bf7df392d6a35117c6349bd727140adf2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 20 Jul 2026 11:50:41 +0000
Subject: [PATCH 124/137] Bump coverage from 7.15.1 to 7.15.2 (#13188)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.15.1
to 7.15.2.
Release notes
Fix: one of the performance improvements in 7.15.1 (pull 2215)
dramatically increased memory use during reporting for large projects.
Now we use a different approach that is both faster and slimmer than
7.15.0. Fixes issue
2229.
Fix: one of the performance improvements in 7.15.1 (pull 2215)
dramatically
increased memory use during reporting for large projects. Now we use a
different approach that is both faster and slimmer than 7.15.0. Fixes
issue 2229_.
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements/constraints.txt | 2 +-
requirements/dev.txt | 2 +-
requirements/test-common-base.txt | 2 +-
requirements/test-common.txt | 2 +-
requirements/test-ft.txt | 2 +-
requirements/test-mobile.txt | 2 +-
requirements/test.txt | 2 +-
7 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 33860a9066d..89cc73b184f 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -59,7 +59,7 @@ click==8.4.2
# pip-tools
# towncrier
# wait-for-it
-coverage==7.15.1
+coverage==7.15.2
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/dev.txt b/requirements/dev.txt
index f2507eed0af..60e7d4435be 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -59,7 +59,7 @@ click==8.4.2
# pip-tools
# towncrier
# wait-for-it
-coverage==7.15.1
+coverage==7.15.2
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/test-common-base.txt b/requirements/test-common-base.txt
index d2e66067bbe..eb0c089765c 100644
--- a/requirements/test-common-base.txt
+++ b/requirements/test-common-base.txt
@@ -6,7 +6,7 @@
#
click==8.4.2
# via wait-for-it
-coverage==7.15.1
+coverage==7.15.2
# via pytest-cov
exceptiongroup==1.3.1
# via pytest
diff --git a/requirements/test-common.txt b/requirements/test-common.txt
index 169b0a411c6..cd729ff5953 100644
--- a/requirements/test-common.txt
+++ b/requirements/test-common.txt
@@ -14,7 +14,7 @@ cffi==2.1.0
# via cryptography
click==8.4.2
# via wait-for-it
-coverage==7.15.1
+coverage==7.15.2
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt
index 92208b78ccb..99b1ab02696 100644
--- a/requirements/test-ft.txt
+++ b/requirements/test-ft.txt
@@ -32,7 +32,7 @@ cffi==2.1.0
# pycares
click==8.4.2
# via wait-for-it
-coverage==7.15.1
+coverage==7.15.2
# via
# -r requirements/test-common.in
# pytest-cov
diff --git a/requirements/test-mobile.txt b/requirements/test-mobile.txt
index c04b3f9398a..13c580c7152 100644
--- a/requirements/test-mobile.txt
+++ b/requirements/test-mobile.txt
@@ -28,7 +28,7 @@ cffi==2.1.0 ; sys_platform != "android" and sys_platform != "ios"
# pycares
click==8.4.2
# via wait-for-it
-coverage==7.15.1
+coverage==7.15.2
# via pytest-cov
exceptiongroup==1.3.1
# via
diff --git a/requirements/test.txt b/requirements/test.txt
index 869ecc64290..8203da6e9fa 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -32,7 +32,7 @@ cffi==2.1.0
# pycares
click==8.4.2
# via wait-for-it
-coverage==7.15.1
+coverage==7.15.2
# via
# -r requirements/test-common.in
# pytest-cov
From 795e0448f40878e0fd380d176557cabbecc7a367 Mon Sep 17 00:00:00 2001
From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com>
Date: Mon, 20 Jul 2026 13:31:25 +0100
Subject: [PATCH 125/137] [PR #13172/a57747ed backport][3.15] Fix C parser
folding fragment into query_string for empty-query origin-form target
(#13191)
**This is a backport of PR #13172 as merged into master
(a57747ed361c7d16b72053f168808d6e62b7929a).**
Co-authored-by: giulio d'erme
---
CHANGES/13171.bugfix.rst | 3 +++
aiohttp/_http_parser.pyx | 2 +-
tests/test_http_parser.py | 13 +++++++++++++
3 files changed, 17 insertions(+), 1 deletion(-)
create mode 100644 CHANGES/13171.bugfix.rst
diff --git a/CHANGES/13171.bugfix.rst b/CHANGES/13171.bugfix.rst
new file mode 100644
index 00000000000..fead0685a8e
--- /dev/null
+++ b/CHANGES/13171.bugfix.rst
@@ -0,0 +1,3 @@
+Fixed the C HTTP parser folding the fragment into the query string for an
+origin-form request target with an empty query (e.g. ``/path?#frag``),
+which diverged from the pure-Python parser -- by :user:`GiulioDER`.
diff --git a/aiohttp/_http_parser.pyx b/aiohttp/_http_parser.pyx
index 01ade16338e..2dcc0f1a2a5 100644
--- a/aiohttp/_http_parser.pyx
+++ b/aiohttp/_http_parser.pyx
@@ -734,7 +734,7 @@ cdef class HttpRequestParser(HttpParser):
else:
path = self._path[0:idx1]
idx1 += 1
- idx2 = self._path.find("#", idx1+1)
+ idx2 = self._path.find("#", idx1)
if idx2 == -1:
query = self._path[idx1:]
fragment = ""
diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py
index c9e9ca51407..89d531eb286 100644
--- a/tests/test_http_parser.py
+++ b/tests/test_http_parser.py
@@ -2439,6 +2439,19 @@ def test_parse_uri_utf8_percent_encoded(parser) -> None:
assert msg.url.fragment == "ΡΡΠ°Π³"
+def test_parse_uri_empty_query_with_fragment(parser: HttpRequestParser) -> None:
+ # Origin-form target with an empty query but a fragment: the ``#`` sits
+ # immediately after ``?``. Regression for the C parser folding ``#frag``
+ # into the query string instead of the fragment.
+ text = b"GET /path?#frag HTTP/1.1\r\nHost: a\r\n\r\n"
+ messages, upgrade, tail = parser.feed_data(text)
+ msg = messages[0][0]
+
+ assert msg.url.path == "/path"
+ assert msg.url.query == {}
+ assert msg.url.fragment == "frag"
+
+
@pytest.mark.skipif(
"HttpRequestParserC" not in dir(aiohttp.http_parser),
reason="C based HTTP parser not available",
From 9ffa1d9fd39cdee72344e1e27afff3581df20b4b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 20 Jul 2026 12:45:00 +0000
Subject: [PATCH 126/137] Bump github/codeql-action from 4.37.0 to 4.37.1
(#13184)
Bumps [github/codeql-action](https://github.com/github/codeql-action)
from 4.37.0 to 4.37.1.
Release notes
Upcoming breaking change: Add a deprecation warning for
customers using CodeQL version 2.20.6 and earlier. These versions of
CodeQL were discontinued on 1 July 2026 alongside GitHub Enterprise
Server 3.16, and will be unsupported by the next minor release of the
CodeQL Action. #3956
Update default CodeQL bundle version to 2.26.1.
#4019
Upcoming breaking change: Add a deprecation warning for
customers using CodeQL version 2.20.6 and earlier. These versions of
CodeQL were discontinued on 1 July 2026 alongside GitHub Enterprise
Server 3.16, and will be unsupported by the next minor release of the
CodeQL Action. #3956
Update default CodeQL bundle version to 2.26.1.
#4019
Commits
7188fc3
Merge pull request #4020
from github/update-v4.37.1-9e7c07009