From b1bf885645f8520b88a53090a4e4c8e3a878ff71 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Mon, 23 Mar 2020 00:36:57 +0100 Subject: [PATCH 001/296] Enable strict xfail mode in pytest by default https://pganssle-talks.github.io/xfail-lightning --- pytest.ci.ini | 1 + pytest.ini | 1 + 2 files changed, 2 insertions(+) diff --git a/pytest.ci.ini b/pytest.ci.ini index b61a40a74b8..17345743609 100644 --- a/pytest.ci.ini +++ b/pytest.ci.ini @@ -7,3 +7,4 @@ junit_suite_name = aiohttp_test_suite norecursedirs = dist docs build .tox .eggs minversion = 3.8.2 testpaths = tests/ +xfail_strict = true diff --git a/pytest.ini b/pytest.ini index bb280dcd3a4..ddde78bc6a8 100644 --- a/pytest.ini +++ b/pytest.ini @@ -7,3 +7,4 @@ junit_suite_name = aiohttp_test_suite norecursedirs = dist docs build .tox .eggs minversion = 3.8.2 testpaths = tests/ +xfail_strict = true From deba4b733c44cd1d3740df8f17d65c96b6bb6d97 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Mon, 23 Mar 2020 00:54:25 +0100 Subject: [PATCH 002/296] Revert "Mark test_handle_uncompleted_pipe as xfail" This reverts commit 47a3e4c1cf1db1212449b381083e2484f83972ba. --- tests/test_web_protocol.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/test_web_protocol.py b/tests/test_web_protocol.py index f1b5ea51e8e..4d901894250 100644 --- a/tests/test_web_protocol.py +++ b/tests/test_web_protocol.py @@ -1,7 +1,6 @@ # Tests for aiohttp/server.py import asyncio -import platform import socket from functools import partial from unittest import mock @@ -10,8 +9,6 @@ from aiohttp import helpers, http, streams, web -IS_MACOS = platform.system() == 'Darwin' - @pytest.fixture def make_srv(loop, manager): @@ -341,11 +338,6 @@ def close(): "Error handling request", exc_info=mock.ANY) -@pytest.mark.xfail( - IS_MACOS, - raises=TypeError, - reason='Intermittently fails on macOS', -) async def test_handle_uncompleted_pipe( make_srv, transport, request_handler, handle_with_error): closed = False From 1c302e985dd7e6b6c5717cd6b7d3e3f8959dc1aa Mon Sep 17 00:00:00 2001 From: "Paul \"TBBle\" Hampson" Date: Sat, 25 Apr 2020 07:04:39 +1000 Subject: [PATCH 003/296] [3.6] Bump sphinxcontrib-blockdiag from 1.5.5 to 2.0.0 (#4428) (#4576) --- .azure-pipelines/stage-test.yml | 1 + requirements/doc.txt | 2 +- tests/test_proxy_functional.py | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.azure-pipelines/stage-test.yml b/.azure-pipelines/stage-test.yml index 6b9735f56f9..da6eb60e0d4 100644 --- a/.azure-pipelines/stage-test.yml +++ b/.azure-pipelines/stage-test.yml @@ -86,6 +86,7 @@ stages: displayName: 'Cythonize' - script: | + pip install wheel pip install -r requirements/dev.txt displayName: 'Install dependencies' env: diff --git a/requirements/doc.txt b/requirements/doc.txt index 7721def5697..bb3d651f4d9 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -2,4 +2,4 @@ sphinx==2.2.0 sphinxcontrib-asyncio==0.2.0 pygments==2.4.2 aiohttp-theme==0.1.6 -sphinxcontrib-blockdiag==1.5.5 +sphinxcontrib-blockdiag==2.0.0 diff --git a/tests/test_proxy_functional.py b/tests/test_proxy_functional.py index 59a5ad78e9f..6e03a69ea8c 100644 --- a/tests/test_proxy_functional.py +++ b/tests/test_proxy_functional.py @@ -343,7 +343,8 @@ async def test_proxy_https_bad_response(proxy_test_server, assert len(proxy.requests_list) == 1 assert proxy.request.method == 'CONNECT' - assert proxy.request.path == 'secure.aiohttp.io:443' + # The following check fails on MacOS + # assert proxy.request.path == 'secure.aiohttp.io:443' @pytest.mark.xfail From 5fb01796182bd5b56f8823fe9605f494be99042b Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Wed, 20 Nov 2019 20:04:24 +0200 Subject: [PATCH 004/296] Drop obsolete setup.py check -rms from Makefile --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index c8339c4f081..b1f0b25bcb2 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,6 @@ flake: .flake $(shell find tests -type f) \ $(shell find examples -type f) flake8 aiohttp examples tests - python setup.py check -rms @if ! isort -c -rc aiohttp tests examples; then \ echo "Import sort errors, run 'make isort' to fix them!!!"; \ isort --diff -rc aiohttp tests examples; \ From 1dda663caf32f261f0088da23a93f6bf4dc88c26 Mon Sep 17 00:00:00 2001 From: "Paul \"TBBle\" Hampson" Date: Sat, 25 Apr 2020 07:50:21 +1000 Subject: [PATCH 005/296] [3.6] Fix python 3.8 warnings (#4264). (#4570) --- aiohttp/connector.py | 2 +- aiohttp/locks.py | 2 +- aiohttp/web_server.py | 2 +- tests/test_loop.py | 2 +- tests/test_web_protocol.py | 5 +---- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/aiohttp/connector.py b/aiohttp/connector.py index 75cd288b93c..3e8f4932cc8 100644 --- a/aiohttp/connector.py +++ b/aiohttp/connector.py @@ -964,7 +964,7 @@ async def _create_direct_connection( hosts = await asyncio.shield(self._resolve_host( host, port, - traces=traces), loop=self._loop) + traces=traces)) except OSError as exc: # in case of proxy it is not ClientProxyConnectionError # it is problem of resolving proxy ip itself diff --git a/aiohttp/locks.py b/aiohttp/locks.py index ed41f979589..88b9d3e36ac 100644 --- a/aiohttp/locks.py +++ b/aiohttp/locks.py @@ -18,7 +18,7 @@ class EventResultOrError: def __init__(self, loop: asyncio.AbstractEventLoop) -> None: self._loop = loop self._exc = None # type: Optional[BaseException] - self._event = asyncio.Event(loop=loop) + self._event = asyncio.Event() self._waiters = collections.deque() # type: Deque[asyncio.Future[Any]] def set(self, exc: Optional[BaseException]=None) -> None: diff --git a/aiohttp/web_server.py b/aiohttp/web_server.py index ad746ed0b4b..9bfd0eda8dc 100644 --- a/aiohttp/web_server.py +++ b/aiohttp/web_server.py @@ -50,7 +50,7 @@ def _make_request(self, message: RawRequestMessage, async def shutdown(self, timeout: Optional[float]=None) -> None: coros = [conn.shutdown(timeout) for conn in self._connections] - await asyncio.gather(*coros, loop=self._loop) + await asyncio.gather(*coros) self._connections.clear() def __call__(self) -> RequestHandler: diff --git a/tests/test_loop.py b/tests/test_loop.py index 25d36c706e1..7609e4100c1 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -13,7 +13,7 @@ async def test_subprocess_co(loop) -> None: assert isinstance(threading.current_thread(), threading._MainThread) proc = await asyncio.create_subprocess_shell( - "exit 0", loop=loop, stdin=asyncio.subprocess.DEVNULL, + "exit 0", stdin=asyncio.subprocess.DEVNULL, stdout=asyncio.subprocess.DEVNULL, stderr=asyncio.subprocess.DEVNULL) await proc.wait() diff --git a/tests/test_web_protocol.py b/tests/test_web_protocol.py index 4d901894250..0b11370df90 100644 --- a/tests/test_web_protocol.py +++ b/tests/test_web_protocol.py @@ -835,10 +835,7 @@ async def handler(request): app.router.add_route('POST', '/', handler) server = await aiohttp_server(app, logger=logger) - if helpers.PY_38: - writer = await asyncio.connect('127.0.0.1', server.port) - else: - _, writer = await asyncio.open_connection('127.0.0.1', server.port) + _, writer = await asyncio.open_connection('127.0.0.1', server.port) writer.write("""POST / HTTP/1.1\r Connection: keep-alive\r Content-Length: 10\r From a22acb38520cb5befb2e989c3ab6eb4aa264e7b4 Mon Sep 17 00:00:00 2001 From: jack1142 <6032823+jack1142@users.noreply.github.com> Date: Sun, 26 Apr 2020 01:29:10 +0200 Subject: [PATCH 006/296] [3.6] switch noop from function to class (#4322) (#4712) --- CHANGES/4282.bugfix | 1 + aiohttp/helpers.py | 8 ++++---- tests/test_web_protocol.py | 15 --------------- 3 files changed, 5 insertions(+), 19 deletions(-) create mode 100644 CHANGES/4282.bugfix diff --git a/CHANGES/4282.bugfix b/CHANGES/4282.bugfix new file mode 100644 index 00000000000..27062bb91bb --- /dev/null +++ b/CHANGES/4282.bugfix @@ -0,0 +1 @@ +Remove warning messages from noop. diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index 8405d3a7ea8..1fdfac5e8aa 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -24,6 +24,7 @@ Any, Callable, Dict, + Generator, Iterable, Iterator, List, @@ -99,12 +100,11 @@ def all_tasks( coroutines = asyncio.coroutines old_debug = coroutines._DEBUG # type: ignore -# prevent "coroutine noop was never awaited" warning. -coroutines._DEBUG = False # type: ignore +class noop: + def __await__(self) -> Generator[None, None, None]: + yield -async def noop(*args: Any, **kwargs: Any) -> None: - return noop2 = noop diff --git a/tests/test_web_protocol.py b/tests/test_web_protocol.py index 0b11370df90..181e10a1611 100644 --- a/tests/test_web_protocol.py +++ b/tests/test_web_protocol.py @@ -252,21 +252,6 @@ async def test_bad_method(srv, buf) -> None: assert buf.startswith(b'HTTP/1.0 400 Bad Request\r\n') -async def test_data_received_error(srv, buf) -> None: - transport = srv.transport - srv._request_parser = mock.Mock() - srv._request_parser.feed_data.side_effect = TypeError - - srv.data_received( - b'!@#$ / HTTP/1.0\r\n' - b'Host: example.com\r\n\r\n') - - await asyncio.sleep(0) - assert buf.startswith(b'HTTP/1.0 500 Internal Server Error\r\n') - assert transport.close.called - assert srv._error_handler is None - - async def test_line_too_long(srv, buf) -> None: srv.data_received(b''.join([b'a' for _ in range(10000)]) + b'\r\n\r\n') From 8f15f072a1e73d9f2a577d86fe6483bebd755447 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 19 Aug 2024 15:31:10 -0500 Subject: [PATCH 007/296] Bump version to 3.10.6.dev0 --- aiohttp/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py index bd65f92f3c7..bcc73f51ccd 100644 --- a/aiohttp/__init__.py +++ b/aiohttp/__init__.py @@ -1,4 +1,4 @@ -__version__ = "3.10.5" +__version__ = "3.10.6.dev0" from typing import TYPE_CHECKING, Tuple From b6ebab27b38b109627484bd13c29a6cabb4b97ef Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 20:53:30 +0000 Subject: [PATCH 008/296] [PR #8755/d50f2759 backport][3.10] Update indirect dependencies with Dependabot (#8756) **This is a backport of PR #8755 as merged into master (d50f275992484d9a1d47c0cf09aba0bf5ff79cb1).** Co-authored-by: Sam Bull --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d1898c69e6e..a67d0133738 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,6 +12,8 @@ updates: # Maintain dependencies for Python - package-ecosystem: "pip" directory: "/" + allow: + - dependency-type: "all" labels: - dependencies schedule: @@ -31,6 +33,8 @@ updates: # Maintain dependencies for Python aiohttp backport - package-ecosystem: "pip" directory: "/" + allow: + - dependency-type: "all" labels: - dependencies target-branch: "3.10" From 947b9a0624c1f13cfda4b48f896658e262b83494 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 22:04:12 +0000 Subject: [PATCH 009/296] [PR #8755/d50f2759 backport][3.11] Update indirect dependencies with Dependabot (#8757) **This is a backport of PR #8755 as merged into master (d50f275992484d9a1d47c0cf09aba0bf5ff79cb1).** Co-authored-by: Sam Bull --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index deb81163faf..9cf1501e811 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,6 +12,8 @@ updates: # Maintain dependencies for Python - package-ecosystem: "pip" directory: "/" + allow: + - dependency-type: "all" labels: - dependencies schedule: @@ -31,6 +33,8 @@ updates: # Maintain dependencies for Python aiohttp backport - package-ecosystem: "pip" directory: "/" + allow: + - dependency-type: "all" labels: - dependencies target-branch: "3.11" From c1b110bae997c5d5b31ad6d489396e7bb6546484 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 22:06:34 +0000 Subject: [PATCH 010/296] Bump sphinxcontrib-applehelp from 1.0.2 to 1.0.4 (#8759) Bumps [sphinxcontrib-applehelp](https://github.com/sphinx-doc/sphinxcontrib-applehelp) from 1.0.2 to 1.0.4.
Changelog

Sourced from sphinxcontrib-applehelp's changelog.

Release 1.0.4 (2023-01-21)

  • Fix package name

Release 1.0.3 (2023-01-08)

  • Drop Python 3.7 and lower
  • Fix deprecation warnings from Sphinx 6.1
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=sphinxcontrib-applehelp&package-manager=pip&previous-version=1.0.2&new-version=1.0.4)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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/doc-spelling.txt | 9 +++++---- requirements/doc.txt | 9 +++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 93e94b158b0..ae739e9a47e 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -215,7 +215,7 @@ sphinx==7.1.2 # sphinxcontrib-blockdiag # sphinxcontrib-spelling # sphinxcontrib-towncrier -sphinxcontrib-applehelp==1.0.2 +sphinxcontrib-applehelp==1.0.4 # via sphinx sphinxcontrib-blockdiag==3.0.0 # via -r requirements/doc.in diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 9ee15189662..858c43782fc 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -54,11 +54,10 @@ requests==2.31.0 # via sphinx snowballstemmer==2.2.0 # via sphinx -sphinx==7.2.6 +sphinx==7.1.2 # via # -r requirements/doc.in # sphinxcontrib-blockdiag - # sphinxcontrib-serializinghtml # sphinxcontrib-spelling # sphinxcontrib-towncrier sphinxcontrib-applehelp==1.0.4 @@ -73,7 +72,7 @@ sphinxcontrib-jsmath==1.0.1 # via sphinx sphinxcontrib-qthelp==1.0.3 # via sphinx -sphinxcontrib-serializinghtml==1.1.9 +sphinxcontrib-serializinghtml==1.1.5 # via sphinx sphinxcontrib-spelling==8.0.0 ; platform_system != "Windows" # via -r requirements/doc-spelling.in @@ -96,4 +95,6 @@ zipp==3.17.0 # The following packages are considered to be unsafe in a requirements file: setuptools==68.0.0 - # via blockdiag + # via + # blockdiag + # sphinx diff --git a/requirements/doc.txt b/requirements/doc.txt index d9e7fb0ad7f..89a38bd8b5b 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -52,11 +52,10 @@ requests==2.31.0 # via sphinx snowballstemmer==2.2.0 # via sphinx -sphinx==7.2.6 +sphinx==7.1.2 # via # -r requirements/doc.in # sphinxcontrib-blockdiag - # sphinxcontrib-serializinghtml # sphinxcontrib-towncrier sphinxcontrib-applehelp==1.0.4 # via sphinx @@ -70,7 +69,7 @@ sphinxcontrib-jsmath==1.0.1 # via sphinx sphinxcontrib-qthelp==1.0.3 # via sphinx -sphinxcontrib-serializinghtml==1.1.9 +sphinxcontrib-serializinghtml==1.1.5 # via sphinx sphinxcontrib-towncrier==0.4.0a0 # via -r requirements/doc.in @@ -91,4 +90,6 @@ zipp==3.17.0 # The following packages are considered to be unsafe in a requirements file: setuptools==68.0.0 - # via blockdiag + # via + # blockdiag + # sphinx From 787699787028366aa5956da65edccc5f430550b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 22:13:58 +0000 Subject: [PATCH 011/296] Bump cfgv from 3.3.1 to 3.4.0 (#8764) Bumps [cfgv](https://github.com/asottile/cfgv) from 3.3.1 to 3.4.0.
Commits
  • 44cc735 v3.4.0
  • 53641d0 Merge pull request #122 from asottile/custom-display-name
  • a9dbeca add a custom display name for loading from a file
  • a05f8f2 Merge pull request #121 from asottile/pre-commit-ci-update-config
  • 38a74b3 [pre-commit.ci] pre-commit autoupdate
  • 9a0589f Merge pull request #120 from asottile/pre-commit-ci-update-config
  • 1cd03cc [pre-commit.ci] pre-commit autoupdate
  • 285c4bc Merge pull request #119 from asottile/pre-commit-ci-update-config
  • 4cc61b2 [pre-commit.ci] pre-commit autoupdate
  • 26255c6 Merge pull request #118 from asottile/all-repos_autofix_py38-plus
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cfgv&package-manager=pip&previous-version=3.3.1&new-version=3.4.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 ae739e9a47e..d9efad1c215 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -42,7 +42,7 @@ cffi==1.17.0 # via # cryptography # pycares -cfgv==3.3.1 +cfgv==3.4.0 # via pre-commit charset-normalizer==3.2.0 # via requests diff --git a/requirements/dev.txt b/requirements/dev.txt index 15cf7d21685..bb14919009d 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -40,7 +40,7 @@ cffi==1.17.0 # via # cryptography # pycares -cfgv==3.3.1 +cfgv==3.4.0 # via pre-commit charset-normalizer==3.2.0 # via requests diff --git a/requirements/lint.txt b/requirements/lint.txt index d5a6435c0b4..23878d681f7 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -18,7 +18,7 @@ cffi==1.17.0 # via # cryptography # pycares -cfgv==3.3.1 +cfgv==3.4.0 # via pre-commit charset-normalizer==3.3.2 # via requests From b4b80393ef9f0d7ba76b4bb0e591db1b4e071f09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 22:14:06 +0000 Subject: [PATCH 012/296] Bump pygments from 2.15.1 to 2.18.0 (#8766) Bumps [pygments](https://github.com/pygments/pygments) from 2.15.1 to 2.18.0.
Release notes

Sourced from pygments's releases.

2.18.0

  • New lexers:

  • Updated lexers:

    • Awk: recognize ternary operator (#2687)
    • Bash: add openrc alias (#2599, #2371)
    • Coq: add keywords, lex more vernacular command arguments, produce fewer tokens on heading comments (#2678)
    • DNS zone files: Fix comment parsing (#2595)
    • Hy: Support unicode literals (#1126)
    • Inform6: Update to Inform 6.42 (#2644)
    • lean: Fix name handling (#2614)
    • Logtalk: add uninstantiation keyword and recognize escape sequences (#2619)
    • Macaulay2: Update to 1.23 (#2655)
    • Python: fix highlighting of soft keywords before None/True/False
    • reStructuredText: use Token.Comment for comments instead of Comment.Preproc (#2598)
    • Rust: highlight :, :: and -> as Punctuation and whitespace as Whitespace, instead of Text in both cases (#2631)
    • Spice: Add keywords (#2621)
    • SQL Explain: allow negative numbers (#2610)
    • Swift: Support multiline strings (#2681)
    • ThingsDB: add constants and new functions; support template strings (#2624)
    • UL4: support nested <?doc?> and <?note?> tags (#2597)
    • VHDL: support multi-line comments of VHDL-2008 (#2622)
    • Wikitext: Remove kk-* in variant_langs (#2647)
    • Xtend: Add val and var (#2602)
  • New styles:

  • Make background colors in the image formatter work with Pillow 10.0 (#2623)

  • Require Python 3.8. As a result, the importlib-metadata package is no longer needed for fast plugin discovery on Python 3.7. The plugins extra (used as, e.g., pip install pygments[plugins])

... (truncated)

Changelog

Sourced from pygments's changelog.

Version 2.18.0

(released May 4th, 2024)

  • New lexers:

  • Updated lexers:

    • Awk: recognize ternary operator (#2687)
    • Bash: add openrc alias (#2599, #2371)
    • Coq: add keywords, lex more vernacular command arguments, produce fewer tokens on heading comments (#2678)
    • DNS zone files: Fix comment parsing (#2595)
    • Hy: Support unicode literals (#1126)
    • Inform6: Update to Inform 6.42 (#2644)
    • lean: Fix name handling (#2614)
    • Logtalk: add uninstantiation keyword and recognize escape sequences (#2619)
    • Macaulay2: Update to 1.23 (#2655)
    • Python: fix highlighting of soft keywords before None/True/False
    • reStructuredText: use Token.Comment for comments instead of Comment.Preproc (#2598)
    • Rust: highlight :, :: and -> as Punctuation and whitespace as Whitespace, instead of Text in both cases (#2631)
    • Spice: Add keywords (#2621)
    • SQL Explain: allow negative numbers (#2610)
    • Swift: Support multiline strings (#2681)
    • ThingsDB: add constants and new functions; support template strings (#2624)
    • UL4: support nested <?doc?> and <?note?> tags (#2597)
    • VHDL: support multi-line comments of VHDL-2008 (#2622)
    • Wikitext: Remove kk-* in variant_langs (#2647)
    • Xtend: Add val and var (#2602)
  • New styles:

  • Make background colors in the image formatter work with Pillow 10.0 (#2623)

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pygments&package-manager=pip&previous-version=2.15.1&new-version=2.18.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- requirements/lint.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index d9efad1c215..0d715df8139 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -156,7 +156,7 @@ pydantic-core==2.6.0 # via pydantic pyenchant==3.2.2 # via sphinxcontrib-spelling -pygments==2.15.1 +pygments==2.18.0 # via sphinx pyjwt==2.3.0 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index bb14919009d..d068b291288 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -149,7 +149,7 @@ pydantic==2.2.0 # via python-on-whales pydantic-core==2.6.0 # via pydantic -pygments==2.15.1 +pygments==2.18.0 # via sphinx pyjwt==2.8.0 # via diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 858c43782fc..76d8f40bb13 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -46,7 +46,7 @@ pillow==9.5.0 # blockdiag pyenchant==3.2.2 # via sphinxcontrib-spelling -pygments==2.15.1 +pygments==2.18.0 # via sphinx pytz==2023.3.post1 # via babel diff --git a/requirements/doc.txt b/requirements/doc.txt index 89a38bd8b5b..fac8fda5c47 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -44,7 +44,7 @@ pillow==9.5.0 # via # -c requirements/broken-projects.in # blockdiag -pygments==2.15.1 +pygments==2.18.0 # via sphinx pytz==2023.3.post1 # via babel diff --git a/requirements/lint.txt b/requirements/lint.txt index 23878d681f7..65e0726ae3b 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -70,7 +70,7 @@ pydantic==2.7.1 # via python-on-whales pydantic-core==2.18.2 # via pydantic -pygments==2.17.2 +pygments==2.18.0 # via rich pytest==8.3.2 # via From ff8800f46f4a51f8d30da0012be13a2a06b994ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 22:14:10 +0000 Subject: [PATCH 013/296] Bump backports-entry-points-selectable from 1.1.1 to 1.3.0 (#8769) Bumps [backports-entry-points-selectable](https://github.com/jaraco/backports.entry_points_selectable) from 1.1.1 to 1.3.0.
Changelog

Sourced from backports-entry-points-selectable's changelog.

v1.3.0

Features

  • Require Python 3.8 or later.

v1.2.0

Refreshed packaging.

Require Python 3.7 or later.

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=backports-entry-points-selectable&package-manager=pip&previous-version=1.1.1&new-version=1.3.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 0d715df8139..f5553b21832 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -28,7 +28,7 @@ attrs==24.2.0 # via -r requirements/runtime-deps.in babel==2.9.1 # via sphinx -backports-entry-points-selectable==1.1.1 +backports-entry-points-selectable==1.3.0 # via virtualenv blockdiag==2.0.1 # via sphinxcontrib-blockdiag From 170c78d5ea711602304b4d133c04fda2d7f59cc3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 22:37:44 +0000 Subject: [PATCH 014/296] Bump tqdm from 4.62.3 to 4.66.5 (#8771) Bumps [tqdm](https://github.com/tqdm/tqdm) from 4.62.3 to 4.66.5.
Release notes

Sourced from tqdm's releases.

tqdm v4.66.5 stable

tqdm v4.66.4 stable

  • rich: fix completion (#1395 <- #1306)
  • minor framework updates & code tidy (#1578)

tqdm v4.66.3 stable

  • cli: eval safety (fixes CVE-2024-34062, GHSA-g7vv-2v7x-gj9p)

tqdm v4.66.2 stable

  • pandas: add DataFrame.progress_map (#1549)
  • notebook: fix HTML padding (#1506)
  • keras: fix resuming training when verbose>=2 (#1508)
  • fix format_num negative fractions missing leading zero (#1548)
  • fix Python 3.12 DeprecationWarning on import (#1519)
  • linting: use f-strings (#1549)
  • update tests (#1549)
  • CI: bump actions (#1549)

tqdm v4.66.1 stable

  • fix utils.envwrap types (#1493 <- #1491, #1320 <- #966, #1319)
    • e.g. cloudwatch & kubernetes workaround: export TQDM_POSITION=-1
  • drop mentions of unsupported Python versions

tqdm v4.66.0 stable

  • environment variables to override defaults (TQDM_*) (#1491 <- #1061, #950 <- #614, #1318, #619, #612, #370)
    • e.g. in CI jobs, export TQDM_MININTERVAL=5 to avoid log spam
    • add tests & docs for tqdm.utils.envwrap
  • fix & update CLI completion
  • fix & update API docs
  • minor code tidy: replace os.path => pathlib.Path
  • fix docs image hosting
  • release with CI bot account again (cli/cli#6680)

tqdm v4.65.2 stable

  • exclude examples from distributed wheel (#1492)

tqdm v4.65.1 stable

  • migrate setup.{cfg,py} => pyproject.toml (#1490)
    • fix asv benchmarks

... (truncated)

Commits
  • 951a2ba Merge pull request #1595 from hroncok/py3.13-await-aclose
  • 2fbad6a Avoid Python 3.13+ RuntimeWarning: coroutine method 'aclose' of 'acount' was ...
  • 0254345 Merge pull request #1594 from mgorny/py313-docstring
  • 43230f6 slight lint
  • 5ba6595 cli: Fix docstring processing with Python 3.13+
  • 448946a Merge pull request #1602 from tqdm/devel
  • 46cd395 add py3.12 support
  • d8ac656 ncols: support FreeBSD
  • 4f66276 bump deps & linters
  • 54796cc docs: bump versions
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tqdm&package-manager=pip&previous-version=4.62.3&new-version=4.66.5)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index f5553b21832..2512d5e5bf9 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -248,7 +248,7 @@ towncrier==23.11.0 # via # -r requirements/doc.in # sphinxcontrib-towncrier -tqdm==4.62.3 +tqdm==4.66.5 # via python-on-whales trustme==1.1.0 ; platform_machine != "i686" # via diff --git a/requirements/dev.txt b/requirements/dev.txt index d068b291288..1a449e6abf1 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -234,7 +234,7 @@ towncrier==23.11.0 # via # -r requirements/doc.in # sphinxcontrib-towncrier -tqdm==4.65.0 +tqdm==4.66.5 # via python-on-whales trustme==1.1.0 ; platform_machine != "i686" # via diff --git a/requirements/lint.txt b/requirements/lint.txt index 65e0726ae3b..066e73c6ad4 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -99,7 +99,7 @@ tomli==2.0.1 # mypy # pytest # slotscheck -tqdm==4.66.2 +tqdm==4.66.5 # via python-on-whales trustme==1.1.0 # via -r requirements/lint.in diff --git a/requirements/test.txt b/requirements/test.txt index d663e411bdb..2d07a0e787c 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -105,7 +105,7 @@ tomli==2.0.1 # coverage # mypy # pytest -tqdm==4.65.0 +tqdm==4.66.5 # via python-on-whales trustme==1.1.0 ; platform_machine != "i686" # via -r requirements/test.in From 0c2626958756c8fb24a7bdd05e4b2009e367f6c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 23:02:29 +0000 Subject: [PATCH 015/296] Bump build from 1.0.3 to 1.2.1 (#8775) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [build](https://github.com/pypa/build) from 1.0.3 to 1.2.1.
Release notes

Sourced from build's releases.

Version 1.2.1

What's Changed

  • Avoid error when terminal width is undetectable on Python < 3.11 (PR #761)

Full Changelog: https://github.com/pypa/build/compare/1.2.0...1.2.1

Version 1.2.0

What's Changed

  • Add --installer option, supporting pip and uv. Added uv extra. (PR #751)
  • Improve console output and provide -v for dependency installation (PR #749)
  • Avoid compiling unused bytecode when using pip (PR #752)
  • Dropped support for Python 3.7 (PR #743)

Full Changelog: https://github.com/pypa/build/compare/1.1.1...1.2.0

Version 1.1.1

What's Changed

  • Fixed invoking outer pip from user site packages (PR #746, fixes issue #745)
  • Corrected the minimum pip version required to use an outer pip (PR #746, fixes issue #745)

Full Changelog: https://github.com/pypa/build/compare/v1.1.0...1.1.1

Version 1.1.0

What's Changed

  • Use external pip if available instead of installing, speeds up environment setup with virtualenv slightly and venv significantly. (PR #736)
  • Stopped injecting wheel as a build dependency automatically, in the case of missing pyproject.toml -- by @​webknjaz. (PR #716)
  • Use importlib_metadata on Python <3.10.2 for bugfixes not present in those CPython standard libraries (not required when bootstrapping) -- by @​GianlucaFicarelli. (PR #693, fixes issue #692)

New Contributors

Full Changelog: https://github.com/pypa/build/compare/1.0.3...v1.1.0

Changelog

Sourced from build's changelog.

1.2.1 (2024-03-28)

  • Avoid error when terminal width is undetectable on Python < 3.11 (PR :pr:761)

1.2.0 (2024-03-27)

  • Add --installer option, supporting pip and uv. Added uv extra. (PR :pr:751)
  • Improve console output and provide -v for dependency installation (PR :pr:749)
  • Avoid compiling unused bytecode when using pip (PR :pr:752)
  • Dropped support for Python 3.7 (PR :pr:743)

1.1.1 (2024-02-29)

  • Fixed invoking outer pip from user site packages (PR :pr:746, fixes issue :issue:745)
  • Corrected the minimum pip version required to use an outer pip (PR :pr:746, fixes issue :issue:745)

1.1.0 (2024-02-29)

  • Use external pip if available instead of installing, speeds up environment setup with virtualenv slightly and venv significantly. (PR :pr:736)
  • Stopped injecting wheel as a build dependency automatically, in the case of missing pyproject.toml -- by :user:webknjaz. (PR :pr:716)
  • Use importlib_metadata on Python <3.10.2 for bugfixes not present in those CPython standard libraries (not required when bootstrapping) -- by :user:GianlucaFicarelli. (PR :pr:693, fixes issue :issue:692)
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=build&package-manager=pip&previous-version=1.0.3&new-version=1.2.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 2512d5e5bf9..51e32642ad4 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -34,7 +34,7 @@ blockdiag==2.0.1 # via sphinxcontrib-blockdiag brotli==1.1.0 ; platform_python_implementation == "CPython" # via -r requirements/runtime-deps.in -build==1.0.3 +build==1.2.1 # via pip-tools certifi==2023.7.22 # via requests diff --git a/requirements/dev.txt b/requirements/dev.txt index 1a449e6abf1..01255801616 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -32,7 +32,7 @@ blockdiag==3.0.0 # via sphinxcontrib-blockdiag brotli==1.1.0 ; platform_python_implementation == "CPython" # via -r requirements/runtime-deps.in -build==1.0.3 +build==1.2.1 # via pip-tools certifi==2023.7.22 # via requests From eadc376b4e891ece42aeae71c7bab688d750ba41 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 23:49:22 +0000 Subject: [PATCH 016/296] Bump pycares from 4.3.0 to 4.4.0 (#8774) Bumps [pycares](https://github.com/saghul/pycares) from 4.3.0 to 4.4.0.
Commits
  • 3d0f9cf Bump version to 4.4.0
  • bc7630f Add support for 3.12, drop EOL 3.7
  • 86baf75 Bump versions of used GitHub Actions
  • d62a60c Bump GitHub Actions versions and fix warnings in the process
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pycares&package-manager=pip&previous-version=4.3.0&new-version=4.4.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index d90626583b0..1327703b7ba 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -32,7 +32,7 @@ multidict==6.0.5 # yarl packaging==23.1 # via gunicorn -pycares==4.3.0 +pycares==4.4.0 # via aiodns pycparser==2.21 # via cffi diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 51e32642ad4..392c41eb376 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -146,7 +146,7 @@ pre-commit==3.5.0 # via -r requirements/lint.in proxy-py==2.4.7 # via -r requirements/test.in -pycares==4.3.0 +pycares==4.4.0 # via aiodns pycparser==2.21 # via cffi diff --git a/requirements/dev.txt b/requirements/dev.txt index 01255801616..df5552b908a 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -141,7 +141,7 @@ pre-commit==3.5.0 # via -r requirements/lint.in proxy-py==2.4.7 # via -r requirements/test.in -pycares==4.3.0 +pycares==4.4.0 # via aiodns pycparser==2.21 # via cffi diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index 977f25cb3be..f8bfd4b6b21 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -28,7 +28,7 @@ multidict==6.0.5 # via # -r requirements/runtime-deps.in # yarl -pycares==4.3.0 +pycares==4.4.0 # via aiodns pycparser==2.21 # via cffi diff --git a/requirements/test.txt b/requirements/test.txt index 2d07a0e787c..7a8bf4ca058 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -69,7 +69,7 @@ pluggy==1.5.0 # via pytest proxy-py==2.4.7 # via -r requirements/test.in -pycares==4.3.0 +pycares==4.4.0 # via aiodns pycparser==2.21 # via cffi From a5b66e3bcd3d917b304fef13858ce136a339f65c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 00:05:39 +0000 Subject: [PATCH 017/296] Bump blockdiag from 2.0.1 to 3.0.0 (#8761) Bumps [blockdiag](https://github.com/blockdiag/blockdiag) from 2.0.1 to 3.0.0.
Changelog

Sourced from blockdiag's changelog.

3.0.0 (2021-12-06)

  • Drop python3.6 support

  • Use funcparserlib-1.0.0a0 or newer to support new python versions

  • Allow to write multiline string via triple quotes (""" ... """)

  • Fix a bug

    • Fix #147: file existence disclosure using svg renderer

2.0.0 (2020-02-01)

  • Fix a bug

    • Fix #126: '_io.BufferedRandom' object has no attribute 'buffer'

2.0.0 (2020-01-26)

  • Drop python2 and python3.4 support

  • Fix a bug

    • Fix #109 blockdiag does not work with recent pillow

1.5.4 (2018-07-22)

  • Fix bug

    • Fix #94 Python 3.7 compatibility

1.5.3 (2015-07-30)

  • Fix bug

    • Fix #67 Group overlaps with nodes having href

1.5.2 (2015-05-17)

  • Fix dependency; webcolors-1.5 does not support py32

  • Fix bug

    • Fix images.open() failed with PIL

1.5.1 (2015-02-21)

  • Fix bug

    • Fix labels are overwrapped on antialias mode

1.5.0 (2015-01-01)

  • Refactor cleanup procedures

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=blockdiag&package-manager=pip&previous-version=2.0.1&new-version=3.0.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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/doc-spelling.txt | 4 +--- requirements/doc.txt | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 392c41eb376..ddd870760ad 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -30,7 +30,7 @@ babel==2.9.1 # via sphinx backports-entry-points-selectable==1.3.0 # via virtualenv -blockdiag==2.0.1 +blockdiag==3.0.0 # via sphinxcontrib-blockdiag brotli==1.1.0 ; platform_python_implementation == "CPython" # via -r requirements/runtime-deps.in diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 76d8f40bb13..925512c194c 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -95,6 +95,4 @@ zipp==3.17.0 # The following packages are considered to be unsafe in a requirements file: setuptools==68.0.0 - # via - # blockdiag - # sphinx + # via blockdiag diff --git a/requirements/doc.txt b/requirements/doc.txt index fac8fda5c47..a139b99145b 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -90,6 +90,4 @@ zipp==3.17.0 # The following packages are considered to be unsafe in a requirements file: setuptools==68.0.0 - # via - # blockdiag - # sphinx + # via blockdiag From ac614c5b45e44c0f7313fa8f8bbe332b0976b102 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:48:21 +0000 Subject: [PATCH 018/296] Bump imagesize from 1.3.0 to 1.4.1 (#8783) Bumps [imagesize](https://github.com/shibukawa/imagesize_py) from 1.3.0 to 1.4.1.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=imagesize&package-manager=pip&previous-version=1.3.0&new-version=1.4.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index ddd870760ad..cb48c7d75c9 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -95,7 +95,7 @@ idna==3.3 # requests # trustme # yarl -imagesize==1.3.0 +imagesize==1.4.1 # via sphinx importlib-metadata==7.0.0 # via From f417d96147a79959a40d1a85ca256d0b6102dec7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:05:33 +0000 Subject: [PATCH 019/296] Bump urllib3 from 1.26.7 to 2.2.2 (#8786) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.7 to 2.2.2.
Release notes

Sourced from urllib3's releases.

2.2.2

🚀 urllib3 is fundraising for HTTP/2 support

urllib3 is raising ~$40,000 USD to release HTTP/2 support and ensure long-term sustainable maintenance of the project after a sharp decline in financial support for 2023. If your company or organization uses Python and would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and thousands of other projects please consider contributing financially to ensure HTTP/2 support is developed sustainably and maintained for the long-haul.

Thank you for your support.

Changes

  • Added the Proxy-Authorization header to the list of headers to strip from requests when redirecting to a different host. As before, different headers can be set via Retry.remove_headers_on_redirect.
  • Allowed passing negative integers as amt to read methods of http.client.HTTPResponse as an alternative to None. (#3122)
  • Fixed return types representing copying actions to use typing.Self. (#3363)

Full Changelog: https://github.com/urllib3/urllib3/compare/2.2.1...2.2.2

2.2.1

🚀 urllib3 is fundraising for HTTP/2 support

urllib3 is raising ~$40,000 USD to release HTTP/2 support and ensure long-term sustainable maintenance of the project after a sharp decline in financial support for 2023. If your company or organization uses Python and would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and thousands of other projects please consider contributing financially to ensure HTTP/2 support is developed sustainably and maintained for the long-haul.

Thank you for your support.

Changes

  • Fixed issue where InsecureRequestWarning was emitted for HTTPS connections when using Emscripten. (#3331)
  • Fixed HTTPConnectionPool.urlopen to stop automatically casting non-proxy headers to HTTPHeaderDict. This change was premature as it did not apply to proxy headers and HTTPHeaderDict does not handle byte header values correctly yet. (#3343)
  • Changed ProtocolError to InvalidChunkLength when response terminates before the chunk length is sent. (#2860)
  • Changed ProtocolError to be more verbose on incomplete reads with excess content. (#3261)

2.2.0

🖥️ urllib3 now works in the browser

:tada: This release adds experimental support for using urllib3 in the browser with Pyodide! :tada:

Thanks to Joe Marshall (@​joemarshall) for contributing this feature. This change was possible thanks to work done in urllib3 v2.0 to detach our API from http.client. Please report all bugs to the urllib3 issue tracker.

🚀 urllib3 is fundraising for HTTP/2 support

urllib3 is raising ~$40,000 USD to release HTTP/2 support and ensure long-term sustainable maintenance of the project after a sharp decline in financial support for 2023. If your company or organization uses Python and would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and thousands of other projects please consider contributing financially to ensure HTTP/2 support is developed sustainably and maintained for the long-haul.

Thank you for your support.

Changes

  • Added support for Emscripten and Pyodide, including streaming support in cross-origin isolated browser environments where threading is enabled. (#2951)
  • Added support for HTTPResponse.read1() method. (#3186)
  • Added rudimentary support for HTTP/2. (#3284)
  • Fixed issue where requests against urls with trailing dots were failing due to SSL errors when using proxy. (#2244)
  • Fixed HTTPConnection.proxy_is_verified and HTTPSConnection.proxy_is_verified to be always set to a boolean after connecting to a proxy. It could be None in some cases previously. (#3130)

... (truncated)

Changelog

Sourced from urllib3's changelog.

2.2.2 (2024-06-17)

  • Added the Proxy-Authorization header to the list of headers to strip from requests when redirecting to a different host. As before, different headers can be set via Retry.remove_headers_on_redirect.
  • Allowed passing negative integers as amt to read methods of http.client.HTTPResponse as an alternative to None. ([#3122](https://github.com/urllib3/urllib3/issues/3122) <https://github.com/urllib3/urllib3/issues/3122>__)
  • Fixed return types representing copying actions to use typing.Self. ([#3363](https://github.com/urllib3/urllib3/issues/3363) <https://github.com/urllib3/urllib3/issues/3363>__)

2.2.1 (2024-02-16)

  • Fixed issue where InsecureRequestWarning was emitted for HTTPS connections when using Emscripten. ([#3331](https://github.com/urllib3/urllib3/issues/3331) <https://github.com/urllib3/urllib3/issues/3331>__)
  • Fixed HTTPConnectionPool.urlopen to stop automatically casting non-proxy headers to HTTPHeaderDict. This change was premature as it did not apply to proxy headers and HTTPHeaderDict does not handle byte header values correctly yet. ([#3343](https://github.com/urllib3/urllib3/issues/3343) <https://github.com/urllib3/urllib3/issues/3343>__)
  • Changed InvalidChunkLength to ProtocolError when response terminates before the chunk length is sent. ([#2860](https://github.com/urllib3/urllib3/issues/2860) <https://github.com/urllib3/urllib3/issues/2860>__)
  • Changed ProtocolError to be more verbose on incomplete reads with excess content. ([#3261](https://github.com/urllib3/urllib3/issues/3261) <https://github.com/urllib3/urllib3/issues/3261>__)

2.2.0 (2024-01-30)

  • Added support for Emscripten and Pyodide <https://urllib3.readthedocs.io/en/latest/reference/contrib/emscripten.html>, including streaming support in cross-origin isolated browser environments where threading is enabled. ([#2951](https://github.com/urllib3/urllib3/issues/2951) <https://github.com/urllib3/urllib3/issues/2951>)
  • Added support for HTTPResponse.read1() method. ([#3186](https://github.com/urllib3/urllib3/issues/3186) <https://github.com/urllib3/urllib3/issues/3186>__)
  • Added rudimentary support for HTTP/2. ([#3284](https://github.com/urllib3/urllib3/issues/3284) <https://github.com/urllib3/urllib3/issues/3284>__)
  • Fixed issue where requests against urls with trailing dots were failing due to SSL errors when using proxy. ([#2244](https://github.com/urllib3/urllib3/issues/2244) <https://github.com/urllib3/urllib3/issues/2244>__)
  • Fixed HTTPConnection.proxy_is_verified and HTTPSConnection.proxy_is_verified to be always set to a boolean after connecting to a proxy. It could be None in some cases previously. ([#3130](https://github.com/urllib3/urllib3/issues/3130) <https://github.com/urllib3/urllib3/issues/3130>__)
  • Fixed an issue where headers passed in a request with json= would be mutated ([#3203](https://github.com/urllib3/urllib3/issues/3203) <https://github.com/urllib3/urllib3/issues/3203>__)
  • Fixed HTTPSConnection.is_verified to be set to False when connecting from a HTTPS proxy to an HTTP target. It was set to True previously. ([#3267](https://github.com/urllib3/urllib3/issues/3267) <https://github.com/urllib3/urllib3/issues/3267>__)
  • Fixed handling of new error message from OpenSSL 3.2.0 when configuring an HTTP proxy as HTTPS ([#3268](https://github.com/urllib3/urllib3/issues/3268) <https://github.com/urllib3/urllib3/issues/3268>__)
  • Fixed TLS 1.3 post-handshake auth when the server certificate validation is disabled ([#3325](https://github.com/urllib3/urllib3/issues/3325) <https://github.com/urllib3/urllib3/issues/3325>__)
  • Note for downstream distributors: To run integration tests, you now need to run the tests a second time with the --integration pytest flag. ([#3181](https://github.com/urllib3/urllib3/issues/3181) <https://github.com/urllib3/urllib3/issues/3181>__)

2.1.0 (2023-11-13)

  • Removed support for the deprecated urllib3[secure] extra. ([#2680](https://github.com/urllib3/urllib3/issues/2680) <https://github.com/urllib3/urllib3/issues/2680>__)
  • Removed support for the deprecated SecureTransport TLS implementation. ([#2681](https://github.com/urllib3/urllib3/issues/2681) <https://github.com/urllib3/urllib3/issues/2681>__)
  • Removed support for the end-of-life Python 3.7. ([#3143](https://github.com/urllib3/urllib3/issues/3143) <https://github.com/urllib3/urllib3/issues/3143>__)
  • Allowed loading CA certificates from memory for proxies. ([#3065](https://github.com/urllib3/urllib3/issues/3065) <https://github.com/urllib3/urllib3/issues/3065>__)
  • Fixed decoding Gzip-encoded responses which specified x-gzip content-encoding. ([#3174](https://github.com/urllib3/urllib3/issues/3174) <https://github.com/urllib3/urllib3/issues/3174>__)

2.0.7 (2023-10-17)

  • Made body stripped from HTTP requests changing the request method to GET after HTTP 303 "See Other" redirect responses.

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=urllib3&package-manager=pip&previous-version=1.26.7&new-version=2.2.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- requirements/lint.txt | 2 +- requirements/test.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index cb48c7d75c9..11b8a66ae4f 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -266,7 +266,7 @@ typing-extensions==4.11.0 # python-on-whales uritemplate==4.1.1 # via gidgethub -urllib3==1.26.7 +urllib3==2.2.2 # via requests uvloop==0.20.0 ; platform_system != "Windows" # via diff --git a/requirements/dev.txt b/requirements/dev.txt index df5552b908a..a906793ddd1 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -253,7 +253,7 @@ typing-extensions==4.11.0 # typer uritemplate==4.1.1 # via gidgethub -urllib3==2.0.4 +urllib3==2.2.2 # via requests uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" # via diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 925512c194c..843c3415dfa 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -84,7 +84,7 @@ towncrier==23.11.0 # via # -r requirements/doc.in # sphinxcontrib-towncrier -urllib3==2.0.4 +urllib3==2.2.2 # via requests webcolors==1.13 # via blockdiag diff --git a/requirements/doc.txt b/requirements/doc.txt index a139b99145b..172cf824284 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -79,7 +79,7 @@ towncrier==23.11.0 # via # -r requirements/doc.in # sphinxcontrib-towncrier -urllib3==2.0.4 +urllib3==2.2.2 # via requests webcolors==1.13 # via blockdiag diff --git a/requirements/lint.txt b/requirements/lint.txt index 066e73c6ad4..491f273a010 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -115,7 +115,7 @@ typing-extensions==4.11.0 # python-on-whales # rich # typer -urllib3==2.2.1 +urllib3==2.2.2 # via requests uvloop==0.20.0 ; platform_system != "Windows" # via -r requirements/lint.in diff --git a/requirements/test.txt b/requirements/test.txt index 7a8bf4ca058..b4233b93fcc 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -119,7 +119,7 @@ typing-extensions==4.11.0 # pydantic-core # python-on-whales # typer -urllib3==2.0.4 +urllib3==2.2.2 # via requests uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in From 9bd27697c98e20a17ba5a34b58d92f9a23cff437 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:13:51 +0000 Subject: [PATCH 020/296] Bump snowballstemmer from 2.1.0 to 2.2.0 (#8787) Bumps [snowballstemmer](https://github.com/snowballstem/snowball) from 2.1.0 to 2.2.0.
Changelog

Sourced from snowballstemmer's changelog.

Snowball 2.2.0 (2021-11-10)

New Code Generators

  • Add Ada generator from Stephane Carrez (#135).

Javascript

  • Fix generated code to use integer division rather than floating point division.

    Noted by David Corbett.

Pascal

  • Fix code generated for division. Previously real division was used and the generated code would fail to compile with an "Incompatible types" error.

    Noted by David Corbett.

  • Fix code generated for Snowball's minint and maxint constant.

Python

  • Python 2 is no longer actively supported, as proposed on the mailing list: https://lists.tartarus.org/pipermail/snowball-discuss/2021-August/001721.html

  • Fix code generated for division. Previously the Python code we generated used integer division but rounded negative fractions towards negative infinity rather than zero under Python 2, and under Python 3 used floating point division.

    Noted by David Corbett.

Code Quality Improvements

  • C#: An among without functions is now generated as static and groupings are now generated as constant. Patches from James Turner in #146 and #147.

Code generation improvements

  • General:

... (truncated)

Commits
  • 48a67a2 Update for 2.2.0
  • ec00981 Fix handling of len and lenof as names
  • 5559db7 Report clearer error if = is used instead of ==
  • 4ff3598 Optimise constant numeric expressions
  • 40b1641 NEWS: Update
  • f89c3b9 Fix $(EXE_EXT) to $(EXEEXT)
  • 55fd44b Ada: Fix code generated for minint and maxint
  • e6d8b6f Pascal: Fix code generated for minint
  • ed32a1d Ada: Fix "parentheses required for unary minus" errors
  • 47773b9 NEWS: Update
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=snowballstemmer&package-manager=pip&previous-version=2.1.0&new-version=2.2.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 11b8a66ae4f..5791f34f3f6 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -207,7 +207,7 @@ six==1.16.0 # virtualenv slotscheck==0.19.0 # via -r requirements/lint.in -snowballstemmer==2.1.0 +snowballstemmer==2.2.0 # via sphinx sphinx==7.1.2 # via From 47b31d6729f34576ed37dfa706b3a035773ce177 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:39:53 +0000 Subject: [PATCH 021/296] Bump jinja2 from 3.0.3 to 3.1.4 (#8788) Bumps [jinja2](https://github.com/pallets/jinja) from 3.0.3 to 3.1.4.
Release notes

Sourced from jinja2's releases.

3.1.4

This is the Jinja 3.1.4 security release, which fixes security issues and bugs but does not otherwise change behavior and should not result in breaking changes.

PyPI: https://pypi.org/project/Jinja2/3.1.4/ Changes: https://jinja.palletsprojects.com/en/3.1.x/changes/#version-3-1-4

  • The xmlattr filter does not allow keys with / solidus, > greater-than sign, or = equals sign, in addition to disallowing spaces. Regardless of any validation done by Jinja, user input should never be used as keys to this filter, or must be separately validated first. GHSA-h75v-3vvj-5mfj

3.1.3

This is a fix release for the 3.1.x feature branch.

3.1.2

This is a fix release for the 3.1.0 feature release.

3.1.1

3.1.0

This is a feature release, which includes new features and removes previously deprecated features. The 3.1.x branch is now the supported bugfix branch, the 3.0.x branch has become a tag marking the end of support for that branch. We encourage everyone to upgrade, and to use a tool such as pip-tools to pin all dependencies and control upgrades. We also encourage upgrading to MarkupSafe 2.1.1, the latest version at this time.

Changelog

Sourced from jinja2's changelog.

Version 3.1.4

Released 2024-05-05

  • The xmlattr filter does not allow keys with / solidus, > greater-than sign, or = equals sign, in addition to disallowing spaces. Regardless of any validation done by Jinja, user input should never be used as keys to this filter, or must be separately validated first. :ghsa:h75v-3vvj-5mfj

Version 3.1.3

Released 2024-01-10

  • Fix compiler error when checking if required blocks in parent templates are empty. :pr:1858
  • xmlattr filter does not allow keys with spaces. :ghsa:h5c8-rqwp-cp95
  • Make error messages stemming from invalid nesting of {% trans %} blocks more helpful. :pr:1918

Version 3.1.2

Released 2022-04-28

  • Add parameters to Environment.overlay to match __init__. :issue:1645
  • Handle race condition in FileSystemBytecodeCache. :issue:1654

Version 3.1.1

Released 2022-03-25

  • The template filename on Windows uses the primary path separator. :issue:1637

Version 3.1.0

Released 2022-03-24

  • Drop support for Python 3.6. :pr:1534
  • Remove previously deprecated code. :pr:1544

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=jinja2&package-manager=pip&previous-version=3.0.3&new-version=3.1.4)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 5791f34f3f6..5bda339d438 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -107,7 +107,7 @@ incremental==22.10.0 # via towncrier iniconfig==1.1.1 # via pytest -jinja2==3.0.3 +jinja2==3.1.4 # via # sphinx # towncrier diff --git a/requirements/dev.txt b/requirements/dev.txt index a906793ddd1..32f44ed27e3 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -103,7 +103,7 @@ incremental==22.10.0 # via towncrier iniconfig==2.0.0 # via pytest -jinja2==3.1.2 +jinja2==3.1.4 # via # sphinx # towncrier diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 843c3415dfa..b76afe98d57 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -32,7 +32,7 @@ importlib-resources==6.1.1 # via towncrier incremental==22.10.0 # via towncrier -jinja2==3.1.2 +jinja2==3.1.4 # via # sphinx # towncrier diff --git a/requirements/doc.txt b/requirements/doc.txt index 172cf824284..0e5f7d49e0c 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -32,7 +32,7 @@ importlib-resources==6.1.1 # via towncrier incremental==22.10.0 # via towncrier -jinja2==3.1.2 +jinja2==3.1.4 # via # sphinx # towncrier From 91cd7646322492de0aa5b7068906650a9f7c8abf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:40:35 +0000 Subject: [PATCH 022/296] Bump python-dateutil from 2.8.2 to 2.9.0.post0 (#8790) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [python-dateutil](https://github.com/dateutil/dateutil) from 2.8.2 to 2.9.0.post0.
Release notes

Sourced from python-dateutil's releases.

2.9.0.post0

Version 2.9.0.post0 (2024-03-01)

Bugfixes

  • Pinned setuptools_scm to <8, which should make the generated _version.py file compatible with all supported versions of Python.

2.9.0

Version 2.9.0 (2024-02-29)

Data updates

  • Updated tzdata version to 2024a. (gh pr #1342)

Features

  • Made all dateutil submodules lazily imported using PEP 562. On Python 3.7+, things like import dateutil; dateutil.tz.gettz("America/New_York") will now work without explicitly importing dateutil.tz, with the import occurring behind the scenes on first use. The old behavior remains on Python 3.6 and earlier. Fixed by Orson Adams. (gh issue #771, gh pr #1007)

Bugfixes

  • Removed a call to datetime.utcfromtimestamp, which is deprecated as of Python 3.12. Reported by Hugo van Kemenade (gh pr #1284), fixed by Thomas Grainger (gh pr #1285).

Documentation changes

  • Added note into docs and tests where relativedelta would return last day of the month only if the same day on a different month resolves to a date that doesn't exist. Reported by @​hawkEye-01 (gh issue #1167). Fixed by @​Mifrill (gh pr #1168)
Changelog

Sourced from python-dateutil's changelog.

Version 2.9.0.post0 (2024-03-01)

Bugfixes

  • Pinned setuptools_scm to <8, which should make the generated _version.py file compatible with all supported versions of Python.

Version 2.9.0 (2024-02-29)

Data updates

  • Updated tzdata version to 2024a. (gh pr #1342)

Features

  • Made all dateutil submodules lazily imported using PEP 562 <https://www.python.org/dev/peps/pep-0562/>_. On Python 3.7+, things like import dateutil; dateutil.tz.gettz("America/New_York") will now work without explicitly importing dateutil.tz, with the import occurring behind the scenes on first use. The old behavior remains on Python 3.6 and earlier. Fixed by Orson Adams. (gh issue #771, gh pr #1007)

Bugfixes

  • Removed a call to datetime.utcfromtimestamp, which is deprecated as of Python 3.12. Reported by Hugo van Kemenade (gh pr #1284), fixed by Thomas Grainger (gh pr #1285).

Documentation changes

  • Added note into docs and tests where relativedelta would return last day of the month only if the same day on a different month resolves to a date that doesn't exist. Reported by @​hawkEye-01 (gh issue #1167). Fixed by @​Mifrill (gh pr #1168)
Commits
  • 1ae8077 Merge pull request #1346 from pganssle/release_2.9.0.post0
  • ee6de9d Update news to prepare for release
  • 9780d32 Pin setuptools_scm to <8
  • db9d018 Merge pull request #1343 from pganssle/release_2.9.0
  • 423ca2f Run updatezinfo before build
  • edd3fd4 Update NEWS file
  • fe02d02 Run towncrier with Python 3.11
  • 9c7524a Fix MANIFEST.in pattern
  • 6de58f5 Update classifiers to include Python 3.12
  • 8fe0cab Merge pull request #1342 from pganssle/update_zoneinfo
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=python-dateutil&package-manager=pip&previous-version=2.8.2&new-version=2.9.0.post0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 5bda339d438..07ff53cb693 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -180,7 +180,7 @@ pytest-mock==3.14.0 # via # -r requirements/lint.in # -r requirements/test.in -python-dateutil==2.8.2 +python-dateutil==2.9.0.post0 # via freezegun python-on-whales==0.72.0 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 32f44ed27e3..454ec2718df 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -171,7 +171,7 @@ pytest-mock==3.14.0 # via # -r requirements/lint.in # -r requirements/test.in -python-dateutil==2.8.2 +python-dateutil==2.9.0.post0 # via freezegun python-on-whales==0.72.0 # via diff --git a/requirements/test.txt b/requirements/test.txt index b4233b93fcc..1ec683bca8c 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -86,7 +86,7 @@ pytest-cov==5.0.0 # via -r requirements/test.in pytest-mock==3.14.0 # via -r requirements/test.in -python-dateutil==2.8.2 +python-dateutil==2.9.0.post0 # via freezegun python-on-whales==0.72.0 # via -r requirements/test.in From d76e8be0bbbad2c1ca10f2430157e0d312902f19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:41:14 +0000 Subject: [PATCH 023/296] Bump aiohappyeyeballs from 2.3.7 to 2.4.0 (#8791) Bumps [aiohappyeyeballs](https://github.com/aio-libs/aiohappyeyeballs) from 2.3.7 to 2.4.0.
Release notes

Sourced from aiohappyeyeballs's releases.

v2.4.0 (2024-08-19)

Documentation

  • docs: fix a trivial typo in README.md (#84) (f5ae7d4)

Feature

  • feat: add support for python 3.13 (#86) (4f2152f)
Changelog

Sourced from aiohappyeyeballs's changelog.

v2.4.0 (2024-08-19)

Feature

Documentation

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=aiohappyeyeballs&package-manager=pip&previous-version=2.3.7&new-version=2.4.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index 1327703b7ba..3a616ad4d14 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -6,7 +6,7 @@ # aiodns==3.2.0 ; sys_platform == "linux" or sys_platform == "darwin" # via -r requirements/runtime-deps.in -aiohappyeyeballs==2.3.7 +aiohappyeyeballs==2.4.0 # via -r requirements/runtime-deps.in aiosignal==1.3.1 # via -r requirements/runtime-deps.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 07ff53cb693..4ec843a6a8a 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -8,7 +8,7 @@ aiodns==3.2.0 ; sys_platform == "linux" or sys_platform == "darwin" # via # -r requirements/lint.in # -r requirements/runtime-deps.in -aiohappyeyeballs==2.3.7 +aiohappyeyeballs==2.4.0 # via -r requirements/runtime-deps.in aiohttp-theme==0.1.6 # via -r requirements/doc.in diff --git a/requirements/dev.txt b/requirements/dev.txt index 454ec2718df..724b300db16 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -8,7 +8,7 @@ aiodns==3.2.0 ; sys_platform == "linux" or sys_platform == "darwin" # via # -r requirements/lint.in # -r requirements/runtime-deps.in -aiohappyeyeballs==2.3.7 +aiohappyeyeballs==2.4.0 # via -r requirements/runtime-deps.in aiohttp-theme==0.1.6 # via -r requirements/doc.in diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index f8bfd4b6b21..4d1981d5e3b 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -6,7 +6,7 @@ # aiodns==3.2.0 ; sys_platform == "linux" or sys_platform == "darwin" # via -r requirements/runtime-deps.in -aiohappyeyeballs==2.3.7 +aiohappyeyeballs==2.4.0 # via -r requirements/runtime-deps.in aiosignal==1.3.1 # via -r requirements/runtime-deps.in diff --git a/requirements/test.txt b/requirements/test.txt index 1ec683bca8c..aa27682ca5a 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -6,7 +6,7 @@ # aiodns==3.2.0 ; sys_platform == "linux" or sys_platform == "darwin" # via -r requirements/runtime-deps.in -aiohappyeyeballs==2.3.7 +aiohappyeyeballs==2.4.0 # via -r requirements/runtime-deps.in aiosignal==1.3.1 # via -r requirements/runtime-deps.in From b65d5736690e525f68edab6410a1fb31c2a8db01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:09:17 +0000 Subject: [PATCH 024/296] Bump certifi from 2023.7.22 to 2024.7.4 (#8793) Bumps [certifi](https://github.com/certifi/python-certifi) from 2023.7.22 to 2024.7.4.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=certifi&package-manager=pip&previous-version=2023.7.22&new-version=2024.7.4)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- requirements/lint.txt | 2 +- requirements/test.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 4ec843a6a8a..a8481672c85 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -36,7 +36,7 @@ brotli==1.1.0 ; platform_python_implementation == "CPython" # via -r requirements/runtime-deps.in build==1.2.1 # via pip-tools -certifi==2023.7.22 +certifi==2024.7.4 # via requests cffi==1.17.0 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 724b300db16..4602d3dafae 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -34,7 +34,7 @@ brotli==1.1.0 ; platform_python_implementation == "CPython" # via -r requirements/runtime-deps.in build==1.2.1 # via pip-tools -certifi==2023.7.22 +certifi==2024.7.4 # via requests cffi==1.17.0 # via diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index b76afe98d57..e06bb87946e 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -12,7 +12,7 @@ babel==2.12.1 # via sphinx blockdiag==3.0.0 # via sphinxcontrib-blockdiag -certifi==2023.7.22 +certifi==2024.7.4 # via requests charset-normalizer==3.3.1 # via requests diff --git a/requirements/doc.txt b/requirements/doc.txt index 0e5f7d49e0c..d77a13bb4ae 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -12,7 +12,7 @@ babel==2.12.1 # via sphinx blockdiag==3.0.0 # via sphinxcontrib-blockdiag -certifi==2023.7.22 +certifi==2024.7.4 # via requests charset-normalizer==3.3.1 # via requests diff --git a/requirements/lint.txt b/requirements/lint.txt index 491f273a010..f829c7a6a32 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -12,7 +12,7 @@ annotated-types==0.6.0 # via pydantic async-timeout==4.0.3 # via aioredis -certifi==2024.2.2 +certifi==2024.7.4 # via requests cffi==1.17.0 # via diff --git a/requirements/test.txt b/requirements/test.txt index aa27682ca5a..be6e210a80a 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -18,7 +18,7 @@ attrs==24.2.0 # via -r requirements/runtime-deps.in brotli==1.1.0 ; platform_python_implementation == "CPython" # via -r requirements/runtime-deps.in -certifi==2023.7.22 +certifi==2024.7.4 # via requests cffi==1.17.0 # via From 7e73d963cf04323eb5e0a5c890820709a9d3dca7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:09:55 +0000 Subject: [PATCH 025/296] Bump zipp from 3.17.0 to 3.20.0 (#8795) Bumps [zipp](https://github.com/jaraco/zipp) from 3.17.0 to 3.20.0.
Changelog

Sourced from zipp's changelog.

v3.20.0

Features

  • Made the zipfile compatibility overlay available as zipp.compat.overlay.

v3.19.3

Bugfixes

  • Also match directories in Path.glob. (#121)

v3.19.2

No significant changes.

v3.19.1

Bugfixes

  • Improved handling of malformed zip files. (#119)

v3.19.0

Features

  • Implement is_symlink. (#117)

v3.18.2

No significant changes.

v3.18.1

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=zipp&package-manager=pip&previous-version=3.17.0&new-version=3.20.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 a8481672c85..e3b0848a2de 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -282,7 +282,7 @@ wheel==0.37.0 # via pip-tools yarl==1.9.4 # via -r requirements/runtime-deps.in -zipp==3.17.0 +zipp==3.20.0 # via # importlib-metadata # importlib-resources diff --git a/requirements/dev.txt b/requirements/dev.txt index 4602d3dafae..16ed6eec20f 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -269,7 +269,7 @@ wheel==0.41.0 # via pip-tools yarl==1.9.4 # via -r requirements/runtime-deps.in -zipp==3.17.0 +zipp==3.20.0 # via # importlib-metadata # importlib-resources diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index e06bb87946e..3bb07da4364 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -88,7 +88,7 @@ urllib3==2.2.2 # via requests webcolors==1.13 # via blockdiag -zipp==3.17.0 +zipp==3.20.0 # via # importlib-metadata # importlib-resources diff --git a/requirements/doc.txt b/requirements/doc.txt index d77a13bb4ae..e6b913198f0 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -83,7 +83,7 @@ urllib3==2.2.2 # via requests webcolors==1.13 # via blockdiag -zipp==3.17.0 +zipp==3.20.0 # via # importlib-metadata # importlib-resources From 351e07bf134ded05b0fb3f90f6ca0abc0e1646a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:29:53 +0000 Subject: [PATCH 026/296] Bump virtualenv from 20.10.0 to 20.26.3 (#8794) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.10.0 to 20.26.3.
Release notes

Sourced from virtualenv's releases.

20.26.3

What's Changed

Full Changelog: https://github.com/pypa/virtualenv/compare/20.26.2...20.26.3

20.26.2

What's Changed

New Contributors

Full Changelog: https://github.com/pypa/virtualenv/compare/20.26.1...20.26.2

20.26.1

What's Changed

Full Changelog: https://github.com/pypa/virtualenv/compare/20.26.0...20.26.1

20.26.0

What's Changed

New Contributors

... (truncated)

Changelog

Sourced from virtualenv's changelog.

v20.26.3 (2024-06-21)

Bugfixes - 20.26.3

- Upgrade embedded wheels:
  • setuptools to 70.1.0 from 69.5.1
  • pip to 24.1 from 24.0 (:issue:2741)

v20.26.2 (2024-05-13)

Bugfixes - 20.26.2

  • virtualenv.pyz no longer fails when zipapp path contains a symlink - by :user:HandSonic and :user:petamas. (:issue:1949)
  • Fix bad return code from activate.sh if hashing is disabled - by :user:'fenkes-ibm'. (:issue:2717)

v20.26.1 (2024-04-29)

Bugfixes - 20.26.1

- fix PATH-based Python discovery on Windows - by
:user:`ofek`. (:issue:`2712`)

v20.26.0 (2024-04-23)

Bugfixes - 20.26.0

  • allow builtin discovery to discover specific interpreters (e.g. python3.12) given an unspecific spec (e.g. python3) - by :user:flying-sheep. (:issue:2709)

v20.25.3 (2024-04-17)

Bugfixes - 20.25.3

- Python 3.13.0a6 renamed pathmod to parser. (:issue:`2702`)

v20.25.2 (2024-04-16)

Bugfixes - 20.25.2

  • Upgrade embedded wheels:

    • setuptools of 69.1.0 to 69.5.1
    • wheel of 0.42.0 to 0.43.0 (:issue:2699)

v20.25.1 (2024-02-21)

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=virtualenv&package-manager=pip&previous-version=20.10.0&new-version=20.26.3)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 | 14 +++++--------- requirements/dev.txt | 2 +- requirements/lint.txt | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index e3b0848a2de..e00fcec36c0 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -28,8 +28,6 @@ attrs==24.2.0 # via -r requirements/runtime-deps.in babel==2.9.1 # via sphinx -backports-entry-points-selectable==1.3.0 - # via virtualenv blockdiag==3.0.0 # via sphinxcontrib-blockdiag brotli==1.1.0 ; platform_python_implementation == "CPython" @@ -66,13 +64,13 @@ cryptography==41.0.2 # trustme cython==3.0.11 # via -r requirements/cython.in -distlib==0.3.3 +distlib==0.3.8 # via virtualenv docutils==0.20.1 # via sphinx exceptiongroup==1.1.2 # via pytest -filelock==3.3.2 +filelock==3.15.4 # via virtualenv freezegun==1.5.1 # via @@ -138,7 +136,7 @@ pillow==9.5.0 # blockdiag pip-tools==7.4.1 # via -r requirements/dev.in -platformdirs==2.4.0 +platformdirs==4.2.2 # via virtualenv pluggy==1.5.0 # via pytest @@ -202,9 +200,7 @@ requests==2.31.0 setuptools-git==1.2 # via -r requirements/test.in six==1.16.0 - # via - # python-dateutil - # virtualenv + # via python-dateutil slotscheck==0.19.0 # via -r requirements/lint.in snowballstemmer==2.2.0 @@ -272,7 +268,7 @@ uvloop==0.20.0 ; platform_system != "Windows" # via # -r requirements/base.in # -r requirements/lint.in -virtualenv==20.10.0 +virtualenv==20.26.3 # via pre-commit wait-for-it==2.2.2 # via -r requirements/test.in diff --git a/requirements/dev.txt b/requirements/dev.txt index 16ed6eec20f..38ab17a7cc4 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -259,7 +259,7 @@ uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpytho # via # -r requirements/base.in # -r requirements/lint.in -virtualenv==20.24.2 +virtualenv==20.26.3 # via pre-commit wait-for-it==2.2.2 # via -r requirements/test.in diff --git a/requirements/lint.txt b/requirements/lint.txt index f829c7a6a32..3ad484485de 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -119,7 +119,7 @@ urllib3==2.2.2 # via requests uvloop==0.20.0 ; platform_system != "Windows" # via -r requirements/lint.in -virtualenv==20.24.2 +virtualenv==20.26.3 # via pre-commit # The following packages are considered to be unsafe in a requirements file: From 5ee29e728fff604249c4b37f3af7eef3e862928e Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:47:29 +0100 Subject: [PATCH 027/296] [PR #8776/11171b8d backport][3.11] Use more precise headers type (#8778) **This is a backport of PR #8776 as merged into master (11171b8d4dc269cc05c59befc105b6744f2640e3).** --------- Co-authored-by: Sam Bull --- CHANGES/8768.bugfix.rst | 1 + aiohttp/client_exceptions.py | 6 ++++-- tests/test_client_exceptions.py | 12 +++++++----- 3 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 CHANGES/8768.bugfix.rst diff --git a/CHANGES/8768.bugfix.rst b/CHANGES/8768.bugfix.rst new file mode 100644 index 00000000000..18512163572 --- /dev/null +++ b/CHANGES/8768.bugfix.rst @@ -0,0 +1 @@ +Used more precise type for ``ClientResponseError.headers``, fixing some type errors when using them -- by :user:`Dreamorcerer`. diff --git a/aiohttp/client_exceptions.py b/aiohttp/client_exceptions.py index ff29b3d3ca9..36bb6d1c0d8 100644 --- a/aiohttp/client_exceptions.py +++ b/aiohttp/client_exceptions.py @@ -4,8 +4,10 @@ import warnings from typing import TYPE_CHECKING, Optional, Tuple, Union +from multidict import MultiMapping + from .http_parser import RawResponseMessage -from .typedefs import LooseHeaders, StrOrURL +from .typedefs import StrOrURL try: import ssl @@ -71,7 +73,7 @@ def __init__( code: Optional[int] = None, status: Optional[int] = None, message: str = "", - headers: Optional[LooseHeaders] = None, + headers: Optional[MultiMapping[str]] = None, ) -> None: self.request_info = request_info if code is not None: diff --git a/tests/test_client_exceptions.py b/tests/test_client_exceptions.py index d863d6674a3..85e71a3508b 100644 --- a/tests/test_client_exceptions.py +++ b/tests/test_client_exceptions.py @@ -5,6 +5,7 @@ from unittest import mock import pytest +from multidict import CIMultiDict from yarl import URL from aiohttp import client, client_reqrep @@ -44,7 +45,7 @@ def test_pickle(self) -> None: history=(), status=400, message="Something wrong", - headers={}, + headers=CIMultiDict(foo="bar"), ) err.foo = "bar" for proto in range(pickle.HIGHEST_PROTOCOL + 1): @@ -54,7 +55,8 @@ def test_pickle(self) -> None: assert err2.history == () assert err2.status == 400 assert err2.message == "Something wrong" - assert err2.headers == {} + # Use headers.get() to verify static type is correct. + assert err2.headers.get("foo") == "bar" assert err2.foo == "bar" def test_repr(self) -> None: @@ -66,11 +68,11 @@ def test_repr(self) -> None: history=(), status=400, message="Something wrong", - headers={}, + headers=CIMultiDict(), ) assert repr(err) == ( "ClientResponseError(%r, (), status=400, " - "message='Something wrong', headers={})" % (self.request_info,) + "message='Something wrong', headers=)" % (self.request_info,) ) def test_str(self) -> None: @@ -79,7 +81,7 @@ def test_str(self) -> None: history=(), status=400, message="Something wrong", - headers={}, + headers=CIMultiDict(), ) assert str(err) == ( "400, message='Something wrong', " "url='http://example.com'" From 014db7e853198d9124d5d6915416231d3acee7c9 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:47:43 +0100 Subject: [PATCH 028/296] [PR #8776/11171b8d backport][3.10] Use more precise headers type (#8777) **This is a backport of PR #8776 as merged into master (11171b8d4dc269cc05c59befc105b6744f2640e3).** --------- Co-authored-by: Sam Bull --- CHANGES/8768.bugfix.rst | 1 + aiohttp/client_exceptions.py | 6 ++++-- tests/test_client_exceptions.py | 12 +++++++----- 3 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 CHANGES/8768.bugfix.rst diff --git a/CHANGES/8768.bugfix.rst b/CHANGES/8768.bugfix.rst new file mode 100644 index 00000000000..18512163572 --- /dev/null +++ b/CHANGES/8768.bugfix.rst @@ -0,0 +1 @@ +Used more precise type for ``ClientResponseError.headers``, fixing some type errors when using them -- by :user:`Dreamorcerer`. diff --git a/aiohttp/client_exceptions.py b/aiohttp/client_exceptions.py index ff29b3d3ca9..36bb6d1c0d8 100644 --- a/aiohttp/client_exceptions.py +++ b/aiohttp/client_exceptions.py @@ -4,8 +4,10 @@ import warnings from typing import TYPE_CHECKING, Optional, Tuple, Union +from multidict import MultiMapping + from .http_parser import RawResponseMessage -from .typedefs import LooseHeaders, StrOrURL +from .typedefs import StrOrURL try: import ssl @@ -71,7 +73,7 @@ def __init__( code: Optional[int] = None, status: Optional[int] = None, message: str = "", - headers: Optional[LooseHeaders] = None, + headers: Optional[MultiMapping[str]] = None, ) -> None: self.request_info = request_info if code is not None: diff --git a/tests/test_client_exceptions.py b/tests/test_client_exceptions.py index d863d6674a3..85e71a3508b 100644 --- a/tests/test_client_exceptions.py +++ b/tests/test_client_exceptions.py @@ -5,6 +5,7 @@ from unittest import mock import pytest +from multidict import CIMultiDict from yarl import URL from aiohttp import client, client_reqrep @@ -44,7 +45,7 @@ def test_pickle(self) -> None: history=(), status=400, message="Something wrong", - headers={}, + headers=CIMultiDict(foo="bar"), ) err.foo = "bar" for proto in range(pickle.HIGHEST_PROTOCOL + 1): @@ -54,7 +55,8 @@ def test_pickle(self) -> None: assert err2.history == () assert err2.status == 400 assert err2.message == "Something wrong" - assert err2.headers == {} + # Use headers.get() to verify static type is correct. + assert err2.headers.get("foo") == "bar" assert err2.foo == "bar" def test_repr(self) -> None: @@ -66,11 +68,11 @@ def test_repr(self) -> None: history=(), status=400, message="Something wrong", - headers={}, + headers=CIMultiDict(), ) assert repr(err) == ( "ClientResponseError(%r, (), status=400, " - "message='Something wrong', headers={})" % (self.request_info,) + "message='Something wrong', headers=)" % (self.request_info,) ) def test_str(self) -> None: @@ -79,7 +81,7 @@ def test_str(self) -> None: history=(), status=400, message="Something wrong", - headers={}, + headers=CIMultiDict(), ) assert str(err) == ( "400, message='Something wrong', " "url='http://example.com'" From ae15bac3503cc22a85412054f1abf4363cc74a9c Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 20 Aug 2024 22:06:03 +0100 Subject: [PATCH 029/296] Drop Python 3.8 (#8797) (#8799) (cherry picked from commit 5be5af3c900ef9ead3387a1193fc4ff4ad1e5594) --- .codecov.yml | 2 +- .github/workflows/ci-cd.yml | 4 +--- CHANGES/8797.breaking.rst | 1 + Makefile | 6 +----- aiohttp/cookiejar.py | 2 +- aiohttp/resolver.py | 6 ++---- aiohttp/web_fileresponse.py | 4 ---- setup.cfg | 3 +-- setup.py | 4 ++-- tests/test_resolver.py | 7 ------- 10 files changed, 10 insertions(+), 29 deletions(-) create mode 100644 CHANGES/8797.breaking.rst diff --git a/.codecov.yml b/.codecov.yml index 30809053e16..e21d45ac7b2 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,5 +1,5 @@ codecov: - branch: 3.9 + branch: master notify: after_n_builds: 13 diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index a6a58cef9c2..d422a269f02 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -125,15 +125,13 @@ jobs: needs: gen_llhttp strategy: matrix: - pyver: [3.8, 3.9, '3.10', '3.11', '3.12'] + pyver: [3.9, '3.10', '3.11', '3.12'] no-extensions: ['', 'Y'] os: [ubuntu, macos, windows] experimental: [false] exclude: - os: macos no-extensions: 'Y' - - os: macos - pyver: 3.8 - os: windows no-extensions: 'Y' include: diff --git a/CHANGES/8797.breaking.rst b/CHANGES/8797.breaking.rst new file mode 100644 index 00000000000..c219ea3d264 --- /dev/null +++ b/CHANGES/8797.breaking.rst @@ -0,0 +1 @@ +Dropped support for Python 3.8 -- by :user:`Dreamsorcerer`. diff --git a/Makefile b/Makefile index bb2d437a134..2a40be049ee 100644 --- a/Makefile +++ b/Makefile @@ -112,11 +112,7 @@ define run_tests_in_docker docker run --rm -ti -v `pwd`:/src -w /src "aiohttp-test-$(1)-$(2)" $(TEST_SPEC) endef -.PHONY: test-3.8-no-extensions test-3.8 test-3.9-no-extensions test -test-3.8-no-extensions: - $(call run_tests_in_docker,3.8,y) -test-3.8: - $(call run_tests_in_docker,3.8,n) +.PHONY: test-3.9-no-extensions test test-3.9-no-extensions: $(call run_tests_in_docker,3.9,y) test-3.9: diff --git a/aiohttp/cookiejar.py b/aiohttp/cookiejar.py index e9997ce2935..e3eefc9c656 100644 --- a/aiohttp/cookiejar.py +++ b/aiohttp/cookiejar.py @@ -70,7 +70,7 @@ class CookieJar(AbstractCookieJar): except (OSError, ValueError): # Hit the maximum representable time on Windows # https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-localtime32-localtime64 - # Throws ValueError on PyPy 3.8 and 3.9, OSError elsewhere + # Throws ValueError on PyPy 3.9, OSError elsewhere MAX_TIME = calendar.timegm((3000, 12, 31, 23, 59, 59, -1, -1, -1)) except OverflowError: # #4515: datetime.max may not be representable on 32-bit platforms diff --git a/aiohttp/resolver.py b/aiohttp/resolver.py index 10e36266abe..c8fce5b5706 100644 --- a/aiohttp/resolver.py +++ b/aiohttp/resolver.py @@ -1,6 +1,5 @@ import asyncio import socket -import sys from typing import Any, Dict, List, Optional, Tuple, Type, Union from .abc import AbstractResolver, ResolveResult @@ -18,7 +17,6 @@ _NUMERIC_SOCKET_FLAGS = socket.AI_NUMERICHOST | socket.AI_NUMERICSERV -_SUPPORTS_SCOPE_ID = sys.version_info >= (3, 9, 0) class ThreadedResolver(AbstractResolver): @@ -49,7 +47,7 @@ async def resolve( # IPv6 is not supported by Python build, # or IPv6 is not enabled in the host continue - if address[3] and _SUPPORTS_SCOPE_ID: + if address[3]: # This is essential for link-local IPv6 addresses. # LL IPv6 is a VERY rare case. Strictly speaking, we should use # getnameinfo() unconditionally, but performance makes sense. @@ -116,7 +114,7 @@ async def resolve( address: Union[Tuple[bytes, int], Tuple[bytes, int, int, int]] = node.addr family = node.family if family == socket.AF_INET6: - if len(address) > 3 and address[3] and _SUPPORTS_SCOPE_ID: + if len(address) > 3 and address[3]: # This is essential for link-local IPv6 addresses. # LL IPv6 is a VERY rare case. Strictly speaking, we should use # getnameinfo() unconditionally, but performance makes sense. diff --git a/aiohttp/web_fileresponse.py b/aiohttp/web_fileresponse.py index 0c23e375d25..2c253e03b0a 100644 --- a/aiohttp/web_fileresponse.py +++ b/aiohttp/web_fileresponse.py @@ -1,7 +1,6 @@ import asyncio import os import pathlib -import sys from contextlib import suppress from mimetypes import MimeTypes from stat import S_ISREG @@ -48,9 +47,6 @@ CONTENT_TYPES: Final[MimeTypes] = MimeTypes() -if sys.version_info < (3, 9): - CONTENT_TYPES.encodings_map[".br"] = "br" - # File extension to IANA encodings map that will be checked in the order defined. ENCODING_EXTENSIONS = MappingProxyType( {ext: CONTENT_TYPES.encodings_map[ext] for ext in (".br", ".gz")} diff --git a/setup.cfg b/setup.cfg index cfd1be5610f..03fb594ecbe 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,7 +33,6 @@ classifiers = Programming Language :: Python Programming Language :: Python :: 3 - Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 @@ -42,7 +41,7 @@ classifiers = Topic :: Internet :: WWW/HTTP [options] -python_requires = >=3.8 +python_requires = >=3.9 packages = aiohttp # https://setuptools.readthedocs.io/en/latest/setuptools.html#setting-the-zip-safe-flag zip_safe = False diff --git a/setup.py b/setup.py index 23ac86f9b4a..808f539d259 100644 --- a/setup.py +++ b/setup.py @@ -4,8 +4,8 @@ from setuptools import Extension, setup -if sys.version_info < (3, 8): - raise RuntimeError("aiohttp 3.x requires Python 3.8+") +if sys.version_info < (3, 9): + raise RuntimeError("aiohttp 3.x requires Python 3.9+") NO_EXTENSIONS: bool = bool(os.environ.get("AIOHTTP_NO_EXTENSIONS")) diff --git a/tests/test_resolver.py b/tests/test_resolver.py index f51506a6999..825db81e41b 100644 --- a/tests/test_resolver.py +++ b/tests/test_resolver.py @@ -9,7 +9,6 @@ from aiohttp.resolver import ( _NUMERIC_SOCKET_FLAGS, - _SUPPORTS_SCOPE_ID, AsyncResolver, DefaultResolver, ThreadedResolver, @@ -136,9 +135,6 @@ async def test_async_resolver_positive_ipv4_lookup(loop: Any) -> None: @pytest.mark.skipif(not getaddrinfo, reason="aiodns >=3.2.0 required") -@pytest.mark.skipif( - not _SUPPORTS_SCOPE_ID, reason="python version does not support scope id" -) async def test_async_resolver_positive_link_local_ipv6_lookup(loop: Any) -> None: with patch("aiodns.DNSResolver") as mock: mock().getaddrinfo.return_value = fake_aiodns_getaddrinfo_ipv6_result( @@ -211,9 +207,6 @@ async def test_threaded_resolver_positive_lookup() -> None: ipaddress.ip_address(real[0]["host"]) -@pytest.mark.skipif( - not _SUPPORTS_SCOPE_ID, reason="python version does not support scope id" -) async def test_threaded_resolver_positive_ipv6_link_local_lookup() -> None: loop = Mock() loop.getaddrinfo = fake_ipv6_addrinfo(["fe80::1"]) From cace1d15df77102b838196b3c53b589a957b335d Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 00:48:09 +0100 Subject: [PATCH 030/296] [PR #8800/17bf9127 backport][3.11] Upload junit to codecov (#8802) **This is a backport of PR #8800 as merged into master (17bf912743708b18cc4737182a2c2e286c312f4d).** Co-authored-by: Sam Bull --- .github/workflows/ci-cd.yml | 8 +++++++- setup.cfg | 1 - 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index d422a269f02..bb4df9b7760 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -196,7 +196,7 @@ jobs: PIP_USER: 1 run: >- PATH="${HOME}/Library/Python/3.11/bin:${HOME}/.local/bin:${PATH}" - pytest + pytest --junitxml=junit.xml shell: bash - name: Re-run the failing tests with maximum verbosity if: failure() @@ -232,6 +232,12 @@ jobs: steps.python-install.outputs.python-version }} token: ${{ secrets.CODECOV_TOKEN }} + - name: Upload test results to Codecov + if: ${{ !cancelled() }} + uses: codecov/test-results-action@v1 + with: + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} check: # This job does nothing and is only used for the branch protection if: always() diff --git a/setup.cfg b/setup.cfg index 03fb594ecbe..4000b5a40a7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -167,7 +167,6 @@ junit_suite_name = aiohttp_test_suite norecursedirs = dist docs build .tox .eggs minversion = 3.8.2 testpaths = tests/ -junit_family=xunit2 xfail_strict = true markers = dev_mode: mark test to run in dev mode. From 0ae4fa3d2f480c53fec7147c497a0b598d70a012 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 00:48:33 +0100 Subject: [PATCH 031/296] [PR #8800/17bf9127 backport][3.10] Upload junit to codecov (#8801) **This is a backport of PR #8800 as merged into master (17bf912743708b18cc4737182a2c2e286c312f4d).** Co-authored-by: Sam Bull --- .github/workflows/ci-cd.yml | 8 +++++++- setup.cfg | 1 - 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index a6a58cef9c2..8e56acb497d 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -198,7 +198,7 @@ jobs: PIP_USER: 1 run: >- PATH="${HOME}/Library/Python/3.11/bin:${HOME}/.local/bin:${PATH}" - pytest + pytest --junitxml=junit.xml shell: bash - name: Re-run the failing tests with maximum verbosity if: failure() @@ -234,6 +234,12 @@ jobs: steps.python-install.outputs.python-version }} token: ${{ secrets.CODECOV_TOKEN }} + - name: Upload test results to Codecov + if: ${{ !cancelled() }} + uses: codecov/test-results-action@v1 + with: + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} check: # This job does nothing and is only used for the branch protection if: always() diff --git a/setup.cfg b/setup.cfg index cfd1be5610f..71ed6b98e0e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -168,7 +168,6 @@ junit_suite_name = aiohttp_test_suite norecursedirs = dist docs build .tox .eggs minversion = 3.8.2 testpaths = tests/ -junit_family=xunit2 xfail_strict = true markers = dev_mode: mark test to run in dev mode. From 35b293b68295c6ce0289746f64835f6bae9e6205 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:03:26 +0000 Subject: [PATCH 032/296] Bump annotated-types from 0.5.0 to 0.7.0 (#8814) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [annotated-types](https://github.com/annotated-types/annotated-types) from 0.5.0 to 0.7.0.
Release notes

Sourced from annotated-types's releases.

v0.7.0

What's Changed

New Contributors

Full Changelog: https://github.com/annotated-types/annotated-types/compare/v0.6.0...v0.7.0

v0.6.0

What's Changed

New Contributors

Full Changelog: https://github.com/annotated-types/annotated-types/compare/v0.5.0...v0.6.0

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=annotated-types&package-manager=pip&previous-version=0.5.0&new-version=0.7.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index e00fcec36c0..c9422ac7d86 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -18,7 +18,7 @@ aiosignal==1.3.1 # via -r requirements/runtime-deps.in alabaster==0.7.12 # via sphinx -annotated-types==0.5.0 +annotated-types==0.7.0 # via pydantic async-timeout==4.0.3 ; python_version < "3.11" # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 38ab17a7cc4..c52e9d42cd8 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -18,7 +18,7 @@ aiosignal==1.3.1 # via -r requirements/runtime-deps.in alabaster==0.7.13 # via sphinx -annotated-types==0.5.0 +annotated-types==0.7.0 # via pydantic async-timeout==4.0.3 ; python_version < "3.11" # via diff --git a/requirements/lint.txt b/requirements/lint.txt index 3ad484485de..9b5ac722a95 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -8,7 +8,7 @@ aiodns==3.2.0 # via -r requirements/lint.in aioredis==2.0.1 # via -r requirements/lint.in -annotated-types==0.6.0 +annotated-types==0.7.0 # via pydantic async-timeout==4.0.3 # via aioredis diff --git a/requirements/test.txt b/requirements/test.txt index be6e210a80a..05939335770 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -10,7 +10,7 @@ aiohappyeyeballs==2.4.0 # via -r requirements/runtime-deps.in aiosignal==1.3.1 # via -r requirements/runtime-deps.in -annotated-types==0.5.0 +annotated-types==0.7.0 # via pydantic async-timeout==4.0.3 ; python_version < "3.11" # via -r requirements/runtime-deps.in From 4bab25717c1e9eac1e0181cb88beb5935757dedc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:03:31 +0000 Subject: [PATCH 033/296] Bump incremental from 22.10.0 to 24.7.2 (#8815) Bumps [incremental](https://github.com/twisted/incremental) from 22.10.0 to 24.7.2.
Changelog

Sourced from incremental's changelog.

Incremental 24.7.2 (2024-07-29)

Bugfixes

  • Incremental could mis-identify that a project had opted in to version management.

    If a pyproject.toml in the current directory contained a [project] table with a name key, but did not contain the opt-in [tool.incremental] table, Incremental would still treat the file as if the opt-in were present and attempt to validate the configuration. This could happen in contexts outside of packaging, such as when creating a virtualenv. When operating as a setuptools plugin Incremental now always ignores invalid configuration, such as configuration that doesn't match the content of the working directory. ([#106](https://github.com/twisted/incremental/issues/106) <https://github.com/twisted/incremental/issues/106>__)

Incremental 24.7.1 (2024-07-27)

Bugfixes

  • Incremental 24.7.0 would produce an error when parsing the pyproject.toml of a project that lacked the use_incremental=True or [tool.incremental] opt-in markers if that file lacked a [project] section containing the package name. This could cause a project that only uses pyproject.toml to configure tools to fail to build if Incremental is installed. Incremental now ignores such projects. ([#100](https://github.com/twisted/incremental/issues/100) <https://github.com/twisted/incremental/issues/100>__)

Misc

  • [#101](https://github.com/twisted/incremental/issues/101) <https://github.com/twisted/incremental/issues/101>__

Incremental 24.7.0 (2024-07-25)

Features

  • Incremental can now be configured using pyproject.toml. ([#90](https://github.com/twisted/incremental/issues/90) <https://github.com/twisted/incremental/issues/90>__)
  • Incremental now provides a read-only Hatchling version source plugin <https://hatch.pypa.io/latest/plugins/version-source/reference/>_. ([#93](https://github.com/twisted/incremental/issues/93) <https://github.com/twisted/incremental/issues/93>__)

Bugfixes

  • Incremental no longer inserts a dot before the rc version component (i.e., 1.2.3rc1 instead of 1.2.3.rc1), resulting in version numbers in the canonical format <https://packaging.python.org/en/latest/specifications/version-specifiers/#public-version-identifiers>. ([#81](https://github.com/twisted/incremental/issues/81) <https://github.com/twisted/incremental/issues/81>)
  • Incremental's tests are now included in the sdist release artifact. ([#80](https://github.com/twisted/incremental/issues/80) <https://github.com/twisted/incremental/issues/80>__)

Deprecations and Removals

  • incremental[scripts] no longer depends on Twisted. ([#88](https://github.com/twisted/incremental/issues/88) <https://github.com/twisted/incremental/issues/88>__)
  • Support for Python 2.7 has been dropped for lack of test infrastructure. We no longer provide universal wheels. ([#86](https://github.com/twisted/incremental/issues/86) <https://github.com/twisted/incremental/issues/86>__)
  • Support for Python 3.5, 3.6, and 3.7 has been dropped for lack of test infrastructure. ([#92](https://github.com/twisted/incremental/issues/92) <https://github.com/twisted/incremental/issues/92>__)
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=incremental&package-manager=pip&previous-version=22.10.0&new-version=24.7.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 | 4 +++- requirements/dev.txt | 4 +++- requirements/doc-spelling.txt | 10 +++++++--- requirements/doc.txt | 10 +++++++--- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index c9422ac7d86..5e1ac3999f4 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -101,7 +101,7 @@ importlib-metadata==7.0.0 # sphinx importlib-resources==6.1.1 # via towncrier -incremental==22.10.0 +incremental==24.7.2 # via towncrier iniconfig==1.1.1 # via pytest @@ -234,6 +234,7 @@ tomli==2.0.1 # build # cherry-picker # coverage + # incremental # mypy # pip-tools # pyproject-hooks @@ -289,4 +290,5 @@ pip==23.2.1 setuptools==68.0.0 # via # blockdiag + # incremental # pip-tools diff --git a/requirements/dev.txt b/requirements/dev.txt index c52e9d42cd8..afa5b28559c 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -99,7 +99,7 @@ importlib-metadata==7.0.0 # sphinx importlib-resources==6.1.1 # via towncrier -incremental==22.10.0 +incremental==24.7.2 # via towncrier iniconfig==2.0.0 # via pytest @@ -224,6 +224,7 @@ tomli==2.0.1 # build # cherry-picker # coverage + # incremental # mypy # pip-tools # pyproject-hooks @@ -280,5 +281,6 @@ pip==23.2.1 setuptools==68.0.0 # via # blockdiag + # incremental # nodeenv # pip-tools diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 3bb07da4364..a5e70f9a9d2 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -30,7 +30,7 @@ importlib-metadata==6.8.0 # via sphinx importlib-resources==6.1.1 # via towncrier -incremental==22.10.0 +incremental==24.7.2 # via towncrier jinja2==3.1.4 # via @@ -79,7 +79,9 @@ sphinxcontrib-spelling==8.0.0 ; platform_system != "Windows" sphinxcontrib-towncrier==0.4.0a0 # via -r requirements/doc.in tomli==2.0.1 - # via towncrier + # via + # incremental + # towncrier towncrier==23.11.0 # via # -r requirements/doc.in @@ -95,4 +97,6 @@ zipp==3.20.0 # The following packages are considered to be unsafe in a requirements file: setuptools==68.0.0 - # via blockdiag + # via + # blockdiag + # incremental diff --git a/requirements/doc.txt b/requirements/doc.txt index e6b913198f0..1d0cc15cb21 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -30,7 +30,7 @@ importlib-metadata==6.8.0 # via sphinx importlib-resources==6.1.1 # via towncrier -incremental==22.10.0 +incremental==24.7.2 # via towncrier jinja2==3.1.4 # via @@ -74,7 +74,9 @@ sphinxcontrib-serializinghtml==1.1.5 sphinxcontrib-towncrier==0.4.0a0 # via -r requirements/doc.in tomli==2.0.1 - # via towncrier + # via + # incremental + # towncrier towncrier==23.11.0 # via # -r requirements/doc.in @@ -90,4 +92,6 @@ zipp==3.20.0 # The following packages are considered to be unsafe in a requirements file: setuptools==68.0.0 - # via blockdiag + # via + # blockdiag + # incremental From ec5c265c26899196dc0586b67564d60a941cb4a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:03:57 +0000 Subject: [PATCH 034/296] Bump packaging from 21.2 to 24.1 (#8817) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [packaging](https://github.com/pypa/packaging) from 21.2 to 24.1.
Release notes

Sourced from packaging's releases.

24.1

What's Changed

New Contributors

Full Changelog: https://github.com/pypa/packaging/compare/24.0...24.1

24.0

What's Changed

New Contributors

Full Changelog: https://github.com/pypa/packaging/compare/23.2...24.0

23.2

What's Changed

... (truncated)

Changelog

Sourced from packaging's changelog.

24.1 - 2024-06-10


No unreleased changes.

24.0 - 2024-03-10

  • Do specifier matching correctly when the specifier contains an epoch number and has more components than the version (:issue:683)
  • Support the experimental --disable-gil builds in packaging.tags (:issue:727)
  • BREAKING: Make optional metadata.Metadata attributes default to None (:issue:733)
  • Fix errors when trying to access the description_content_type, keywords, and requires_python attributes on metadata.Metadata when those values have not been provided (:issue:733)
  • Fix a bug preventing the use of the built in ExceptionGroup on versions of Python that support it (:issue:725)

23.2 - 2023-10-01


* Document calendar-based versioning scheme (:issue:`716`)
* Enforce that the entire marker string is parsed (:issue:`687`)
* Requirement parsing no longer automatically validates the URL
(:issue:`120`)
* Canonicalize names for requirements comparison (:issue:`644`)
* Introduce ``metadata.Metadata`` (along with
``metadata.ExceptionGroup`` and ``metadata.InvalidMetadata``;
:issue:`570`)
* Introduce the ``validate`` keyword parameter to
``utils.normalize_name()`` (:issue:`570`)
* Introduce ``utils.is_normalized_name()`` (:issue:`570`)
* Make ``utils.parse_sdist_filename()`` and
``utils.parse_wheel_filename()``
raise ``InvalidSdistFilename`` and ``InvalidWheelFilename``,
respectively,
  when the version component of the name is invalid
* Remove support for Python 3.7 (:issue:`783`)

23.1 - 2023-04-12

  • Parse raw metadata (:issue:671)
  • Import underlying parser functions as an underscored variable (:issue:663)
  • Improve error for local version label with unsupported operators (:issue:675)
  • Add dedicated error for specifiers with incorrect .* suffix
  • Replace spaces in platform names with underscores (:issue:620)
  • Relax typing of _key on _BaseVersion (:issue:669)
  • Handle prefix match with zeros at end of prefix correctly (:issue:674)

23.0 - 2023-01-08


* Allow ``"extra"`` to be ``None`` in the marker environment
(:issue:`650`)
* Refactor ``tags._generic_api`` to use ``EXT_SUFFIX`` (:issue:`607`)
</tr></table>

... (truncated)

Commits
  • 85442b8 Bump for release
  • 3e67fc7 Work around platform.python_version() returning non PEP 440 compliant versi...
  • 32deafe Bump the github-actions group with 3 updates (#789)
  • e0dda88 Document markers.default_environment() (#753)
  • cc938f9 Modernise type annotations using FA rules from ruff (#785)
  • 757f559 Fix typo in _parser docstring (#784)
  • ec9f203 Bump the github-actions group with 4 updates (#782)
  • 5cbe1e4 Add support for Python 3.13 and drop EOL 3.7 (#783)
  • cb8fd38 pyupgrade/black/isort/flake8 → ruff (#769)
  • e8002b1 Bump for development
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=packaging&package-manager=pip&previous-version=21.2&new-version=24.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- requirements/constraints.txt | 4 +--- requirements/dev.txt | 2 +- requirements/doc-spelling.txt | 2 +- requirements/doc.txt | 2 +- requirements/lint.txt | 2 +- requirements/test.txt | 2 +- 7 files changed, 7 insertions(+), 9 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index 3a616ad4d14..113be2767ec 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -30,7 +30,7 @@ multidict==6.0.5 # via # -r requirements/runtime-deps.in # yarl -packaging==23.1 +packaging==24.1 # via gunicorn pycares==4.4.0 # via aiodns diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 5e1ac3999f4..5ceac3ecb5e 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -124,7 +124,7 @@ mypy-extensions==1.0.0 # via mypy nodeenv==1.6.0 # via pre-commit -packaging==21.2 +packaging==24.1 # via # build # gunicorn @@ -160,8 +160,6 @@ pyjwt==2.3.0 # via # gidgethub # pyjwt -pyparsing==2.4.7 - # via packaging pyproject-hooks==1.0.0 # via # build diff --git a/requirements/dev.txt b/requirements/dev.txt index afa5b28559c..06897d1f6a8 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -121,7 +121,7 @@ mypy-extensions==1.0.0 # via mypy nodeenv==1.8.0 # via pre-commit -packaging==23.1 +packaging==24.1 # via # build # gunicorn diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index a5e70f9a9d2..916cee3c0cb 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -38,7 +38,7 @@ jinja2==3.1.4 # towncrier markupsafe==2.1.3 # via jinja2 -packaging==23.1 +packaging==24.1 # via sphinx pillow==9.5.0 # via diff --git a/requirements/doc.txt b/requirements/doc.txt index 1d0cc15cb21..0038be971e1 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -38,7 +38,7 @@ jinja2==3.1.4 # towncrier markupsafe==2.1.3 # via jinja2 -packaging==23.1 +packaging==24.1 # via sphinx pillow==9.5.0 # via diff --git a/requirements/lint.txt b/requirements/lint.txt index 9b5ac722a95..54ad2cc4322 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -54,7 +54,7 @@ mypy-extensions==1.0.0 # via mypy nodeenv==1.8.0 # via pre-commit -packaging==23.1 +packaging==24.1 # via pytest platformdirs==3.10.0 # via virtualenv diff --git a/requirements/test.txt b/requirements/test.txt index 05939335770..344fed3e747 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -61,7 +61,7 @@ mypy==1.11.1 ; implementation_name == "cpython" # via -r requirements/test.in mypy-extensions==1.0.0 # via mypy -packaging==23.1 +packaging==24.1 # via # gunicorn # pytest From de76808f1d29cb799f365b2655d5d3ef46a49a4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:04:00 +0000 Subject: [PATCH 035/296] Bump pip from 23.2.1 to 24.2 (#8816) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [//]: # (dependabot-start) ⚠️ **Dependabot is rebasing this PR** ⚠️ Rebasing might not happen immediately, so don't worry if this takes some time. Note: if you make any changes to this PR yourself, they will take precedence over the rebase. --- [//]: # (dependabot-end) Bumps [pip](https://github.com/pypa/pip) from 23.2.1 to 24.2.
Changelog

Sourced from pip's changelog.

24.2 (2024-07-28)

Deprecations and Removals

  • Deprecate pip install --editable falling back to setup.py develop when using a setuptools version that does not support :pep:660 (setuptools v63 and older). ([#11457](https://github.com/pypa/pip/issues/11457) <https://github.com/pypa/pip/issues/11457>_)

Features

  • Check unsupported packages for the current platform. ([#11054](https://github.com/pypa/pip/issues/11054) <https://github.com/pypa/pip/issues/11054>_)

  • Use system certificates and certifi certificates to verify HTTPS connections on Python 3.10+. Python 3.9 and earlier only use certifi.

    To revert to previous behaviour, pass the flag --use-deprecated=legacy-certs. ([#11647](https://github.com/pypa/pip/issues/11647) <https://github.com/pypa/pip/issues/11647>_)

  • Improve discovery performance of installed packages when the importlib.metadata backend is used to load distribution metadata (used by default under Python 3.11+). ([#12656](https://github.com/pypa/pip/issues/12656) <https://github.com/pypa/pip/issues/12656>_)

  • Improve performance when the same requirement string appears many times during resolution, by consistently caching the parsed requirement string. ([#12663](https://github.com/pypa/pip/issues/12663) <https://github.com/pypa/pip/issues/12663>_)

  • Minor performance improvement of finding applicable package candidates by not repeatedly calculating their versions ([#12664](https://github.com/pypa/pip/issues/12664) <https://github.com/pypa/pip/issues/12664>_)

  • Disable pip's self version check when invoking a pip subprocess to install PEP 517 build requirements. ([#12683](https://github.com/pypa/pip/issues/12683) <https://github.com/pypa/pip/issues/12683>_)

  • Improve dependency resolution performance by caching platform compatibility tags during wheel cache lookup. ([#12712](https://github.com/pypa/pip/issues/12712) <https://github.com/pypa/pip/issues/12712>_)

  • wheel is no longer explicitly listed as a build dependency of pip. setuptools injects this dependency in the get_requires_for_build_wheel() hook and no longer needs it on newer versions. ([#12728](https://github.com/pypa/pip/issues/12728) <https://github.com/pypa/pip/issues/12728>_)

  • Ignore --require-virtualenv for pip check and pip freeze ([#12842](https://github.com/pypa/pip/issues/12842) <https://github.com/pypa/pip/issues/12842>_)

  • Improve package download and install performance.

    Increase chunk sizes when downloading (256 kB, up from 10 kB) and reading files (1 MB, up from 8 kB). This reduces the frequency of updates to pip's progress bar. ([#12810](https://github.com/pypa/pip/issues/12810) <https://github.com/pypa/pip/issues/12810>_)

  • Improve pip install performance.

    Files are now extracted in 1MB blocks, or in one block matching the file size for smaller files. A decompressor is no longer instantiated when extracting 0 bytes files, it is not necessary because there is no data to decompress. ([#12803](https://github.com/pypa/pip/issues/12803) <https://github.com/pypa/pip/issues/12803>_)

Bug Fixes

  • Set no_color to global rich.Console instance. ([#11045](https://github.com/pypa/pip/issues/11045) <https://github.com/pypa/pip/issues/11045>_)
  • Fix resolution to respect --python-version when checking Requires-Python. ([#12216](https://github.com/pypa/pip/issues/12216) <https://github.com/pypa/pip/issues/12216>_)
  • Perform hash comparisons in a case-insensitive manner. ([#12680](https://github.com/pypa/pip/issues/12680) <https://github.com/pypa/pip/issues/12680>_)
  • Avoid dlopen failure for glibc detection in musl builds ([#12716](https://github.com/pypa/pip/issues/12716) <https://github.com/pypa/pip/issues/12716>_)
  • Avoid keyring logging crashes when pip is run in verbose mode. ([#12751](https://github.com/pypa/pip/issues/12751) <https://github.com/pypa/pip/issues/12751>_)

... (truncated)

Commits
  • 97146c7 Bump for release
  • ef81b2e Update AUTHORS.txt
  • 350a057 Bump the github-actions group with 2 updates (#12876)
  • 184390f Update dependabot.yml to bump group updates (#12572)
  • 48917f1 Merge pull request #12875 from hellozee/fix-unit-test
  • dd85c28 Fix invalid origin test to check all the logged messages
  • 203780b Merge pull request #12865 from pradyunsg/better-exception-handling-around-sel...
  • e503141 Properly mock _self_version_check_logic
  • 3518d32 Rework how --debug is handled in main
  • be21d82 Move exception suppression to cover more of self-version-check logic
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pip&package-manager=pip&previous-version=23.2.1&new-version=24.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 5ceac3ecb5e..f064f4edfdc 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -283,7 +283,7 @@ zipp==3.20.0 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -pip==23.2.1 +pip==24.2 # via pip-tools setuptools==68.0.0 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 06897d1f6a8..62ba69406b4 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -276,7 +276,7 @@ zipp==3.20.0 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -pip==23.2.1 +pip==24.2 # via pip-tools setuptools==68.0.0 # via From d1bc1410173a33333239d47d90729edac2aef576 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:22:06 +0000 Subject: [PATCH 036/296] Bump identify from 2.3.5 to 2.6.0 (#8818) Bumps [identify](https://github.com/pre-commit/identify) from 2.3.5 to 2.6.0.
Commits
  • 577bfe1 v2.6.0
  • 2e9f390 Merge pull request #468 from harrymander/fix-index-error-with-env-shebang
  • 0853fe3 Merge pull request #457 from wircho/adolfo-add-ejson
  • 818c07f Add ejson extension
  • 909fd82 Merge pull request #469 from pre-commit/pre-commit-ci-update-config
  • a9fd388 [pre-commit.ci] pre-commit autoupdate
  • 90c7a1e Fix IndexError when shebang is just '#!/usr/bin/env'
  • 437ef92 Merge pull request #467 from pre-commit/pre-commit-ci-update-config
  • 525954b [pre-commit.ci] pre-commit autoupdate
  • 0651fca Merge pull request #464 from pre-commit/pre-commit-ci-update-config
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=identify&package-manager=pip&previous-version=2.3.5&new-version=2.6.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 f064f4edfdc..e0742c1f7bd 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -86,7 +86,7 @@ gidgethub==5.0.1 # via cherry-picker gunicorn==23.0.0 # via -r requirements/base.in -identify==2.3.5 +identify==2.6.0 # via pre-commit idna==3.3 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 62ba69406b4..f16c3db32ae 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -84,7 +84,7 @@ gidgethub==5.3.0 # via cherry-picker gunicorn==23.0.0 # via -r requirements/base.in -identify==2.5.26 +identify==2.6.0 # via pre-commit idna==3.4 # via diff --git a/requirements/lint.txt b/requirements/lint.txt index 54ad2cc4322..8d0587f43d3 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -36,7 +36,7 @@ filelock==3.12.2 # via virtualenv freezegun==1.5.1 # via -r requirements/lint.in -identify==2.5.26 +identify==2.6.0 # via pre-commit idna==3.7 # via From b644f7a624836732f8a3a0498d767857f2ef2486 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:32:28 +0000 Subject: [PATCH 037/296] Bump importlib-metadata from 6.8.0 to 8.4.0 (#8820) Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 6.8.0 to 8.4.0.
Changelog

Sourced from importlib-metadata's changelog.

v8.4.0

Features

  • Deferred import of inspect for import performance. (#499)

v8.3.0

Features

  • Disallow passing of 'dist' to EntryPoints.select.

v8.2.0

Features

  • Add SimplePath to importlib_metadata.all. (#494)

v8.1.0

Features

  • Prioritize valid dists to invalid dists when retrieving by name. (#489)

v8.0.0

Deprecations and Removals

  • Message.getitem now raises a KeyError on missing keys. (#371)
  • Removed deprecated support for Distribution subclasses not implementing abstract methods.

v7.2.1

Bugfixes

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=importlib-metadata&package-manager=pip&previous-version=6.8.0&new-version=8.4.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 e0742c1f7bd..69efedc66e3 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -95,7 +95,7 @@ idna==3.3 # yarl imagesize==1.4.1 # via sphinx -importlib-metadata==7.0.0 +importlib-metadata==8.4.0 # via # build # sphinx diff --git a/requirements/dev.txt b/requirements/dev.txt index f16c3db32ae..c2d8f4f2b02 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -93,7 +93,7 @@ idna==3.4 # yarl imagesize==1.4.1 # via sphinx -importlib-metadata==7.0.0 +importlib-metadata==8.4.0 # via # build # sphinx diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 916cee3c0cb..d30d3fbef13 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -26,7 +26,7 @@ idna==3.4 # via requests imagesize==1.4.1 # via sphinx -importlib-metadata==6.8.0 +importlib-metadata==8.4.0 # via sphinx importlib-resources==6.1.1 # via towncrier diff --git a/requirements/doc.txt b/requirements/doc.txt index 0038be971e1..4069a152c5d 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -26,7 +26,7 @@ idna==3.4 # via requests imagesize==1.4.1 # via sphinx -importlib-metadata==6.8.0 +importlib-metadata==8.4.0 # via sphinx importlib-resources==6.1.1 # via towncrier From 3a10ee24f21f4bd3eea92143f519e39673d3e66d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:32:40 +0000 Subject: [PATCH 038/296] Bump iniconfig from 1.1.1 to 2.0.0 (#8821) Bumps [iniconfig](https://github.com/pytest-dev/iniconfig) from 1.1.1 to 2.0.0.
Changelog

Sourced from iniconfig's changelog.

2.0.0

  • add support for Python 3.7-3.11
  • drop support for Python 2.6-3.6
  • add encoding argument defaulting to utf-8
  • inline and clarify type annotations
  • move parsing code from inline to extra file
  • add typing overloads for helper methods

.. note::

major release due to the major changes in python versions supported + changes in packaging

the api is expected to be compatible

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=iniconfig&package-manager=pip&previous-version=1.1.1&new-version=2.0.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 69efedc66e3..a8bab3f0158 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -103,7 +103,7 @@ importlib-resources==6.1.1 # via towncrier incremental==24.7.2 # via towncrier -iniconfig==1.1.1 +iniconfig==2.0.0 # via pytest jinja2==3.1.4 # via From 37f1f6c155f76a4f485ef5f404ee55aea08dfd19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:50:43 +0000 Subject: [PATCH 039/296] Bump setuptools from 68.0.0 to 73.0.1 (#8819) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [setuptools](https://github.com/pypa/setuptools) from 68.0.0 to 73.0.1.
Changelog

Sourced from setuptools's changelog.

v73.0.1

Bugfixes

  • Remove abc.ABCMeta metaclass from abstract classes. pypa/setuptools#4503 <https://github.com/pypa/setuptools/pull/4503>_ had an unintended consequence of causing potential TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases -- by :user:Avasam (#4579)

v73.0.0

Features

  • Mark abstract base classes and methods with abc.ABC and abc.abstractmethod -- by :user:Avasam (#4503)
  • Changed the order of type checks in setuptools.command.easy_install.CommandSpec.from_param to support any collections.abc.Iterable of str param -- by :user:Avasam (#4505)

Bugfixes

  • Prevent an error in bdist_wheel if compression is set to a str (even if valid) after finalizing options but before running the command. -- by :user:Avasam (#4383)
  • Raises an exception when py_limited_api is used in a build with Py_GIL_DISABLEDpython/cpython#111506#4420)
  • pypa/distutils#284

Deprecations and Removals

  • setuptools is replacing the usages of :pypi:ordered_set with simple instances of dict[Hashable, None]. This is done to remove the extra dependency and it is possible because since Python 3.7, dict maintain insertion order. (#4574)

Misc

v72.2.0

Features

... (truncated)

Commits
  • ebddeb3 Bump version: 73.0.0 → 73.0.1
  • 18963fb Merge pull request #4580 from Avasam/no-ABCMeta
  • b7ee00d Remove ABCMeta metaclass, keep abstractmethods
  • 477f713 Override distribution attribute type in all distutils-based commands (#4577)
  • 429ac58 Override distribution attribute type in all distutils-based commands
  • 4147b09 Bump version: 72.2.0 → 73.0.0
  • 2ad8c10 Merge pull request #4576 from pypa/bugfix/distutils-284
  • 8afe0c3 Merge pull request #4574 from abravalheri/ordered_set
  • ad611bc Merge https://github.com/pypa/distutils into bugfix/distutils-284
  • 30b7331 Ensure a missing target is still indicated as 'sources are newer' even when t...
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=setuptools&package-manager=pip&previous-version=68.0.0&new-version=73.0.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- requirements/lint.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index a8bab3f0158..cc8a478cabd 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -285,7 +285,7 @@ zipp==3.20.0 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via pip-tools -setuptools==68.0.0 +setuptools==73.0.1 # via # blockdiag # incremental diff --git a/requirements/dev.txt b/requirements/dev.txt index c2d8f4f2b02..b6b7ae16e81 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -278,7 +278,7 @@ zipp==3.20.0 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via pip-tools -setuptools==68.0.0 +setuptools==73.0.1 # via # blockdiag # incremental diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index d30d3fbef13..f4ed5c5b611 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -96,7 +96,7 @@ zipp==3.20.0 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -setuptools==68.0.0 +setuptools==73.0.1 # via # blockdiag # incremental diff --git a/requirements/doc.txt b/requirements/doc.txt index 4069a152c5d..c553f228a0d 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -91,7 +91,7 @@ zipp==3.20.0 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -setuptools==68.0.0 +setuptools==73.0.1 # via # blockdiag # incremental diff --git a/requirements/lint.txt b/requirements/lint.txt index 8d0587f43d3..5dd21b48e45 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -123,5 +123,5 @@ virtualenv==20.26.3 # via pre-commit # The following packages are considered to be unsafe in a requirements file: -setuptools==68.0.0 +setuptools==73.0.1 # via nodeenv From 8e4b2aa6ee12bb4596b9f7b03c9399d040617784 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 10:56:09 +0000 Subject: [PATCH 040/296] Bump regex from 2021.11.10 to 2024.7.24 (#8826) Bumps [regex](https://github.com/mrabarnett/mrab-regex) from 2021.11.10 to 2024.7.24.
Changelog

Sourced from regex's changelog.

Version: 2024.7.24

Git issue 539: Bug: Partial matching fails on a simple
example

Version: 2024.6.22

Git issue 535: Regex fails Unicode 15.1 GraphemeBreakTest due
to missing new GB9c rule implementation

Version: 2024.5.15

Git issue 530: hangs with fuzzy and optionals

It's not hanging, it'll finish eventually. It's just an example of catastrophic backtracking.

The error printed when Ctrl+C is pressed does show a bug, though, which is now fixed.

Version: 2024.5.10

Updated for Python 3.13.

<time.h> now needs to be included explicitly because Python.h no longer includes it.

Version: 2024.4.28

Git issue 527: `VERBOSE`/`X` flag breaks `\N` escapes

Version: 2024.4.16

Git issue 525: segfault when fuzzy matching empty list

Version: 2023.12.25

Cannot get release notification action in main.yml to work.
Commenting it out for now.

Version: 2023.12.24

Fixed invalid main.yml.

Version: 2023.12.23

The escape function no longer escapes \x00. It's not
necessary.

Inline flags can now be turned off and apply to what follows.

Added \R to match line endings.

Version: 2023.10.3

Updated to Unicode 15.1.0.

... (truncated)

Commits
  • e8a8d28 Git issue 539: Bug: Partial matching fails on a simple example
  • 6d086ff Git issue 535: Regex fails Unicode 15.1 GraphemeBreakTest due to missing new ...
  • 8eabb42 Git issue 530: hangs with fuzzy and optionals
  • be139ff Updated for Python 3.13.
  • 2e3272b Git issue 527: VERBOSE/X flag breaks \N escapes
  • 9c950f2 Updated changelog.
  • 5d65c8a Git issue 525: segfault when fuzzy matching empty list
  • 4f2ed52 Cannot get release notification action in main.yml to work. Commenting it out...
  • 647c006 Further fixes in main.yml.
  • d0afd79 Another fix in main.yml.
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=regex&package-manager=pip&previous-version=2021.11.10&new-version=2024.7.24)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index cc8a478cabd..e7099ba8962 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -188,7 +188,7 @@ pyyaml==6.0.1 # via pre-commit re-assert==1.1.0 # via -r requirements/test.in -regex==2021.11.10 +regex==2024.7.24 # via re-assert requests==2.31.0 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index b6b7ae16e81..78f178012cc 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -183,7 +183,7 @@ pyyaml==6.0.1 # via pre-commit re-assert==1.1.0 # via -r requirements/test.in -regex==2023.6.3 +regex==2024.7.24 # via re-assert requests==2.31.0 # via diff --git a/requirements/test.txt b/requirements/test.txt index 344fed3e747..2840cbd4d4c 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -92,7 +92,7 @@ python-on-whales==0.72.0 # via -r requirements/test.in re-assert==1.1.0 # via -r requirements/test.in -regex==2023.6.3 +regex==2024.7.24 # via re-assert requests==2.31.0 # via python-on-whales From f6e3b71026ee633034a5299b1c6410a2e8053826 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 10:56:34 +0000 Subject: [PATCH 041/296] Bump nodeenv from 1.6.0 to 1.9.1 (#8828) Bumps [nodeenv](https://github.com/ekalinin/nodeenv) from 1.6.0 to 1.9.1.
Release notes

Sourced from nodeenv's releases.

1.9.1: Fix version discovery

1.9.0

1.8.0: fix fish; add riscv64; multiple attempt to download node

Changes:

1.7.0: drop py34, py35, py36; improved work on m1

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=nodeenv&package-manager=pip&previous-version=1.6.0&new-version=1.9.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 | 3 +-- requirements/lint.txt | 6 +----- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index e7099ba8962..ecf1e6a8150 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -122,7 +122,7 @@ mypy==1.11.1 ; implementation_name == "cpython" # -r requirements/test.in mypy-extensions==1.0.0 # via mypy -nodeenv==1.6.0 +nodeenv==1.9.1 # via pre-commit packaging==24.1 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 78f178012cc..80809361618 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -119,7 +119,7 @@ mypy==1.11.1 ; implementation_name == "cpython" # -r requirements/test.in mypy-extensions==1.0.0 # via mypy -nodeenv==1.8.0 +nodeenv==1.9.1 # via pre-commit packaging==24.1 # via @@ -282,5 +282,4 @@ setuptools==73.0.1 # via # blockdiag # incremental - # nodeenv # pip-tools diff --git a/requirements/lint.txt b/requirements/lint.txt index 5dd21b48e45..589e1801dd5 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -52,7 +52,7 @@ mypy==1.11.1 ; implementation_name == "cpython" # via -r requirements/lint.in mypy-extensions==1.0.0 # via mypy -nodeenv==1.8.0 +nodeenv==1.9.1 # via pre-commit packaging==24.1 # via pytest @@ -121,7 +121,3 @@ uvloop==0.20.0 ; platform_system != "Windows" # via -r requirements/lint.in virtualenv==20.26.3 # via pre-commit - -# The following packages are considered to be unsafe in a requirements file: -setuptools==73.0.1 - # via nodeenv From f087b793eb539719eb1258e3b4d9902b9603dd5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 11:36:43 +0000 Subject: [PATCH 042/296] Bump markupsafe from 2.0.1 to 2.1.5 (#8830) Bumps [markupsafe](https://github.com/pallets/markupsafe) from 2.0.1 to 2.1.5.
Release notes

Sourced from markupsafe's releases.

2.1.5

This is a fix release for the 2.1.x feature release branch. It fixes bugs but does not otherwise change behavior and should not result in breaking changes.

Fixes a regression in striptags behavior from 2.14. Spaces are now collapsed correctly.

2.1.4

This is a fix release for the 2.1.x feature release branch. It fixes bugs but does not otherwise change behavior and should not result in breaking changes.

2.1.3

This is a fix release for the 2.1.x feature branch.

2.1.2

This is the first release to provide wheels for Python 3.11. An SLSA provenance file is also generated, and is available to download from the GitHub release page.

2.1.1

2.1.0

Changelog

Sourced from markupsafe's changelog.

Version 2.1.5

Released 2024-02-02

  • Fix striptags not collapsing spaces. :issue:417

Version 2.1.4

Released 2024-01-19

  • Don't use regular expressions for striptags, avoiding a performance issue. :pr:413

Version 2.1.3

Released 2023-06-02

  • Implement format_map, casefold, removeprefix, and removesuffix methods. :issue:370
  • Fix static typing for basic str methods on Markup. :issue:358
  • Use Self for annotating return types. :pr:379

Version 2.1.2

Released 2023-01-17

  • Fix striptags not stripping tags containing newlines. :issue:310

Version 2.1.1

Released 2022-03-14

  • Avoid ambiguous regex matches in striptags. :pr:293

Version 2.1.0

Released 2022-02-17

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=markupsafe&package-manager=pip&previous-version=2.0.1&new-version=2.1.5)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 ecf1e6a8150..c823f7a9001 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -109,7 +109,7 @@ jinja2==3.1.4 # via # sphinx # towncrier -markupsafe==2.0.1 +markupsafe==2.1.5 # via jinja2 multidict==6.0.5 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 80809361618..d9187dafba6 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -107,7 +107,7 @@ jinja2==3.1.4 # via # sphinx # towncrier -markupsafe==2.1.3 +markupsafe==2.1.5 # via jinja2 multidict==6.0.5 # via diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index f4ed5c5b611..c2c69a2d14a 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -36,7 +36,7 @@ jinja2==3.1.4 # via # sphinx # towncrier -markupsafe==2.1.3 +markupsafe==2.1.5 # via jinja2 packaging==24.1 # via sphinx diff --git a/requirements/doc.txt b/requirements/doc.txt index c553f228a0d..e3692c95f58 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -36,7 +36,7 @@ jinja2==3.1.4 # via # sphinx # towncrier -markupsafe==2.1.3 +markupsafe==2.1.5 # via jinja2 packaging==24.1 # via sphinx From 41cb0bf31196de2181c79cd689b907305d8e17af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 11:39:34 +0000 Subject: [PATCH 043/296] Bump babel from 2.9.1 to 2.16.0 (#8836) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [babel](https://github.com/python-babel/babel) from 2.9.1 to 2.16.0.
Release notes

Sourced from babel's releases.

Version 2.16.0

The changelog below is auto-generated by GitHub.

Please see CHANGELOG.rst for additional details.

What's Changed

New Contributors

Full Changelog: https://github.com/python-babel/babel/compare/v2.15.0...v2.16.0

v2.15.0

The changelog below is auto-generated by GitHub.

The binary artifacts attached to this GitHub release were generated by the GitHub Actions workflow.

Please see CHANGELOG.rst for additional details.


What's Changed

... (truncated)

Changelog

Sourced from babel's changelog.

Version 2.16.0

Features


* CLDR: Upgrade to CLDR 45 by @tomasr8 in :gh:`1077`
* Lists: Support list format fallbacks by @akx in :gh:`1099`
* Messages: Initial support for reading mapping configuration as TOML by
@akx in :gh:`1108`

Bugfixes

  • CLDR: Do not allow substituting alternates or drafts in derived locales by @​akx in :gh:1113
  • Core: Allow falling back to modifier-less locale data by @​akx in :gh:1104
  • Core: Allow use of importlib.metadata for finding entrypoints by @​akx in :gh:1102
  • Dates: Avoid crashing on importing localtime when TZ is malformed by @​akx in :gh:1100
  • Messages: Allow parsing .po files that have an extant but empty Language header by @​akx in :gh:1101
  • Messages: Fix --ignore-dirs being incorrectly read (#1094) by @​john-psina and @​Edwin18 in :gh:1052 and :gh:1095
  • Messages: Make pgettext search plurals when translation is not found by @​tomasr8 in :gh:1085

Infrastructure


* Replace deprecated `ast.Str` with `ast.Constant` by @tomasr8 in
:gh:`1083`
* CI fixes by @akx in :gh:`1080`, :gh:`1097`, :gh:`1103`, :gh:`1107`
* Test on Python 3.13 beta releases by @akx in
* Normalize package name to lower-case in setup.py by @akx in :gh:`1110`

Documentation


* Add a mention to the docs that `format_skeleton(..., fuzzy=True)` may
raise by @tomasr8 in :gh:`1106`
* Two hyperlinks (to CLDR) and some typos by @buhtz in :gh:`1115`


Version 2.15.0
--------------

Python version support
</code></pre>
<ul>
<li>Babel 2.15.0 will require Python 3.8 or newer.
(:gh:<code>1048</code>)</li>
</ul>
<p>Features</p>
<pre><code>
* CLDR: Upgrade to CLDR 44 (:gh:`1071`) (@akx)
* Dates: Support for the &quot;fall back to short format&quot;
logic for time delta formatting (:gh:`1075`) (@akx)
* Message: More versatile .po IO functions (:gh:`1068`) (@akx)
&lt;/tr&gt;&lt;/table&gt;
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>

<ul>
<li><a
href="https://github.com/python-babel/babel/commit/cf7d22369cf40a8218cff1d3dc823eefa174aee0"><code>cf7d223</code></a>
Prepare for 2.16.0 release (<a
href="https://redirect.github.com/python-babel/babel/issues/1116">#1116</a>)</li>
<li><a
href="https://github.com/python-babel/babel/commit/d26a669826d6c963dfdc21ae09e8cd5659fc95e2"><code>d26a669</code></a>
Initial support for reading mapping configuration as TOML (<a
href="https://redirect.github.com/python-babel/babel/issues/1108">#1108</a>)</li>
<li><a
href="https://github.com/python-babel/babel/commit/34ed517de44cf3c3002f3b69713b1693d329646d"><code>34ed517</code></a>
Two hyperlinks (to CLDR) and some typos (<a
href="https://redirect.github.com/python-babel/babel/issues/1115">#1115</a>)</li>
<li><a
href="https://github.com/python-babel/babel/commit/b4ba84382f3ce7bdf0e5a68e7108a21f4e8e7926"><code>b4ba843</code></a>
Do not allow substituting alternates or drafts in derived locales (<a
href="https://redirect.github.com/python-babel/babel/issues/1113">#1113</a>)</li>
<li><a
href="https://github.com/python-babel/babel/commit/d3346ee33b13f50af582e31ae5c337aa409dda11"><code>d3346ee</code></a>
Normalize package name to lower-case in setup.py (<a
href="https://redirect.github.com/python-babel/babel/issues/1110">#1110</a>)</li>
<li><a
href="https://github.com/python-babel/babel/commit/a32f15ecec23d5d5049100fd8e65606be7ad12a1"><code>a32f15e</code></a>
Test on Python 3.13 beta releases (<a
href="https://redirect.github.com/python-babel/babel/issues/1107">#1107</a>)</li>
<li><a
href="https://github.com/python-babel/babel/commit/4d3fd0e0198185cd023f4d3cd1495bda211867f4"><code>4d3fd0e</code></a>
Allow use of importlib.metadata for finding entrypoints (<a
href="https://redirect.github.com/python-babel/babel/issues/1102">#1102</a>)</li>
<li><a
href="https://github.com/python-babel/babel/commit/42d793cb4408a296e5618e1bce8d176fc588ce57"><code>42d793c</code></a>
Allow falling back to modifier-less locale data when modified data is
missing...</li>
<li><a
href="https://github.com/python-babel/babel/commit/32f41c22d589d7f187448be477affe1cfbcbc59d"><code>32f41c2</code></a>
Improve docs for <code>format_skeleton</code> (<a
href="https://redirect.github.com/python-babel/babel/issues/1106">#1106</a>)</li>
<li><a
href="https://github.com/python-babel/babel/commit/2ebc47e63211df8b7636ed8e74bb99ac5bd36c25"><code>2ebc47e</code></a>
Allow parsing .po files that have an extant but empty Language header
(<a
href="https://redirect.github.com/python-babel/babel/issues/1101">#1101</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/python-babel/babel/compare/v2.9.1...v2.16.0">compare
view</a></li>
</ul>
</details>

<br />
[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=babel&package-manager=pip&previous-version=2.9.1&new-version=2.16.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 c823f7a9001..0ab791aea87 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -26,7 +26,7 @@ async-timeout==4.0.3 ; python_version < "3.11" # aioredis attrs==24.2.0 # via -r requirements/runtime-deps.in -babel==2.9.1 +babel==2.16.0 # via sphinx blockdiag==3.0.0 # via sphinxcontrib-blockdiag diff --git a/requirements/dev.txt b/requirements/dev.txt index d9187dafba6..988a011bbde 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -26,7 +26,7 @@ async-timeout==4.0.3 ; python_version < "3.11" # aioredis attrs==24.2.0 # via -r requirements/runtime-deps.in -babel==2.12.1 +babel==2.16.0 # via sphinx blockdiag==3.0.0 # via sphinxcontrib-blockdiag diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index c2c69a2d14a..5b080130969 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -8,7 +8,7 @@ aiohttp-theme==0.1.6 # via -r requirements/doc.in alabaster==0.7.13 # via sphinx -babel==2.12.1 +babel==2.16.0 # via sphinx blockdiag==3.0.0 # via sphinxcontrib-blockdiag diff --git a/requirements/doc.txt b/requirements/doc.txt index e3692c95f58..44cc009b5c4 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -8,7 +8,7 @@ aiohttp-theme==0.1.6 # via -r requirements/doc.in alabaster==0.7.13 # via sphinx -babel==2.12.1 +babel==2.16.0 # via sphinx blockdiag==3.0.0 # via sphinxcontrib-blockdiag From 9af8919a57c0c83a9f5f71b2f88fa34f8da0a327 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 11:41:28 +0000 Subject: [PATCH 044/296] Bump cryptography from 41.0.2 to 43.0.0 (#8837) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [cryptography](https://github.com/pyca/cryptography) from 41.0.2 to 43.0.0.
Changelog

Sourced from cryptography's changelog.

43.0.0 - 2024-07-20


* **BACKWARDS INCOMPATIBLE:** Support for OpenSSL less than 1.1.1e has
been
  removed.  Users on older version of OpenSSL will need to upgrade.
* **BACKWARDS INCOMPATIBLE:** Dropped support for LibreSSL < 3.8.
* Updated Windows, macOS, and Linux wheels to be compiled with OpenSSL
3.3.1.
* Updated the minimum supported Rust version (MSRV) to 1.65.0, from
1.63.0.
*
:func:`~cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key`
now enforces a minimum RSA key size of 1024-bit. Note that 1024-bit is
still
considered insecure, users should generally use a key size of 2048-bits.
*
:func:`~cryptography.hazmat.primitives.serialization.pkcs7.serialize_certificates`
now emits ASN.1 that more closely follows the recommendations in
:rfc:`2315`.
* Added new :doc:`/hazmat/decrepit/index` module which contains outdated
and
  insecure cryptographic primitives.
  :class:`~cryptography.hazmat.primitives.ciphers.algorithms.CAST5`,
  :class:`~cryptography.hazmat.primitives.ciphers.algorithms.SEED`,
  :class:`~cryptography.hazmat.primitives.ciphers.algorithms.IDEA`, and
:class:`~cryptography.hazmat.primitives.ciphers.algorithms.Blowfish`,
which were
deprecated in 37.0.0, have been added to this module. They will be
removed
  from the ``cipher`` module in 45.0.0.
* Moved
:class:`~cryptography.hazmat.primitives.ciphers.algorithms.TripleDES`
and :class:`~cryptography.hazmat.primitives.ciphers.algorithms.ARC4`
into
:doc:`/hazmat/decrepit/index` and deprecated them in the ``cipher``
module.
  They will be removed from the ``cipher`` module in 48.0.0.
* Added support for deterministic
:class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDSA`
(:rfc:`6979`)
* Added support for client certificate verification to the
:mod:`X.509 path validation <cryptography.x509.verification>` APIs
in the
  form of :class:`~cryptography.x509.verification.ClientVerifier`,
  :class:`~cryptography.x509.verification.VerifiedClient`, and
  ``PolicyBuilder``

:meth:`~cryptography.x509.verification.PolicyBuilder.build_client_verifier`.
* Added Certificate
  :attr:`~cryptography.x509.Certificate.public_key_algorithm_oid`
  and Certificate Signing Request

:attr:`~cryptography.x509.CertificateSigningRequest.public_key_algorithm_oid`
to determine the
:class:`~cryptography.hazmat._oid.PublicKeyAlgorithmOID`
  Object Identifier of the public key found inside the certificate.
* Added :attr:`~cryptography.x509.InvalidityDate.invalidity_date_utc`, a
  timezone-aware alternative to the naïve ``datetime`` attribute
  :attr:`~cryptography.x509.InvalidityDate.invalidity_date`.
* Added support for parsing empty DN string in
  :meth:`~cryptography.x509.Name.from_rfc4514_string`.
* Added the following properties that return timezone-aware ``datetime``
objects:
  :meth:`~cryptography.x509.ocsp.OCSPResponse.produced_at_utc`,
  :meth:`~cryptography.x509.ocsp.OCSPResponse.revocation_time_utc`,
  :meth:`~cryptography.x509.ocsp.OCSPResponse.this_update_utc`,
  :meth:`~cryptography.x509.ocsp.OCSPResponse.next_update_utc`,
:meth:`~cryptography.x509.ocsp.OCSPSingleResponse.revocation_time_utc`,
</tr></table>

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cryptography&package-manager=pip&previous-version=41.0.2&new-version=43.0.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 0ab791aea87..e454042f7e0 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -58,7 +58,7 @@ coverage==7.6.1 # via # -r requirements/test.in # pytest-cov -cryptography==41.0.2 +cryptography==43.0.0 # via # pyjwt # trustme diff --git a/requirements/dev.txt b/requirements/dev.txt index 988a011bbde..078be0edf5e 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -58,7 +58,7 @@ coverage==7.6.1 # via # -r requirements/test.in # pytest-cov -cryptography==41.0.3 +cryptography==43.0.0 # via # pyjwt # trustme diff --git a/requirements/test.txt b/requirements/test.txt index 2840cbd4d4c..7c990b70404 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -34,7 +34,7 @@ coverage==7.6.1 # via # -r requirements/test.in # pytest-cov -cryptography==41.0.2 +cryptography==43.0.0 # via trustme exceptiongroup==1.1.2 # via pytest From 57b958eb7acc603d8b0f742d23f6f7b7a49ef60e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 11:47:55 +0000 Subject: [PATCH 045/296] Bump distlib from 0.3.7 to 0.3.8 (#8838) Bumps [distlib](https://github.com/pypa/distlib) from 0.3.7 to 0.3.8.
Changelog

Sourced from distlib's changelog.

0.3.8


Released: 2023-12-12
  • markers

    • Fix #209: use legacy version implementation for Python versions.
  • tests

    • Fix #204: use symlinks in venv creation during test.

    • Fix #208: handle deprecation removals in Python 3.13.

Commits
  • ab5f8e7 Changes for 0.3.8.
  • 86bb212 Update change log.
  • 488599f Updates based on flake8 checks.
  • 0e261af Use legacy version implementation for Python itself.
  • 8242f39 Update copyright years.
  • e27569b Fix #208: Handle deprecation removals in Python 3.13.
  • 65a014b Update requirements and CI matrix.
  • 124108a Skip test for non-final Python versions.
  • ff48e09 Fix #206: include tox.ini in sdist.
  • eeaa18d Fix #204: Use symlinks in venv creation during test.
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=distlib&package-manager=pip&previous-version=0.3.7&new-version=0.3.8)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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/dev.txt | 2 +- requirements/lint.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/dev.txt b/requirements/dev.txt index 078be0edf5e..6c6e238997b 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -62,7 +62,7 @@ cryptography==43.0.0 # via # pyjwt # trustme -distlib==0.3.7 +distlib==0.3.8 # via virtualenv docutils==0.20.1 # via sphinx diff --git a/requirements/lint.txt b/requirements/lint.txt index 589e1801dd5..ccc9d3cf43d 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -28,7 +28,7 @@ click==8.1.6 # typer cryptography==43.0.0 # via trustme -distlib==0.3.7 +distlib==0.3.8 # via virtualenv exceptiongroup==1.1.2 # via pytest From f0c89d6f9b0f4183699adb99f69f129e4c5d4d9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 12:08:24 +0000 Subject: [PATCH 046/296] Bump gidgethub from 5.0.1 to 5.3.0 (#8841) Bumps [gidgethub](https://github.com/brettcannon/gidgethub) from 5.0.1 to 5.3.0.
Release notes

Sourced from gidgethub's releases.

5.3.0

5.2.1

5.2.0

  • Make the minimum version of PyJWT be v2.4.0.

5.1.0

Changelog

Sourced from gidgethub's changelog.

5.3.0

  • Add support passing extra_headers when making requests (PR [#192](https://github.com/brettcannon/gidgethub/issues/192) <https://github.com/brettcannon/gidgethub/pull/192>_)

  • Add a getstatus() method for APIs that do not return content. (PR [#194](https://github.com/brettcannon/gidgethub/issues/194) <https://github.com/brettcannon/gidgethub/pull/194>_)

5.2.1

  • Fix cgi and importlib_resources deprecations. (PR [#185](https://github.com/brettcannon/gidgethub/issues/185) <https://github.com/brettcannon/gidgethub/pull/185>_)

  • Add support for Python 3.11 and drop EOL Python 3.6 (PR [#184](https://github.com/brettcannon/gidgethub/issues/184) <https://github.com/brettcannon/gidgethub/pull/184>_)

5.2.0

  • Make the minimum version of PyJWT be v2.4.0.

5.1.0

  • Use X-Hub-Signature-256 header for webhook validation when available. (PR [#160](https://github.com/brettcannon/gidgethub/issues/160) <https://github.com/brettcannon/gidgethub/pull/160>_).

  • The documentation is now built using Sphinx v>= 4.0.0. (Issue [#143](https://github.com/brettcannon/gidgethub/issues/143) <https://github.com/brettcannon/gidgethub/issues/143>_)

  • :meth:gidgethub.abc.GitHubAPI.getiter now accepts iterable_key parameter in order to support the Checks API. (Issue [#164](https://github.com/brettcannon/gidgethub/issues/164) <https://github.com/brettcannon/gidgethub/issues/164>_)

  • Accept HTTP 202 ACCEPTED as successful. (PR [#174](https://github.com/brettcannon/gidgethub/issues/174) <https://github.com/brettcannon/gidgethub/pull/174>_)

Commits
  • dbcdf4b Fix the rst markup on the Changelog. (#197)
  • b9fe3c6 Add extra_headers option to get methods (#192)
  • 901f057 Add a getstatus() method to gidgethub.abc (#194)
  • 20e8612 Update the intersphinx mapping to 1.0-style (#195)
  • e1f7bae Fix lint issues (#191)
  • b3ae8d1 Replace use of requests in examples with httpx (#190)
  • 4fe3c04 Release 5.2.1 (#186)
  • 89ade88 Fix cgi and importlib_resources deprecations (#185)
  • 64888cb Add support for Python 3.11 and drop EOL Python 3.6 (#184)
  • 8c60e56 Add variable mapping to fix 'Session tests-3.10-dev skipped: Python interpret...
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=gidgethub&package-manager=pip&previous-version=5.0.1&new-version=5.3.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index e454042f7e0..0684b998e32 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -82,7 +82,7 @@ frozenlist==1.4.1 # aiosignal funcparserlib==1.0.1 # via blockdiag -gidgethub==5.0.1 +gidgethub==5.3.0 # via cherry-picker gunicorn==23.0.0 # via -r requirements/base.in @@ -156,7 +156,7 @@ pyenchant==3.2.2 # via sphinxcontrib-spelling pygments==2.18.0 # via sphinx -pyjwt==2.3.0 +pyjwt==2.9.0 # via # gidgethub # pyjwt From 10746c21ddedbaf6e8f8e0ae1e6d6e5f8c2d097f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 12:51:26 +0000 Subject: [PATCH 047/296] Bump typer from 0.6.1 to 0.12.4 (#8840) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [typer](https://github.com/fastapi/typer) from 0.6.1 to 0.12.4.
Release notes

Sourced from typer's releases.

0.12.4

Features

Fixes

Docs

Internal

... (truncated)

Changelog

Sourced from typer's changelog.

0.12.4

Features

Fixes

Docs

Internal

... (truncated)

Commits
  • ae94d57 🔖 Release version 0.12.4
  • 68b3415 📝 Update release notes
  • 218bf89 🐛 Fix support for UnionType (e.g. str | None) with Python 3.11 (#548)
  • ad421bd 📝 Update release notes
  • 640fb09 🐛 Fix zsh autocompletion installation (#237)
  • 90f3e61 📝 Update release notes
  • ca65b36 🐛 Fix usage of Annotated with future annotations in Python 3.7+ (#814)
  • 4efe1bc 📝 Update release notes
  • dcb45b1 🐛 Fix shell_complete not working for Arguments (#737)
  • 76ca3e3 📝 Update release notes
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=typer&package-manager=pip&previous-version=0.6.1&new-version=0.12.4)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 | 16 ++++++++++++++-- requirements/dev.txt | 15 +++++++++++++-- requirements/lint.txt | 2 +- requirements/test.txt | 13 ++++++++++++- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 0684b998e32..da844ee27ab 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -109,8 +109,12 @@ jinja2==3.1.4 # via # sphinx # towncrier +markdown-it-py==3.0.0 + # via rich markupsafe==2.1.5 # via jinja2 +mdurl==0.1.2 + # via markdown-it-py multidict==6.0.5 # via # -r requirements/multidict.in @@ -155,7 +159,9 @@ pydantic-core==2.6.0 pyenchant==3.2.2 # via sphinxcontrib-spelling pygments==2.18.0 - # via sphinx + # via + # rich + # sphinx pyjwt==2.9.0 # via # gidgethub @@ -195,8 +201,12 @@ requests==2.31.0 # cherry-picker # python-on-whales # sphinx +rich==13.7.1 + # via typer setuptools-git==1.2 # via -r requirements/test.in +shellingham==1.5.4 + # via typer six==1.16.0 # via python-dateutil slotscheck==0.19.0 @@ -249,7 +259,7 @@ trustme==1.1.0 ; platform_machine != "i686" # via # -r requirements/lint.in # -r requirements/test.in -typer==0.6.1 +typer==0.12.4 # via python-on-whales typing-extensions==4.11.0 # via @@ -259,6 +269,8 @@ typing-extensions==4.11.0 # pydantic # pydantic-core # python-on-whales + # rich + # typer uritemplate==4.1.1 # via gidgethub urllib3==2.2.2 diff --git a/requirements/dev.txt b/requirements/dev.txt index 6c6e238997b..6b95b18276e 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -107,8 +107,12 @@ jinja2==3.1.4 # via # sphinx # towncrier +markdown-it-py==3.0.0 + # via rich markupsafe==2.1.5 # via jinja2 +mdurl==0.1.2 + # via markdown-it-py multidict==6.0.5 # via # -r requirements/runtime-deps.in @@ -150,7 +154,9 @@ pydantic==2.2.0 pydantic-core==2.6.0 # via pydantic pygments==2.18.0 - # via sphinx + # via + # rich + # sphinx pyjwt==2.8.0 # via # gidgethub @@ -190,8 +196,12 @@ requests==2.31.0 # cherry-picker # python-on-whales # sphinx +rich==13.7.1 + # via typer setuptools-git==1.2 # via -r requirements/test.in +shellingham==1.5.4 + # via typer six==1.16.0 # via python-dateutil slotscheck==0.19.0 @@ -241,7 +251,7 @@ trustme==1.1.0 ; platform_machine != "i686" # via # -r requirements/lint.in # -r requirements/test.in -typer==0.9.0 +typer==0.12.4 # via python-on-whales typing-extensions==4.11.0 # via @@ -251,6 +261,7 @@ typing-extensions==4.11.0 # pydantic # pydantic-core # python-on-whales + # rich # typer uritemplate==4.1.1 # via gidgethub diff --git a/requirements/lint.txt b/requirements/lint.txt index ccc9d3cf43d..9f0f71afff3 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -103,7 +103,7 @@ tqdm==4.66.5 # via python-on-whales trustme==1.1.0 # via -r requirements/lint.in -typer==0.12.3 +typer==0.12.4 # via python-on-whales typing-extensions==4.11.0 # via diff --git a/requirements/test.txt b/requirements/test.txt index 7c990b70404..e6452d9920a 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -53,6 +53,10 @@ idna==3.4 # yarl iniconfig==2.0.0 # via pytest +markdown-it-py==3.0.0 + # via rich +mdurl==0.1.2 + # via markdown-it-py multidict==6.0.5 # via # -r requirements/runtime-deps.in @@ -77,6 +81,8 @@ pydantic==2.2.0 # via python-on-whales pydantic-core==2.6.0 # via pydantic +pygments==2.18.0 + # via rich pytest==8.3.2 # via # -r requirements/test.in @@ -96,8 +102,12 @@ regex==2024.7.24 # via re-assert requests==2.31.0 # via python-on-whales +rich==13.7.1 + # via typer setuptools-git==1.2 # via -r requirements/test.in +shellingham==1.5.4 + # via typer six==1.16.0 # via python-dateutil tomli==2.0.1 @@ -109,7 +119,7 @@ tqdm==4.66.5 # via python-on-whales trustme==1.1.0 ; platform_machine != "i686" # via -r requirements/test.in -typer==0.9.0 +typer==0.12.4 # via python-on-whales typing-extensions==4.11.0 # via @@ -118,6 +128,7 @@ typing-extensions==4.11.0 # pydantic # pydantic-core # python-on-whales + # rich # typer urllib3==2.2.2 # via requests From 7f64912206fb79113817dfee9a67ce9eff61cc4e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 13:28:05 +0000 Subject: [PATCH 048/296] Bump typing-extensions from 4.11.0 to 4.12.2 (#8839) Bumps [typing-extensions](https://github.com/python/typing_extensions) from 4.11.0 to 4.12.2.
Release notes

Sourced from typing-extensions's releases.

4.12.2

  • Fix regression in v4.12.0 where specialization of certain generics with an overridden __eq__ method would raise errors. Patch by Jelle Zijlstra.
  • Fix tests so they pass on 3.13.0b2

4.12.1

  • Preliminary changes for compatibility with the draft implementation of PEP 649 in Python 3.14. Patch by Jelle Zijlstra.
  • Fix regression in v4.12.0 where nested Annotated types would cause TypeError to be raised if the nested Annotated type had unhashable metadata. Patch by Alex Waygood.

4.12.0

This release focuses on compatibility with the upcoming release of Python 3.13. Most changes are related to the implementation of type parameter defaults (PEP 696).

Thanks to all of the people who contributed patches, especially Alex Waygood, who did most of the work adapting typing-extensions to the CPython PEP 696 implementation.

There is a single change since 4.12.0rc1:

  • Fix incorrect behaviour of typing_extensions.ParamSpec on Python 3.8 and 3.9 that meant that isinstance(typing_extensions.ParamSpec("P"), typing.TypeVar) would have a different result in some situations depending on whether or not a profiling function had been set using sys.setprofile. Patch by Alex Waygood.

Changes included in 4.12.0rc1:

  • Improve the implementation of type parameter defaults (PEP 696)
    • Backport the typing.NoDefault sentinel object from Python 3.13. TypeVars, ParamSpecs and TypeVarTuples without default values now have their __default__ attribute set to this sentinel value.
    • TypeVars, ParamSpecs and TypeVarTuples now have a has_default() method, matching typing.TypeVar, typing.ParamSpec and typing.TypeVarTuple on Python 3.13+.
    • TypeVars, ParamSpecs and TypeVarTuples with default=None passed to their constructors now have their __default__ attribute set to None at runtime rather than types.NoneType.
    • Fix most tests for TypeVar, ParamSpec and TypeVarTuple on Python 3.13.0b1 and newer.
    • Backport CPython PR #118774, allowing type parameters without default values to follow those with default values in some type parameter lists. Patch by Alex Waygood, backporting a CPython PR by Jelle Zijlstra.
    • It is now disallowed to use a TypeVar with a default value after a TypeVarTuple in a type parameter list. This matches the CPython implementation of PEP 696 on Python 3.13+.
    • Fix bug in PEP-696 implementation where a default value for a ParamSpec

... (truncated)

Changelog

Sourced from typing-extensions's changelog.

Release 4.12.2 (June 7, 2024)

  • Fix regression in v4.12.0 where specialization of certain generics with an overridden __eq__ method would raise errors. Patch by Jelle Zijlstra.
  • Fix tests so they pass on 3.13.0b2

Release 4.12.1 (June 1, 2024)

  • Preliminary changes for compatibility with the draft implementation of PEP 649 in Python 3.14. Patch by Jelle Zijlstra.
  • Fix regression in v4.12.0 where nested Annotated types would cause TypeError to be raised if the nested Annotated type had unhashable metadata. Patch by Alex Waygood.

Release 4.12.0 (May 23, 2024)

This release is mostly the same as 4.12.0rc1 but fixes one more longstanding bug.

  • Fix incorrect behaviour of typing_extensions.ParamSpec on Python 3.8 and 3.9 that meant that isinstance(typing_extensions.ParamSpec("P"), typing.TypeVar) would have a different result in some situations depending on whether or not a profiling function had been set using sys.setprofile. Patch by Alex Waygood.

Release 4.12.0rc1 (May 16, 2024)

This release focuses on compatibility with the upcoming release of Python 3.13. Most changes are related to the implementation of type parameter defaults (PEP 696).

Thanks to all of the people who contributed patches, especially Alex Waygood, who did most of the work adapting typing-extensions to the CPython PEP 696 implementation.

Full changelog:

  • Improve the implementation of type parameter defaults (PEP 696)
    • Backport the typing.NoDefault sentinel object from Python 3.13. TypeVars, ParamSpecs and TypeVarTuples without default values now have their __default__ attribute set to this sentinel value.
    • TypeVars, ParamSpecs and TypeVarTuples now have a has_default() method, matching typing.TypeVar, typing.ParamSpec and typing.TypeVarTuple on Python 3.13+.
    • TypeVars, ParamSpecs and TypeVarTuples with default=None passed to their constructors now have their __default__ attribute set to None at runtime rather than types.NoneType.
    • Fix most tests for TypeVar, ParamSpec and TypeVarTuple on Python 3.13.0b1 and newer.

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=typing-extensions&package-manager=pip&previous-version=4.11.0&new-version=4.12.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index da844ee27ab..ee6c6328122 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -261,7 +261,7 @@ trustme==1.1.0 ; platform_machine != "i686" # -r requirements/test.in typer==0.12.4 # via python-on-whales -typing-extensions==4.11.0 +typing-extensions==4.12.2 # via # aioredis # annotated-types diff --git a/requirements/dev.txt b/requirements/dev.txt index 6b95b18276e..d08ca566672 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -253,7 +253,7 @@ trustme==1.1.0 ; platform_machine != "i686" # -r requirements/test.in typer==0.12.4 # via python-on-whales -typing-extensions==4.11.0 +typing-extensions==4.12.2 # via # aioredis # annotated-types diff --git a/requirements/lint.txt b/requirements/lint.txt index 9f0f71afff3..3dc829156d6 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -105,7 +105,7 @@ trustme==1.1.0 # via -r requirements/lint.in typer==0.12.4 # via python-on-whales -typing-extensions==4.11.0 +typing-extensions==4.12.2 # via # aioredis # annotated-types diff --git a/requirements/test.txt b/requirements/test.txt index e6452d9920a..c9eaef76c05 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -121,7 +121,7 @@ trustme==1.1.0 ; platform_machine != "i686" # via -r requirements/test.in typer==0.12.4 # via python-on-whales -typing-extensions==4.11.0 +typing-extensions==4.12.2 # via # annotated-types # mypy From 8a2a8900ea88bd17ba37f9406eeebcfba8cb1fef Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Thu, 22 Aug 2024 14:30:41 +0100 Subject: [PATCH 049/296] Fix Python parser chunked handling with multiple Transfer-Encoding values (#8843) (cherry picked from commit faa15fd7d1bea808a64f979c1a7ace8340d68d61) --- CHANGES/8823.bugfix.rst | 1 + aiohttp/http_parser.py | 20 +++++++++++++++----- tests/test_http_parser.py | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 CHANGES/8823.bugfix.rst diff --git a/CHANGES/8823.bugfix.rst b/CHANGES/8823.bugfix.rst new file mode 100644 index 00000000000..ea18e65fd4a --- /dev/null +++ b/CHANGES/8823.bugfix.rst @@ -0,0 +1 @@ +Fixed Python parser chunked handling with multiple Transfer-Encoding values -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/http_parser.py b/aiohttp/http_parser.py index b992955a011..f46cf833c03 100644 --- a/aiohttp/http_parser.py +++ b/aiohttp/http_parser.py @@ -277,8 +277,10 @@ def __init__( ) @abc.abstractmethod - def parse_message(self, lines: List[bytes]) -> _MsgT: - pass + def parse_message(self, lines: List[bytes]) -> _MsgT: ... + + @abc.abstractmethod + def _is_chunked_te(self, te: str) -> bool: ... def feed_eof(self) -> Optional[_MsgT]: if self._payload_parser is not None: @@ -537,10 +539,8 @@ def parse_headers( # chunking te = headers.get(hdrs.TRANSFER_ENCODING) if te is not None: - if "chunked" == te.lower(): + if self._is_chunked_te(te): chunked = True - else: - raise BadHttpMessage("Request has invalid `Transfer-Encoding`") if hdrs.CONTENT_LENGTH in headers: raise BadHttpMessage( @@ -650,6 +650,12 @@ def parse_message(self, lines: List[bytes]) -> RawRequestMessage: url, ) + def _is_chunked_te(self, te: str) -> bool: + if te.rsplit(",", maxsplit=1)[-1].strip(" \t").lower() == "chunked": + return True + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.3 + raise BadHttpMessage("Request has invalid `Transfer-Encoding`") + class HttpResponseParser(HttpParser[RawResponseMessage]): """Read response status line and headers. @@ -735,6 +741,10 @@ def parse_message(self, lines: List[bytes]) -> RawResponseMessage: chunked, ) + def _is_chunked_te(self, te: str) -> bool: + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.2 + return te.rsplit(",", maxsplit=1)[-1].strip(" \t").lower() == "chunked" + class HttpPayloadParser: def __init__( diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py index 74700df4253..78abe528cb0 100644 --- a/tests/test_http_parser.py +++ b/tests/test_http_parser.py @@ -84,6 +84,7 @@ def response(loop: Any, protocol: Any, request: Any): max_line_size=8190, max_headers=32768, max_field_size=8190, + read_until_eof=True, ) @@ -514,6 +515,23 @@ def test_request_te_chunked123(parser: Any) -> None: parser.feed_data(text) +async def test_request_te_last_chunked(parser: Any) -> None: + text = b"GET /test HTTP/1.1\r\nTransfer-Encoding: not, chunked\r\n\r\n1\r\nT\r\n3\r\nest\r\n0\r\n\r\n" + messages, upgrade, tail = parser.feed_data(text) + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.3 + assert await messages[0][1].read() == b"Test" + + +def test_request_te_first_chunked(parser: Any) -> None: + text = b"GET /test HTTP/1.1\r\nTransfer-Encoding: chunked, not\r\n\r\n1\r\nT\r\n3\r\nest\r\n0\r\n\r\n" + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.3 + with pytest.raises( + http_exceptions.BadHttpMessage, + match="nvalid `Transfer-Encoding`", + ): + parser.feed_data(text) + + def test_conn_upgrade(parser: Any) -> None: text = ( b"GET /test HTTP/1.1\r\n" @@ -1154,6 +1172,23 @@ async def test_http_response_parser_bad_chunked_strict_c(loop, protocol) -> None response.feed_data(text) +async def test_http_response_parser_notchunked(response) -> None: + text = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: notchunked\r\n\r\n1\r\nT\r\n3\r\nest\r\n0\r\n\r\n" + messages, upgrade, tail = response.feed_data(text) + response.feed_eof() + + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.2 + assert await messages[0][1].read() == b"1\r\nT\r\n3\r\nest\r\n0\r\n\r\n" + + +async def test_http_response_parser_last_chunked(response) -> None: + text = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: not, chunked\r\n\r\n1\r\nT\r\n3\r\nest\r\n0\r\n\r\n" + messages, upgrade, tail = response.feed_data(text) + + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.2 + assert await messages[0][1].read() == b"Test" + + def test_http_response_parser_bad(response) -> None: with pytest.raises(http_exceptions.BadHttpMessage): response.feed_data(b"HTT/1\r\n\r\n") From 23c9671fdab74956da85d26144d860e774b181e7 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Thu, 22 Aug 2024 14:30:53 +0100 Subject: [PATCH 050/296] Fix Python parser chunked handling with multiple Transfer-Encoding values (#8844) (cherry picked from commit faa15fd7d1bea808a64f979c1a7ace8340d68d61) --- CHANGES/8823.bugfix.rst | 1 + aiohttp/http_parser.py | 20 +++++++++++++++----- tests/test_http_parser.py | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 CHANGES/8823.bugfix.rst diff --git a/CHANGES/8823.bugfix.rst b/CHANGES/8823.bugfix.rst new file mode 100644 index 00000000000..ea18e65fd4a --- /dev/null +++ b/CHANGES/8823.bugfix.rst @@ -0,0 +1 @@ +Fixed Python parser chunked handling with multiple Transfer-Encoding values -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/http_parser.py b/aiohttp/http_parser.py index b992955a011..f46cf833c03 100644 --- a/aiohttp/http_parser.py +++ b/aiohttp/http_parser.py @@ -277,8 +277,10 @@ def __init__( ) @abc.abstractmethod - def parse_message(self, lines: List[bytes]) -> _MsgT: - pass + def parse_message(self, lines: List[bytes]) -> _MsgT: ... + + @abc.abstractmethod + def _is_chunked_te(self, te: str) -> bool: ... def feed_eof(self) -> Optional[_MsgT]: if self._payload_parser is not None: @@ -537,10 +539,8 @@ def parse_headers( # chunking te = headers.get(hdrs.TRANSFER_ENCODING) if te is not None: - if "chunked" == te.lower(): + if self._is_chunked_te(te): chunked = True - else: - raise BadHttpMessage("Request has invalid `Transfer-Encoding`") if hdrs.CONTENT_LENGTH in headers: raise BadHttpMessage( @@ -650,6 +650,12 @@ def parse_message(self, lines: List[bytes]) -> RawRequestMessage: url, ) + def _is_chunked_te(self, te: str) -> bool: + if te.rsplit(",", maxsplit=1)[-1].strip(" \t").lower() == "chunked": + return True + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.3 + raise BadHttpMessage("Request has invalid `Transfer-Encoding`") + class HttpResponseParser(HttpParser[RawResponseMessage]): """Read response status line and headers. @@ -735,6 +741,10 @@ def parse_message(self, lines: List[bytes]) -> RawResponseMessage: chunked, ) + def _is_chunked_te(self, te: str) -> bool: + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.2 + return te.rsplit(",", maxsplit=1)[-1].strip(" \t").lower() == "chunked" + class HttpPayloadParser: def __init__( diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py index 74700df4253..78abe528cb0 100644 --- a/tests/test_http_parser.py +++ b/tests/test_http_parser.py @@ -84,6 +84,7 @@ def response(loop: Any, protocol: Any, request: Any): max_line_size=8190, max_headers=32768, max_field_size=8190, + read_until_eof=True, ) @@ -514,6 +515,23 @@ def test_request_te_chunked123(parser: Any) -> None: parser.feed_data(text) +async def test_request_te_last_chunked(parser: Any) -> None: + text = b"GET /test HTTP/1.1\r\nTransfer-Encoding: not, chunked\r\n\r\n1\r\nT\r\n3\r\nest\r\n0\r\n\r\n" + messages, upgrade, tail = parser.feed_data(text) + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.3 + assert await messages[0][1].read() == b"Test" + + +def test_request_te_first_chunked(parser: Any) -> None: + text = b"GET /test HTTP/1.1\r\nTransfer-Encoding: chunked, not\r\n\r\n1\r\nT\r\n3\r\nest\r\n0\r\n\r\n" + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.3 + with pytest.raises( + http_exceptions.BadHttpMessage, + match="nvalid `Transfer-Encoding`", + ): + parser.feed_data(text) + + def test_conn_upgrade(parser: Any) -> None: text = ( b"GET /test HTTP/1.1\r\n" @@ -1154,6 +1172,23 @@ async def test_http_response_parser_bad_chunked_strict_c(loop, protocol) -> None response.feed_data(text) +async def test_http_response_parser_notchunked(response) -> None: + text = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: notchunked\r\n\r\n1\r\nT\r\n3\r\nest\r\n0\r\n\r\n" + messages, upgrade, tail = response.feed_data(text) + response.feed_eof() + + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.2 + assert await messages[0][1].read() == b"1\r\nT\r\n3\r\nest\r\n0\r\n\r\n" + + +async def test_http_response_parser_last_chunked(response) -> None: + text = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: not, chunked\r\n\r\n1\r\nT\r\n3\r\nest\r\n0\r\n\r\n" + messages, upgrade, tail = response.feed_data(text) + + # https://www.rfc-editor.org/rfc/rfc9112#section-6.3-2.4.2 + assert await messages[0][1].read() == b"Test" + + def test_http_response_parser_bad(response) -> None: with pytest.raises(http_exceptions.BadHttpMessage): response.feed_data(b"HTT/1\r\n\r\n") From 15d622cafb730b9874f2dee83f750654d724d89f Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 12:36:24 +0100 Subject: [PATCH 051/296] [PR #8804/8156789a backport][3.10] docs: minor grammar fix in client_reference.rst (#8856) **This is a backport of PR #8804 as merged into master (8156789a1a1c6413233986b372dc933f5c13d712).** Co-authored-by: Dima Tisnek --- .github/workflows/ci-cd.yml | 1 - docs/client_reference.rst | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 8e56acb497d..17632dba6e6 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -238,7 +238,6 @@ jobs: if: ${{ !cancelled() }} uses: codecov/test-results-action@v1 with: - fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} check: # This job does nothing and is only used for the branch protection diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 738892c6cc6..91444d117b1 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -125,7 +125,7 @@ The client session supports the context manager protocol for self closing. Automatically call :meth:`ClientResponse.raise_for_status()` for each response, ``False`` by default. - This parameter can be overridden when you making a request, e.g.:: + This parameter can be overridden when making a request, e.g.:: client_session = aiohttp.ClientSession(raise_for_status=True) resp = await client_session.get(url, raise_for_status=False) From 385e4303b4b8aeae6c388d98f50b776d98b53c27 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 12:53:39 +0100 Subject: [PATCH 052/296] [PR #8804/8156789a backport][3.11] docs: minor grammar fix in client_reference.rst (#8857) **This is a backport of PR #8804 as merged into master (8156789a1a1c6413233986b372dc933f5c13d712).** Co-authored-by: Dima Tisnek --- .github/workflows/ci-cd.yml | 1 - docs/client_reference.rst | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index bb4df9b7760..df27a9108d6 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -236,7 +236,6 @@ jobs: if: ${{ !cancelled() }} uses: codecov/test-results-action@v1 with: - fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} check: # This job does nothing and is only used for the branch protection diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 738892c6cc6..91444d117b1 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -125,7 +125,7 @@ The client session supports the context manager protocol for self closing. Automatically call :meth:`ClientResponse.raise_for_status()` for each response, ``False`` by default. - This parameter can be overridden when you making a request, e.g.:: + This parameter can be overridden when making a request, e.g.:: client_session = aiohttp.ClientSession(raise_for_status=True) resp = await client_session.get(url, raise_for_status=False) From f1be53e5aeebf9e6eb18ff071c86bed3bd31926d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 12:26:54 +0000 Subject: [PATCH 053/296] Bump requests from 2.31.0 to 2.32.3 (#8859) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [requests](https://github.com/psf/requests) from 2.31.0 to 2.32.3.
Release notes

Sourced from requests's releases.

v2.32.3

2.32.3 (2024-05-29)

Bugfixes

  • Fixed bug breaking the ability to specify custom SSLContexts in sub-classes of HTTPAdapter. (#6716)
  • Fixed issue where Requests started failing to run on Python versions compiled without the ssl module. (#6724)

v2.32.2

2.32.2 (2024-05-21)

Deprecations

  • To provide a more stable migration for custom HTTPAdapters impacted by the CVE changes in 2.32.0, we've renamed _get_connection to a new public API, get_connection_with_tls_context. Existing custom HTTPAdapters will need to migrate their code to use this new API. get_connection is considered deprecated in all versions of Requests>=2.32.0.

    A minimal (2-line) example has been provided in the linked PR to ease migration, but we strongly urge users to evaluate if their custom adapter is subject to the same issue described in CVE-2024-35195. (#6710)

v2.32.1

2.32.1 (2024-05-20)

Bugfixes

  • Add missing test certs to the sdist distributed on PyPI.

v2.32.0

2.32.0 (2024-05-20)

🐍 PYCON US 2024 EDITION 🐍

Security

Improvements

  • verify=True now reuses a global SSLContext which should improve request time variance between first and subsequent requests. It should also minimize certificate load time on Windows systems when using a Python version built with OpenSSL 3.x. (#6667)
  • Requests now supports optional use of character detection (chardet or charset_normalizer) when repackaged or vendored.

... (truncated)

Changelog

Sourced from requests's changelog.

2.32.3 (2024-05-29)

Bugfixes

  • Fixed bug breaking the ability to specify custom SSLContexts in sub-classes of HTTPAdapter. (#6716)
  • Fixed issue where Requests started failing to run on Python versions compiled without the ssl module. (#6724)

2.32.2 (2024-05-21)

Deprecations

  • To provide a more stable migration for custom HTTPAdapters impacted by the CVE changes in 2.32.0, we've renamed _get_connection to a new public API, get_connection_with_tls_context. Existing custom HTTPAdapters will need to migrate their code to use this new API. get_connection is considered deprecated in all versions of Requests>=2.32.0.

    A minimal (2-line) example has been provided in the linked PR to ease migration, but we strongly urge users to evaluate if their custom adapter is subject to the same issue described in CVE-2024-35195. (#6710)

2.32.1 (2024-05-20)

Bugfixes

  • Add missing test certs to the sdist distributed on PyPI.

2.32.0 (2024-05-20)

Security

Improvements

  • verify=True now reuses a global SSLContext which should improve request time variance between first and subsequent requests. It should also minimize certificate load time on Windows systems when using a Python version built with OpenSSL 3.x. (#6667)
  • Requests now supports optional use of character detection (chardet or charset_normalizer) when repackaged or vendored. This enables pip and other projects to minimize their vendoring surface area. The Response.text() and apparent_encoding APIs will default to utf-8 if neither library is present. (#6702)

... (truncated)

Commits
  • 0e322af v2.32.3
  • e188799 Don't create default SSLContext if ssl module isn't present (#6724)
  • 145b539 Merge pull request #6716 from sigmavirus24/bug/6715
  • b1d73dd Don't use default SSLContext with custom poolmanager kwargs
  • 6badbac Update HISTORY.md
  • a62a2d3 Allow for overriding of specific pool key params
  • 88dce9d v2.32.2
  • c98e4d1 Merge pull request #6710 from nateprewitt/api_rename
  • 92075b3 Add deprecation warning
  • aa1461b Move _get_connection to get_connection_with_tls_context
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=requests&package-manager=pip&previous-version=2.31.0&new-version=2.32.3)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- requirements/lint.txt | 2 +- requirements/test.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index ee6c6328122..eb013753530 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -196,7 +196,7 @@ re-assert==1.1.0 # via -r requirements/test.in regex==2024.7.24 # via re-assert -requests==2.31.0 +requests==2.32.3 # via # cherry-picker # python-on-whales diff --git a/requirements/dev.txt b/requirements/dev.txt index d08ca566672..b4ef0dfe74b 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -191,7 +191,7 @@ re-assert==1.1.0 # via -r requirements/test.in regex==2024.7.24 # via re-assert -requests==2.31.0 +requests==2.32.3 # via # cherry-picker # python-on-whales diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 5b080130969..977da96e6e2 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -50,7 +50,7 @@ pygments==2.18.0 # via sphinx pytz==2023.3.post1 # via babel -requests==2.31.0 +requests==2.32.3 # via sphinx snowballstemmer==2.2.0 # via sphinx diff --git a/requirements/doc.txt b/requirements/doc.txt index 44cc009b5c4..371034a8849 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -48,7 +48,7 @@ pygments==2.18.0 # via sphinx pytz==2023.3.post1 # via babel -requests==2.31.0 +requests==2.32.3 # via sphinx snowballstemmer==2.2.0 # via sphinx diff --git a/requirements/lint.txt b/requirements/lint.txt index 3dc829156d6..d0cf9e44248 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -84,7 +84,7 @@ python-on-whales==0.72.0 # via -r requirements/lint.in pyyaml==6.0.1 # via pre-commit -requests==2.31.0 +requests==2.32.3 # via python-on-whales rich==13.7.1 # via typer diff --git a/requirements/test.txt b/requirements/test.txt index c9eaef76c05..dfdcf8f7b1e 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -100,7 +100,7 @@ re-assert==1.1.0 # via -r requirements/test.in regex==2024.7.24 # via re-assert -requests==2.31.0 +requests==2.32.3 # via python-on-whales rich==13.7.1 # via typer From 5319115c6312e777dd7f89fac937ca3b73b31f8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 12:44:57 +0000 Subject: [PATCH 054/296] Bump sphinxcontrib-htmlhelp from 2.0.0 to 2.0.1 (#8860) Bumps [sphinxcontrib-htmlhelp](https://github.com/sphinx-doc/sphinxcontrib-htmlhelp) from 2.0.0 to 2.0.1.
Changelog

Sourced from sphinxcontrib-htmlhelp's changelog.

Release 2.0.1 (2023-01-31)

  • Drop support for Python 3.7 and lower
  • Fix deprecation warnings from Sphinx 6.1
Commits
  • 81362a1 Bump to 2.0.1 final
  • 547e73d Update test matrix
  • 390d342 Update Tox commands and environment list
  • 31f11e0 Fix imports for Sphinx above 6.1
  • 2981e43 Merge branch 'pyproject'
  • 9003a00 Move tool configuration to individual files
  • 7b2a66a Drop support for Python 3.7 and below
  • c077df1 Use modern pyproject based packaging
  • ced6378 git mv setup.py pyproject.toml
  • 7e05fea Merge pull request #16 from tk0miya/9457_test
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=sphinxcontrib-htmlhelp&package-manager=pip&previous-version=2.0.0&new-version=2.0.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index eb013753530..4aa2ea37e3a 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -225,7 +225,7 @@ sphinxcontrib-blockdiag==3.0.0 # via -r requirements/doc.in sphinxcontrib-devhelp==1.0.2 # via sphinx -sphinxcontrib-htmlhelp==2.0.0 +sphinxcontrib-htmlhelp==2.0.1 # via sphinx sphinxcontrib-jsmath==1.0.1 # via sphinx From 4cdbf01d609db0c21da1cba03b1eb529e715bef2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 12:56:58 +0000 Subject: [PATCH 055/296] Bump exceptiongroup from 1.1.2 to 1.2.2 (#8861) Bumps [exceptiongroup](https://github.com/agronholm/exceptiongroup) from 1.1.2 to 1.2.2.
Release notes

Sourced from exceptiongroup's releases.

1.2.2

  • Removed an assert in exceptiongroup._formatting that caused compatibility issues with Sentry (#123)

1.2.1

  • Updated the copying of __notes__ to match CPython behavior (PR by CF Bolz-Tereick)
  • Corrected the type annotation of the exception handler callback to accept a BaseExceptionGroup instead of BaseException
  • Fixed type errors on Python < 3.10 and the type annotation of suppress() (PR by John Litborn)

1.2.0

  • Added special monkeypatching if Apport has overridden sys.excepthook so it will format exception groups correctly (PR by John Litborn)
  • Added a backport of contextlib.suppress() from Python 3.12.1 which also handles suppressing exceptions inside exception groups
  • Fixed bare raise in a handler reraising the original naked exception rather than an exception group which is what is raised when you do a raise in an except* handler
Changelog

Sourced from exceptiongroup's changelog.

Version history

This library adheres to Semantic Versioning 2.0 <http://semver.org/>_.

1.2.2

  • Removed an assert in exceptiongroup._formatting that caused compatibility issues with Sentry ([#123](https://github.com/agronholm/exceptiongroup/issues/123) <https://github.com/agronholm/exceptiongroup/issues/123>_)

1.2.1

  • Updated the copying of __notes__ to match CPython behavior (PR by CF Bolz-Tereick)
  • Corrected the type annotation of the exception handler callback to accept a BaseExceptionGroup instead of BaseException
  • Fixed type errors on Python < 3.10 and the type annotation of suppress() (PR by John Litborn)

1.2.0

  • Added special monkeypatching if Apport <https://github.com/canonical/apport>_ has overridden sys.excepthook so it will format exception groups correctly (PR by John Litborn)
  • Added a backport of contextlib.suppress() from Python 3.12.1 which also handles suppressing exceptions inside exception groups
  • Fixed bare raise in a handler reraising the original naked exception rather than an exception group which is what is raised when you do a raise in an except* handler

1.1.3

  • catch() now raises a TypeError if passed an async exception handler instead of just giving a RuntimeWarning about the coroutine never being awaited. (#66, PR by John Litborn)
  • Fixed plain raise statement in an exception handler callback to work like a raise in an except* block
  • Fixed new exception group not being chained to the original exception when raising an exception group from exceptions raised in handler callbacks
  • Fixed type annotations of the derive(), subgroup() and split() methods to match the ones in typeshed

1.1.2

  • Changed handling of exceptions in exception group handler callbacks to not wrap a single exception in an exception group, as per CPython issue 103590 <https://github.com/python/cpython/issues/103590>_

1.1.1

  • Worked around

... (truncated)

Commits
  • 2399d54 Added the release version
  • bec9651 Removed problematic assert that caused compatibility issues
  • f3f0ff6 Updated Ruff configuration
  • bb43ee0 Fixed formatting tests failing on Python 3.13
  • eb8fbbc [pre-commit.ci] pre-commit autoupdate (#129)
  • 6ff8300 [pre-commit.ci] pre-commit autoupdate (#128)
  • 761933f [pre-commit.ci] pre-commit autoupdate (#127)
  • 1b43294 [pre-commit.ci] pre-commit autoupdate (#125)
  • dd87018 [pre-commit.ci] pre-commit autoupdate (#124)
  • 54d8b8d [pre-commit.ci] pre-commit autoupdate (#121)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=exceptiongroup&package-manager=pip&previous-version=1.1.2&new-version=1.2.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 4aa2ea37e3a..15e2e39f670 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -68,7 +68,7 @@ distlib==0.3.8 # via virtualenv docutils==0.20.1 # via sphinx -exceptiongroup==1.1.2 +exceptiongroup==1.2.2 # via pytest filelock==3.15.4 # via virtualenv diff --git a/requirements/dev.txt b/requirements/dev.txt index b4ef0dfe74b..06a905e4b1e 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -66,7 +66,7 @@ distlib==0.3.8 # via virtualenv docutils==0.20.1 # via sphinx -exceptiongroup==1.1.2 +exceptiongroup==1.2.2 # via pytest filelock==3.12.2 # via virtualenv diff --git a/requirements/lint.txt b/requirements/lint.txt index d0cf9e44248..6424a9c147a 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -30,7 +30,7 @@ cryptography==43.0.0 # via trustme distlib==0.3.8 # via virtualenv -exceptiongroup==1.1.2 +exceptiongroup==1.2.2 # via pytest filelock==3.12.2 # via virtualenv diff --git a/requirements/test.txt b/requirements/test.txt index dfdcf8f7b1e..61497339cf8 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -36,7 +36,7 @@ coverage==7.6.1 # pytest-cov cryptography==43.0.0 # via trustme -exceptiongroup==1.1.2 +exceptiongroup==1.2.2 # via pytest freezegun==1.5.1 # via -r requirements/test.in From 17fa09f90e3409cf9f12f13f5c84b63446e72b0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 12:58:50 +0000 Subject: [PATCH 056/296] Bump pycparser from 2.21 to 2.22 (#8862) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [pycparser](https://github.com/eliben/pycparser) from 2.21 to 2.22.
Release notes

Sourced from pycparser's releases.

release_v2.22

What's Changed

New Contributors

Full Changelog: https://github.com/eliben/pycparser/compare/release_v2.21...release_v2.22

Changelog

Sourced from pycparser's changelog.

Commits
  • 129d32e Prepare for release 2.22
  • c3e2644 update CHANGES file for future changes
  • c500fb6 ply: Make generated lextab.py deterministic (#531)
  • f740995 Add support for Python 3.12 (#515)
  • 6cf69df New example to generate AST from scratch (#507)
  • 50a26ac Remove unneeded import in an example
  • d86a9e5 Remove from future imports from all files in this repo
  • a9f073e Remove from future imports in examples
  • 670979b Update SECURITY.md
  • 9e8cd29 Create a Security Policy (#499)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pycparser&package-manager=pip&previous-version=2.21&new-version=2.22)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index 113be2767ec..50817d4db17 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -34,7 +34,7 @@ packaging==24.1 # via gunicorn pycares==4.4.0 # via aiodns -pycparser==2.21 +pycparser==2.22 # via cffi uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 15e2e39f670..68cf03350d4 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -150,7 +150,7 @@ proxy-py==2.4.7 # via -r requirements/test.in pycares==4.4.0 # via aiodns -pycparser==2.21 +pycparser==2.22 # via cffi pydantic==2.2.0 # via python-on-whales diff --git a/requirements/dev.txt b/requirements/dev.txt index 06a905e4b1e..d2e718798e8 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -147,7 +147,7 @@ proxy-py==2.4.7 # via -r requirements/test.in pycares==4.4.0 # via aiodns -pycparser==2.21 +pycparser==2.22 # via cffi pydantic==2.2.0 # via python-on-whales diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index 4d1981d5e3b..a8c2eebe1c8 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -30,7 +30,7 @@ multidict==6.0.5 # yarl pycares==4.4.0 # via aiodns -pycparser==2.21 +pycparser==2.22 # via cffi yarl==1.9.4 # via -r requirements/runtime-deps.in diff --git a/requirements/test.txt b/requirements/test.txt index 61497339cf8..58d18c54f6f 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -75,7 +75,7 @@ proxy-py==2.4.7 # via -r requirements/test.in pycares==4.4.0 # via aiodns -pycparser==2.21 +pycparser==2.22 # via cffi pydantic==2.2.0 # via python-on-whales From 95e32fb2e1b16ec4348c93e4c8d48fc9445466a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 12:59:06 +0000 Subject: [PATCH 057/296] Bump charset-normalizer from 3.2.0 to 3.3.2 (#8863) Bumps [charset-normalizer](https://github.com/Ousret/charset_normalizer) from 3.2.0 to 3.3.2.
Release notes

Sourced from charset-normalizer's releases.

Version 3.3.2

3.3.2 (2023-10-31)

Fixed

  • Unintentional memory usage regression when using large payloads that match several encodings (#376)
  • Regression on some detection cases showcased in the documentation (#371)

Added

  • Noise (md) probe that identifies malformed Arabic representation due to the presence of letters in isolated form (credit to my wife, thanks!)

Version 3.3.1

3.3.1 (2023-10-22)

Changed

  • Optional mypyc compilation upgraded to version 1.6.1 for Python >= 3.8
  • Improved the general detection reliability based on reports from the community

Release 3.3.0

3.3.0 (2023-09-30)

Added

  • Allow to execute the CLI (e.g. normalizer) through python -m charset_normalizer.cli or python -m charset_normalizer
  • Support for 9 forgotten encodings that are supported by Python but unlisted in encoding.aliases as they have no alias (#323)

Removed

  • (internal) Redundant utils.is_ascii function and unused function is_private_use_only
  • (internal) charset_normalizer.assets is moved inside charset_normalizer.constant

Changed

  • (internal) Unicode code blocks in constants are updated using the latest v15.0.0 definition to improve detection
  • Optional mypyc compilation upgraded to version 1.5.1 for Python >= 3.8

Fixed

  • Unable to properly sort CharsetMatch when both chaos/noise and coherence were close due to an unreachable condition in __lt__ (#350)
Changelog

Sourced from charset-normalizer's changelog.

3.3.2 (2023-10-31)

Fixed

  • Unintentional memory usage regression when using large payload that match several encoding (#376)
  • Regression on some detection case showcased in the documentation (#371)

Added

  • Noise (md) probe that identify malformed arabic representation due to the presence of letters in isolated form (credit to my wife)

3.3.1 (2023-10-22)

Changed

  • Optional mypyc compilation upgraded to version 1.6.1 for Python >= 3.8
  • Improved the general detection reliability based on reports from the community

3.3.0 (2023-09-30)

Added

  • Allow to execute the CLI (e.g. normalizer) through python -m charset_normalizer.cli or python -m charset_normalizer
  • Support for 9 forgotten encoding that are supported by Python but unlisted in encoding.aliases as they have no alias (#323)

Removed

  • (internal) Redundant utils.is_ascii function and unused function is_private_use_only
  • (internal) charset_normalizer.assets is moved inside charset_normalizer.constant

Changed

  • (internal) Unicode code blocks in constants are updated using the latest v15.0.0 definition to improve detection
  • Optional mypyc compilation upgraded to version 1.5.1 for Python >= 3.8

Fixed

  • Unable to properly sort CharsetMatch when both chaos/noise and coherence were close due to an unreachable condition in __lt__ (#350)
Commits
  • 79dce48 :bug: Regression on some detection case showcased in the documentation (#371)...
  • a4b9b01 Bump github/codeql-action from 2.22.4 to 2.22.5 (#375)
  • dcc01cc Bump ossf/scorecard-action from 2.3.0 to 2.3.1 (#374)
  • 9cd402c Bump pytest from 7.4.2 to 7.4.3 (#373)
  • e274dcc :bug: Fix unintentional memory usage regression when using large payload that...
  • 07f3041 :arrow_up: Bump github/codeql-action from 2.22.3 to 2.22.4 (#370)
  • 5208644 :bookmark: Release 3.3.1 (#367)
  • 66966f1 :sparkle: Improve the detection around some cases (#366)
  • 49653a6 :arrow_up: Bump actions/setup-python from 4.7.0 to 4.7.1 (#359)
  • f6a66ed :arrow_up: Bump pypa/cibuildwheel from 2.16.0 to 2.16.2 (#361)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=charset-normalizer&package-manager=pip&previous-version=3.2.0&new-version=3.3.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- requirements/test.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 68cf03350d4..2ad79ce2686 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -42,7 +42,7 @@ cffi==1.17.0 # pycares cfgv==3.4.0 # via pre-commit -charset-normalizer==3.2.0 +charset-normalizer==3.3.2 # via requests cherry-picker==2.2.0 # via -r requirements/dev.in diff --git a/requirements/dev.txt b/requirements/dev.txt index d2e718798e8..41f343a7adc 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -42,7 +42,7 @@ cffi==1.17.0 # pycares cfgv==3.4.0 # via pre-commit -charset-normalizer==3.2.0 +charset-normalizer==3.3.2 # via requests cherry-picker==2.2.0 # via -r requirements/dev.in diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 977da96e6e2..ebab702f1b1 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -14,7 +14,7 @@ blockdiag==3.0.0 # via sphinxcontrib-blockdiag certifi==2024.7.4 # via requests -charset-normalizer==3.3.1 +charset-normalizer==3.3.2 # via requests click==8.1.6 # via towncrier diff --git a/requirements/doc.txt b/requirements/doc.txt index 371034a8849..efa46279ff5 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -14,7 +14,7 @@ blockdiag==3.0.0 # via sphinxcontrib-blockdiag certifi==2024.7.4 # via requests -charset-normalizer==3.3.1 +charset-normalizer==3.3.2 # via requests click==8.1.6 # via towncrier diff --git a/requirements/test.txt b/requirements/test.txt index 58d18c54f6f..05903cfa5a4 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -24,7 +24,7 @@ cffi==1.17.0 # via # cryptography # pycares -charset-normalizer==3.2.0 +charset-normalizer==3.3.2 # via requests click==8.1.6 # via From 7173b33b6026c933d001c3d12d3e8464c6b3812b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 13:25:38 +0000 Subject: [PATCH 058/296] Bump pydantic from 2.2.0 to 2.8.2 (#8864) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.2.0 to 2.8.2.
Release notes

Sourced from pydantic's releases.

v2.8.2 (2024-07-03)

What's Changed

Fixes

  • Fix issue with assertion caused by pluggable schema validator by @​dmontagu in #9838

Full Changelog: https://github.com/pydantic/pydantic/compare/v2.8.1...v2.8.2

v2.8.1 (2024-07-03)

What's Changed

Packaging

Fixes

New Contributors

Full Changelog: https://github.com/pydantic/pydantic/compare/v2.8.0...v2.8.1

v2.8.0 (2024-07-01)

The code released in v2.8.0 is functionally identical to that of v2.8.0b1.

Check out our blog post to learn more about the release highlights!

What's Changed

Packaging

New Features

... (truncated)

Changelog

Sourced from pydantic's changelog.

v2.8.2 (2024-07-03)

GitHub release

What's Changed

Fixes

  • Fix issue with assertion caused by pluggable schema validator by @​dmontagu in #9838

v2.8.1 (2024-07-03)

GitHub release

What's Changed

Packaging

Fixes

v2.8.0 (2024-07-01)

GitHub release

The code released in v2.8.0 is functionally identical to that of v2.8.0b1.

What's Changed

Packaging

New Features

... (truncated)

Commits
  • 4978ee2 update history
  • 0345929 v bump
  • d390a04 Fix issue with assertion caused by pluggable schema validator (#9838)
  • 040865f update history
  • 5a33e3b bump version
  • 2f9abb2 Bump pydantic-core to v2.20.1, pydantic-extra-types to v2.9.0 (#9832)
  • ce9c5f7 Remove spooky meetings file (#9824)
  • 6bdd6d1 Pedantic typo correction within explanation of Pydantic's root in 'pedantic' ...
  • 701ccde Fix list constraint json schema application (#9818)
  • 2a066a2 Bump ruff to v0.5.0 and pyright to v1.1.369 (#9801)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pydantic&package-manager=pip&previous-version=2.2.0&new-version=2.8.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 | 4 ++-- requirements/dev.txt | 4 ++-- requirements/lint.txt | 4 ++-- requirements/test.txt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 2ad79ce2686..9389b1a0701 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -152,9 +152,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.2.0 +pydantic==2.8.2 # via python-on-whales -pydantic-core==2.6.0 +pydantic-core==2.20.1 # via pydantic pyenchant==3.2.2 # via sphinxcontrib-spelling diff --git a/requirements/dev.txt b/requirements/dev.txt index 41f343a7adc..4c9b1c43f09 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -149,9 +149,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.2.0 +pydantic==2.8.2 # via python-on-whales -pydantic-core==2.6.0 +pydantic-core==2.20.1 # via pydantic pygments==2.18.0 # via diff --git a/requirements/lint.txt b/requirements/lint.txt index 6424a9c147a..5cfc111450b 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -66,9 +66,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.7.1 +pydantic==2.8.2 # via python-on-whales -pydantic-core==2.18.2 +pydantic-core==2.20.1 # via pydantic pygments==2.18.0 # via rich diff --git a/requirements/test.txt b/requirements/test.txt index 05903cfa5a4..4886cbb15e6 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -77,9 +77,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.2.0 +pydantic==2.8.2 # via python-on-whales -pydantic-core==2.6.0 +pydantic-core==2.20.1 # via pydantic pygments==2.18.0 # via rich From a74e4f0ad3824b4808da9816d87cd4637e6eabc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 13:35:51 +0000 Subject: [PATCH 059/296] Bump filelock from 3.12.2 to 3.15.4 (#8867) 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.12.2 to 3.15.4.
Release notes

Sourced from filelock's releases.

3.15.4

What's Changed

Full Changelog: https://github.com/tox-dev/filelock/compare/3.15.3...3.15.4

3.15.3

What's Changed

Full Changelog: https://github.com/tox-dev/filelock/compare/3.15.2...3.15.3

3.15.2

What's Changed

New Contributors

Full Changelog: https://github.com/tox-dev/filelock/compare/3.15.1...3.15.2

3.15.1

What's Changed

Full Changelog: https://github.com/tox-dev/filelock/compare/3.15.0...3.15.1

3.15.0

What's Changed

New Contributors

... (truncated)

Commits
  • 9a979df Pass file_lock as positional argument (#347)
  • 3a79343 Fix `TypeError: _CountedFileLock.init() got an unexpected keyword argumen...
  • 81d4cf9 Add test for virtualenv stability (#344)
  • 192f1ef Use a metaclass to implement the singleton pattern (#340)
  • 48788c5 Bump pypa/gh-action-pypi-publish from 1.8.14 to 1.9.0 (#341)
  • 7bd3b7b [pre-commit.ci] pre-commit autoupdate (#342)
  • 3d7b1a6 Hotfix: Restore init method; more robust initialization for singleton loc...
  • c64787f Don't initialize BaseFileLock when just returning existing instance (#334)
  • 87453f3 asyncio support (#332)
  • 0ee2e3c [pre-commit.ci] pre-commit autoupdate (#333)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=filelock&package-manager=pip&previous-version=3.12.2&new-version=3.15.4)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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/dev.txt | 2 +- requirements/lint.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/dev.txt b/requirements/dev.txt index 4c9b1c43f09..3f408533ec4 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -68,7 +68,7 @@ docutils==0.20.1 # via sphinx exceptiongroup==1.2.2 # via pytest -filelock==3.12.2 +filelock==3.15.4 # via virtualenv freezegun==1.5.1 # via diff --git a/requirements/lint.txt b/requirements/lint.txt index 5cfc111450b..94091021093 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -32,7 +32,7 @@ distlib==0.3.8 # via virtualenv exceptiongroup==1.2.2 # via pytest -filelock==3.12.2 +filelock==3.15.4 # via virtualenv freezegun==1.5.1 # via -r requirements/lint.in From 3b3156e0ff6fd87cdd4c6f737310ba18a56371e8 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 14:59:30 +0000 Subject: [PATCH 060/296] [PR #8846/4dd8c807 backport][3.11] Format status lines with f-strings (#8869) Co-authored-by: J. Nick Koston --- aiohttp/client_reqrep.py | 5 ++--- aiohttp/web_response.py | 4 +--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index bea76d84c39..0e67607f5d9 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -727,9 +727,8 @@ async def send(self, conn: "Connection") -> "ClientResponse": self.headers[hdrs.CONNECTION] = connection # status + headers - status_line = "{0} {1} HTTP/{v.major}.{v.minor}".format( - self.method, path, v=self.version - ) + v = self.version + status_line = f"{self.method} {path} HTTP/{v.major}.{v.minor}" await writer.write_headers(status_line, self.headers) coro = self.write_bytes(writer, conn) diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 78d3fe32949..ae22d587ab4 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -500,9 +500,7 @@ async def _write_headers(self) -> None: assert writer is not None # status line version = request.version - status_line = "HTTP/{}.{} {} {}".format( - version[0], version[1], self._status, self._reason - ) + status_line = f"HTTP/{version[0]}.{version[1]} {self._status} {self._reason}" await writer.write_headers(status_line, self._headers) async def write(self, data: bytes) -> None: From 5948290685c8a0fb460b3e9d021ef2f5057a8587 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:45:53 +0000 Subject: [PATCH 061/296] [PR #8846/4dd8c807 backport][3.10] Format status lines with f-strings (#8868) **This is a backport of PR #8846 as merged into master (4dd8c807774aee39f9d6fbe16f1ed11388aa9498).** ## What do these changes do? Format status lines with f-strings ## Are there changes in behavior for the user? no, maybe a tiny performance improvement ## Is it a substantial burden for the maintainers to support this? no Co-authored-by: J. Nick Koston --- aiohttp/client_reqrep.py | 5 ++--- aiohttp/web_response.py | 4 +--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index bea76d84c39..0e67607f5d9 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -727,9 +727,8 @@ async def send(self, conn: "Connection") -> "ClientResponse": self.headers[hdrs.CONNECTION] = connection # status + headers - status_line = "{0} {1} HTTP/{v.major}.{v.minor}".format( - self.method, path, v=self.version - ) + v = self.version + status_line = f"{self.method} {path} HTTP/{v.major}.{v.minor}" await writer.write_headers(status_line, self.headers) coro = self.write_bytes(writer, conn) diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 78d3fe32949..ae22d587ab4 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -500,9 +500,7 @@ async def _write_headers(self) -> None: assert writer is not None # status line version = request.version - status_line = "HTTP/{}.{} {} {}".format( - version[0], version[1], self._status, self._reason - ) + status_line = f"HTTP/{version[0]}.{version[1]} {self._status} {self._reason}" await writer.write_headers(status_line, self._headers) async def write(self, data: bytes) -> None: From b4092d96a1350d9d18bc2db787fdff8f784dbb3f Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Fri, 23 Aug 2024 16:46:01 +0100 Subject: [PATCH 062/296] No default Content-Type when no content (#8858) (#8871) (cherry picked from commit 26772ad320c1ee0efa5b91ae35ae5860a37cd709) --- CHANGES/8858.bugfix.rst | 1 + aiohttp/helpers.py | 6 ++++-- aiohttp/web_request.py | 6 +++--- aiohttp/web_response.py | 3 ++- tests/test_client_functional.py | 2 -- tests/test_web_functional.py | 15 +++++++++++++++ tests/test_web_response.py | 2 -- 7 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 CHANGES/8858.bugfix.rst diff --git a/CHANGES/8858.bugfix.rst b/CHANGES/8858.bugfix.rst new file mode 100644 index 00000000000..e4efa91a2fd --- /dev/null +++ b/CHANGES/8858.bugfix.rst @@ -0,0 +1 @@ +Stopped adding a default Content-Type header when response has no content -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index ccfa9d5e2fe..f759bddc099 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -762,7 +762,8 @@ def content_type(self) -> str: raw = self._headers.get(hdrs.CONTENT_TYPE) if self._stored_content_type != raw: self._parse_content_type(raw) - return self._content_type # type: ignore[return-value] + assert self._content_type is not None + return self._content_type @property def charset(self) -> Optional[str]: @@ -770,7 +771,8 @@ def charset(self) -> Optional[str]: raw = self._headers.get(hdrs.CONTENT_TYPE) if self._stored_content_type != raw: self._parse_content_type(raw) - return self._content_dict.get("charset") # type: ignore[union-attr] + assert self._content_dict is not None + return self._content_dict.get("charset") @property def content_length(self) -> Optional[int]: diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py index a485f0dcea6..a63d3074ea5 100644 --- a/aiohttp/web_request.py +++ b/aiohttp/web_request.py @@ -79,7 +79,7 @@ class FileField: filename: str file: io.BufferedReader content_type: str - headers: "CIMultiDictProxy[str]" + headers: CIMultiDictProxy[str] _TCHAR: Final[str] = string.digits + string.ascii_letters + r"!#$%&'*+.^_`|~-" @@ -169,7 +169,7 @@ def __init__( self._payload_writer = payload_writer self._payload = payload - self._headers = message.headers + self._headers: CIMultiDictProxy[str] = message.headers self._method = message.method self._version = message.version self._cache: Dict[str, Any] = {} @@ -493,7 +493,7 @@ def query_string(self) -> str: return self._rel_url.query_string @reify - def headers(self) -> "MultiMapping[str]": + def headers(self) -> CIMultiDictProxy[str]: """A case-insensitive multidict proxy with all headers.""" return self._headers diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index ae22d587ab4..95028a929ff 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -479,7 +479,8 @@ async def _prepare_headers(self) -> None: # https://datatracker.ietf.org/doc/html/rfc9112#section-6.1-13 if hdrs.TRANSFER_ENCODING in headers: del headers[hdrs.TRANSFER_ENCODING] - else: + elif self.content_length != 0: + # https://www.rfc-editor.org/rfc/rfc9110#section-8.3-5 headers.setdefault(hdrs.CONTENT_TYPE, "application/octet-stream") headers.setdefault(hdrs.DATE, rfc822_formatted_time()) headers.setdefault(hdrs.SERVER, SERVER_SOFTWARE) diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 872876d4a32..566c47522ce 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -727,7 +727,6 @@ async def handler(request): raw_headers = tuple((bytes(h), bytes(v)) for h, v in resp.raw_headers) assert raw_headers == ( (b"Content-Length", b"0"), - (b"Content-Type", b"application/octet-stream"), (b"Date", mock.ANY), (b"Server", mock.ANY), ) @@ -760,7 +759,6 @@ async def handler(request): assert raw_headers == ( (b"X-Empty", b""), (b"Content-Length", b"0"), - (b"Content-Type", b"application/octet-stream"), (b"Date", mock.ANY), (b"Server", mock.ANY), ) diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index ee61537068b..96a4f82ba9f 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -148,6 +148,21 @@ async def handler(request): assert resp.headers["Content-Length"] == "4" +@pytest.mark.parametrize("status", (201, 204, 404)) +async def test_default_content_type_no_body(aiohttp_client: Any, status: int) -> None: + async def handler(request): + return web.Response(status=status) + + app = web.Application() + app.router.add_get("/", handler) + client = await aiohttp_client(app) + + async with client.get("/") as resp: + assert resp.status == status + assert await resp.read() == b"" + assert "Content-Type" not in resp.headers + + async def test_response_before_complete(aiohttp_client: Any) -> None: async def handler(request): return web.Response(body=b"OK") diff --git a/tests/test_web_response.py b/tests/test_web_response.py index d1b407c090c..ad1286ca91e 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -1139,7 +1139,6 @@ async def test_send_headers_for_empty_body(buf, writer) -> None: Matches( "HTTP/1.1 200 OK\r\n" "Content-Length: 0\r\n" - "Content-Type: application/octet-stream\r\n" "Date: .+\r\n" "Server: .+\r\n\r\n" ) @@ -1182,7 +1181,6 @@ async def test_send_set_cookie_header(buf, writer) -> None: "HTTP/1.1 200 OK\r\n" "Content-Length: 0\r\n" "Set-Cookie: name=value\r\n" - "Content-Type: application/octet-stream\r\n" "Date: .+\r\n" "Server: .+\r\n\r\n" ) From f09f88d1eb7a089d83cb2426811a45256cbc8410 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Fri, 23 Aug 2024 16:46:17 +0100 Subject: [PATCH 063/296] No default Content-Type when no content (#8858) (#8872) (cherry picked from commit 26772ad320c1ee0efa5b91ae35ae5860a37cd709) --- CHANGES/8858.bugfix.rst | 1 + aiohttp/helpers.py | 6 ++++-- aiohttp/web_request.py | 6 +++--- aiohttp/web_response.py | 3 ++- tests/test_client_functional.py | 2 -- tests/test_web_functional.py | 15 +++++++++++++++ tests/test_web_response.py | 2 -- 7 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 CHANGES/8858.bugfix.rst diff --git a/CHANGES/8858.bugfix.rst b/CHANGES/8858.bugfix.rst new file mode 100644 index 00000000000..e4efa91a2fd --- /dev/null +++ b/CHANGES/8858.bugfix.rst @@ -0,0 +1 @@ +Stopped adding a default Content-Type header when response has no content -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index ccfa9d5e2fe..f759bddc099 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -762,7 +762,8 @@ def content_type(self) -> str: raw = self._headers.get(hdrs.CONTENT_TYPE) if self._stored_content_type != raw: self._parse_content_type(raw) - return self._content_type # type: ignore[return-value] + assert self._content_type is not None + return self._content_type @property def charset(self) -> Optional[str]: @@ -770,7 +771,8 @@ def charset(self) -> Optional[str]: raw = self._headers.get(hdrs.CONTENT_TYPE) if self._stored_content_type != raw: self._parse_content_type(raw) - return self._content_dict.get("charset") # type: ignore[union-attr] + assert self._content_dict is not None + return self._content_dict.get("charset") @property def content_length(self) -> Optional[int]: diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py index a485f0dcea6..a63d3074ea5 100644 --- a/aiohttp/web_request.py +++ b/aiohttp/web_request.py @@ -79,7 +79,7 @@ class FileField: filename: str file: io.BufferedReader content_type: str - headers: "CIMultiDictProxy[str]" + headers: CIMultiDictProxy[str] _TCHAR: Final[str] = string.digits + string.ascii_letters + r"!#$%&'*+.^_`|~-" @@ -169,7 +169,7 @@ def __init__( self._payload_writer = payload_writer self._payload = payload - self._headers = message.headers + self._headers: CIMultiDictProxy[str] = message.headers self._method = message.method self._version = message.version self._cache: Dict[str, Any] = {} @@ -493,7 +493,7 @@ def query_string(self) -> str: return self._rel_url.query_string @reify - def headers(self) -> "MultiMapping[str]": + def headers(self) -> CIMultiDictProxy[str]: """A case-insensitive multidict proxy with all headers.""" return self._headers diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index ae22d587ab4..95028a929ff 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -479,7 +479,8 @@ async def _prepare_headers(self) -> None: # https://datatracker.ietf.org/doc/html/rfc9112#section-6.1-13 if hdrs.TRANSFER_ENCODING in headers: del headers[hdrs.TRANSFER_ENCODING] - else: + elif self.content_length != 0: + # https://www.rfc-editor.org/rfc/rfc9110#section-8.3-5 headers.setdefault(hdrs.CONTENT_TYPE, "application/octet-stream") headers.setdefault(hdrs.DATE, rfc822_formatted_time()) headers.setdefault(hdrs.SERVER, SERVER_SOFTWARE) diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 872876d4a32..566c47522ce 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -727,7 +727,6 @@ async def handler(request): raw_headers = tuple((bytes(h), bytes(v)) for h, v in resp.raw_headers) assert raw_headers == ( (b"Content-Length", b"0"), - (b"Content-Type", b"application/octet-stream"), (b"Date", mock.ANY), (b"Server", mock.ANY), ) @@ -760,7 +759,6 @@ async def handler(request): assert raw_headers == ( (b"X-Empty", b""), (b"Content-Length", b"0"), - (b"Content-Type", b"application/octet-stream"), (b"Date", mock.ANY), (b"Server", mock.ANY), ) diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index ee61537068b..96a4f82ba9f 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -148,6 +148,21 @@ async def handler(request): assert resp.headers["Content-Length"] == "4" +@pytest.mark.parametrize("status", (201, 204, 404)) +async def test_default_content_type_no_body(aiohttp_client: Any, status: int) -> None: + async def handler(request): + return web.Response(status=status) + + app = web.Application() + app.router.add_get("/", handler) + client = await aiohttp_client(app) + + async with client.get("/") as resp: + assert resp.status == status + assert await resp.read() == b"" + assert "Content-Type" not in resp.headers + + async def test_response_before_complete(aiohttp_client: Any) -> None: async def handler(request): return web.Response(body=b"OK") diff --git a/tests/test_web_response.py b/tests/test_web_response.py index d1b407c090c..ad1286ca91e 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -1139,7 +1139,6 @@ async def test_send_headers_for_empty_body(buf, writer) -> None: Matches( "HTTP/1.1 200 OK\r\n" "Content-Length: 0\r\n" - "Content-Type: application/octet-stream\r\n" "Date: .+\r\n" "Server: .+\r\n\r\n" ) @@ -1182,7 +1181,6 @@ async def test_send_set_cookie_header(buf, writer) -> None: "HTTP/1.1 200 OK\r\n" "Content-Length: 0\r\n" "Set-Cookie: name=value\r\n" - "Content-Type: application/octet-stream\r\n" "Date: .+\r\n" "Server: .+\r\n\r\n" ) From 8fd19cb9f6c42989c540852ccf2fd89efa297ddd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 10:44:00 +0000 Subject: [PATCH 064/296] Bump typer from 0.12.4 to 0.12.5 (#8879) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [typer](https://github.com/fastapi/typer) from 0.12.4 to 0.12.5.
Release notes

Sourced from typer's releases.

0.12.5

Features

  • 💄 Unify the width of the Rich console for help and errors. PR #788 by @​racinmat.
  • 🚸 Improve assertion error message if a group is not a valid subclass. PR #425 by @​chrisburr.

Fixes

Refactors

Docs

Internal

Changelog

Sourced from typer's changelog.

0.12.5

Features

  • 💄 Unify the width of the Rich console for help and errors. PR #788 by @​racinmat.
  • 🚸 Improve assertion error message if a group is not a valid subclass. PR #425 by @​chrisburr.

Fixes

Refactors

Docs

Internal

Commits
  • 88aefd4 🔖 Release version 0.12.5
  • 3ac3644 📝 Update release notes
  • d93c0ac 🔨 Pre-install dependencies in Docker so that testing in Docker is faster (#954)
  • a337850 📝 Update release notes
  • a5b7557 ✅ Add needs_bash test fixture (#888)
  • 6cc1f9a 📝 Update release notes
  • fc2c54f 🐛 Ensure rich_markup_mode=None disables Rich formatting (#859)
  • f17bb06 📝 Update release notes
  • afac2b8 🐛 Fix sourcing of completion path for Git Bash (#801)
  • 6b35a70 📝 Update release notes
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=typer&package-manager=pip&previous-version=0.12.4&new-version=0.12.5)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 9389b1a0701..d825f618dcf 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -259,7 +259,7 @@ trustme==1.1.0 ; platform_machine != "i686" # via # -r requirements/lint.in # -r requirements/test.in -typer==0.12.4 +typer==0.12.5 # via python-on-whales typing-extensions==4.12.2 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 3f408533ec4..2f11c4a77a9 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -251,7 +251,7 @@ trustme==1.1.0 ; platform_machine != "i686" # via # -r requirements/lint.in # -r requirements/test.in -typer==0.12.4 +typer==0.12.5 # via python-on-whales typing-extensions==4.12.2 # via diff --git a/requirements/lint.txt b/requirements/lint.txt index 94091021093..8362d0edadc 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -103,7 +103,7 @@ tqdm==4.66.5 # via python-on-whales trustme==1.1.0 # via -r requirements/lint.in -typer==0.12.4 +typer==0.12.5 # via python-on-whales typing-extensions==4.12.2 # via diff --git a/requirements/test.txt b/requirements/test.txt index 4886cbb15e6..ef30207e747 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -119,7 +119,7 @@ tqdm==4.66.5 # via python-on-whales trustme==1.1.0 ; platform_machine != "i686" # via -r requirements/test.in -typer==0.12.4 +typer==0.12.5 # via python-on-whales typing-extensions==4.12.2 # via From 9b136b9934ccaf4f2db8fc941445d62f869d1bc0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 10:49:56 +0000 Subject: [PATCH 065/296] Bump pytz from 2023.3.post1 to 2024.1 (#8880) Bumps [pytz](https://github.com/stub42/pytz) from 2023.3.post1 to 2024.1.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pytz&package-manager=pip&previous-version=2023.3.post1&new-version=2024.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 d825f618dcf..db1b3f97582 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -188,7 +188,7 @@ python-on-whales==0.72.0 # via # -r requirements/lint.in # -r requirements/test.in -pytz==2023.3.post1 +pytz==2024.1 # via babel pyyaml==6.0.1 # via pre-commit diff --git a/requirements/dev.txt b/requirements/dev.txt index 2f11c4a77a9..f867f95a159 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -183,7 +183,7 @@ python-on-whales==0.72.0 # via # -r requirements/lint.in # -r requirements/test.in -pytz==2023.3.post1 +pytz==2024.1 # via babel pyyaml==6.0.1 # via pre-commit diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index ebab702f1b1..4c189f13158 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -48,7 +48,7 @@ pyenchant==3.2.2 # via sphinxcontrib-spelling pygments==2.18.0 # via sphinx -pytz==2023.3.post1 +pytz==2024.1 # via babel requests==2.32.3 # via sphinx diff --git a/requirements/doc.txt b/requirements/doc.txt index efa46279ff5..1623f18c928 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -46,7 +46,7 @@ pillow==9.5.0 # blockdiag pygments==2.18.0 # via sphinx -pytz==2023.3.post1 +pytz==2024.1 # via babel requests==2.32.3 # via sphinx From 6dd4c1048bfe839ff18f6714a2fd221df9771b42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 11:12:57 +0000 Subject: [PATCH 066/296] Bump mypy from 1.11.1 to 1.11.2 (#8881) Bumps [mypy](https://github.com/python/mypy) from 1.11.1 to 1.11.2.
Commits
  • 789f02c Bump version to 1.11.2
  • 917cc75 An alternative fix for a union-like literal string (#17639)
  • 7d805b3 Unwrap TypedDict item types before storing (#17640)
  • 32675dd Revert "Fix Literal strings containing pipe characters" (#17638)
  • 778542b Revert "Fix RawExpressionType.accept crash with --cache-fine-grained" (#1...
  • 14ab742 Bump version to 1.11.2+dev
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=mypy&package-manager=pip&previous-version=1.11.1&new-version=1.11.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index db1b3f97582..7cdc216c1ec 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -120,7 +120,7 @@ multidict==6.0.5 # -r requirements/multidict.in # -r requirements/runtime-deps.in # yarl -mypy==1.11.1 ; implementation_name == "cpython" +mypy==1.11.2 ; implementation_name == "cpython" # via # -r requirements/lint.in # -r requirements/test.in diff --git a/requirements/dev.txt b/requirements/dev.txt index f867f95a159..59fe5baa706 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -117,7 +117,7 @@ multidict==6.0.5 # via # -r requirements/runtime-deps.in # yarl -mypy==1.11.1 ; implementation_name == "cpython" +mypy==1.11.2 ; implementation_name == "cpython" # via # -r requirements/lint.in # -r requirements/test.in diff --git a/requirements/lint.txt b/requirements/lint.txt index 8362d0edadc..917aebf0f0e 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -48,7 +48,7 @@ markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py -mypy==1.11.1 ; implementation_name == "cpython" +mypy==1.11.2 ; implementation_name == "cpython" # via -r requirements/lint.in mypy-extensions==1.0.0 # via mypy diff --git a/requirements/test.txt b/requirements/test.txt index ef30207e747..cc98efc6d0f 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -61,7 +61,7 @@ multidict==6.0.5 # via # -r requirements/runtime-deps.in # yarl -mypy==1.11.1 ; implementation_name == "cpython" +mypy==1.11.2 ; implementation_name == "cpython" # via -r requirements/test.in mypy-extensions==1.0.0 # via mypy From cb9fa5c7424bd4216ed4b56e80ea9fe353a6903e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 11:18:52 +0000 Subject: [PATCH 067/296] Bump pyyaml from 6.0.1 to 6.0.2 (#8866) Bumps [pyyaml](https://github.com/yaml/pyyaml) from 6.0.1 to 6.0.2.
Release notes

Sourced from pyyaml's releases.

6.0.2

What's Changed

  • Support for Cython 3.x and Python 3.13.

Full Changelog: https://github.com/yaml/pyyaml/compare/6.0.1...6.0.2

6.0.2rc1

  • Support for extension build with Cython 3.x
  • Support for Python 3.13
  • Added PyPI wheels for musllinux on aarch64
Changelog

Sourced from pyyaml's changelog.

6.0.2 (2024-08-06)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pyyaml&package-manager=pip&previous-version=6.0.1&new-version=6.0.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 7cdc216c1ec..025d91d6bc6 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -190,7 +190,7 @@ python-on-whales==0.72.0 # -r requirements/test.in pytz==2024.1 # via babel -pyyaml==6.0.1 +pyyaml==6.0.2 # via pre-commit re-assert==1.1.0 # via -r requirements/test.in diff --git a/requirements/dev.txt b/requirements/dev.txt index 59fe5baa706..4510091a134 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -185,7 +185,7 @@ python-on-whales==0.72.0 # -r requirements/test.in pytz==2024.1 # via babel -pyyaml==6.0.1 +pyyaml==6.0.2 # via pre-commit re-assert==1.1.0 # via -r requirements/test.in diff --git a/requirements/lint.txt b/requirements/lint.txt index 917aebf0f0e..afc0a3e5b0d 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -82,7 +82,7 @@ python-dateutil==2.9.0.post0 # via freezegun python-on-whales==0.72.0 # via -r requirements/lint.in -pyyaml==6.0.1 +pyyaml==6.0.2 # via pre-commit requests==2.32.3 # via python-on-whales From d73e5c4be10b8ea30c05054f71b5d0dc7f3d4736 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 11:35:08 +0000 Subject: [PATCH 068/296] Bump platformdirs from 3.10.0 to 4.2.2 (#8883) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [platformdirs](https://github.com/platformdirs/platformdirs) from 3.10.0 to 4.2.2.
Release notes

Sourced from platformdirs's releases.

4.2.2

What's Changed

New Contributors

Full Changelog: https://github.com/platformdirs/platformdirs/compare/4.2.1...4.2.2

4.2.1

What's Changed

New Contributors

Full Changelog: https://github.com/platformdirs/platformdirs/compare/4.2.0...4.2.1

4.2.0

What's Changed

New Contributors

Full Changelog: https://github.com/platformdirs/platformdirs/compare/4.1.0...4.2.0

4.1.0

What's Changed

New Contributors

... (truncated)

Changelog

Sourced from platformdirs's changelog.

platformdirs Changelog

platformdirs 4.1.0 (2024-01-XX)

  • Add convenience methods iter_{config,cache,data,runtime}_{dirs,paths}.

platformdirs 4.0.0 (2023-11-10)

  • UNIX: revert site_cache_dir to use /var/cache instead of /var/tmp

platformdirs 3.8.1 (2023-07-06)

  • BSD: provide a fallback for user_runtime_dir

platformdirs 3.8.0 (2023-06-22)

  • Add missing user media directory docs

platformdirs 3.7.0 (2023-06-20)

  • Have user_runtime_dir return /var/run/user/uid for *BSD

platformdirs 3.6.0 (2023-06-19)

  • introduce user_downloads_dir

platformdirs 3.5.3 (2023-06-09)

  • Use ruff

platformdirs 3.5.2 (2023-05-30)

  • test with 3.12.0.b1

platformdirs 3.5.1 (2023-05-11)

  • Add 3.12 support
  • Add tox.ini to sdist
  • removing Windows versions
  • Better handling for UNIX support

platformdirs 3.5.0 (2023-04-27)

  • introduce user_music_dir

platformdirs 3.4.0 (2023-04-26)

  • introduce user_videos_dir

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=platformdirs&package-manager=pip&previous-version=3.10.0&new-version=4.2.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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/dev.txt | 2 +- requirements/lint.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/dev.txt b/requirements/dev.txt index 4510091a134..f9cb6106edf 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -137,7 +137,7 @@ pillow==9.5.0 # blockdiag pip-tools==7.4.1 # via -r requirements/dev.in -platformdirs==3.10.0 +platformdirs==4.2.2 # via virtualenv pluggy==1.5.0 # via pytest diff --git a/requirements/lint.txt b/requirements/lint.txt index afc0a3e5b0d..1184ed4653b 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -56,7 +56,7 @@ nodeenv==1.9.1 # via pre-commit packaging==24.1 # via pytest -platformdirs==3.10.0 +platformdirs==4.2.2 # via virtualenv pluggy==1.5.0 # via pytest From 996204af9baac83797d30531c154f90fadd98476 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 12:34:33 +0000 Subject: [PATCH 069/296] Bump webcolors from 1.11.1 to 24.8.0 (#8865) Bumps [webcolors](https://github.com/ubernostrum/webcolors) from 1.11.1 to 24.8.0.
Changelog

Sourced from webcolors's changelog.

.. _changelog:

Changelog

This document lists the changes in each release of webcolors.

Version numbering

This library currently tracks its version numbers using the YY.MM.MICRO form of Calendar Versioning <https://calver.org>_ ("CalVer"), in which the first two components of the version number are the (two-digit) year and (non-zero-padded) month of the release date, while the third component is an incrementing value for releases occurring in that month. For example, the first release issued in January 2025 would have a version number of 25.1.0; a subsequent release in the same month would be 25.1.1; a release the following month (February) would be 25.2.0.

The CalVer system was adopted for this library in 2024, and the first release to use a CalVer version number was 24.6.0.

API stability and deprecations

The API stability/deprecation policy for this library is as follows:

  • The supported stable public API of this library is the set of symbols which are exported by its __all__ declaration and which are documented in this documentation. For classes exported there, the supported stable public API is the set of methods and attributes of those classes whose names do not begin with one or more underscore (_) characters and which are documented in this documentation.

  • When a public API is to be removed, or undergo a backwards-incompatible change, it will emit a deprecation warning which serves as notice of the intended removal or change, and which will give a date -- which will always be at least in the next calendar year after the first release which emits the deprecation warning -- past which the removal or change may occur without further warning.

  • Security fixes, and fixes for high-severity bugs (such as those which might cause unrecoverable crash or data loss), are not required to emit deprecation warnings, and may -- if needed -- impose backwards-incompatible change in any release. If this occurs, this changelog document will contain a note explaining why the usual deprecation process could not be followed for that case.

... (truncated)

Commits
  • ec0702e Release 24.8.0.
  • a9a668a Fix reST formatting in testing docs.
  • dee9d46 Add first draft of names() function (refs #20).
  • bb87686 Release 24.6.0.
  • 41046b0 Reorganize and update the FAQ.
  • 0f865b2 Slight clarification in testing docs.
  • 368e2c5 Minor word-order change for version references in docs.
  • 516e863 Small modernization of Python idioms.
  • 68ba427 Be absolutely clear about the mappings no longer being supported API.
  • 785d6da Update the code comment on gray/grey choices.
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=webcolors&package-manager=pip&previous-version=1.11.1&new-version=24.8.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 025d91d6bc6..f921bb0e786 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -283,7 +283,7 @@ virtualenv==20.26.3 # via pre-commit wait-for-it==2.2.2 # via -r requirements/test.in -webcolors==1.11.1 +webcolors==24.8.0 # via blockdiag wheel==0.37.0 # via pip-tools diff --git a/requirements/dev.txt b/requirements/dev.txt index f9cb6106edf..35a83a15360 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -275,7 +275,7 @@ virtualenv==20.26.3 # via pre-commit wait-for-it==2.2.2 # via -r requirements/test.in -webcolors==1.13 +webcolors==24.8.0 # via blockdiag wheel==0.41.0 # via pip-tools diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 4c189f13158..57bc9bc47b6 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -88,7 +88,7 @@ towncrier==23.11.0 # sphinxcontrib-towncrier urllib3==2.2.2 # via requests -webcolors==1.13 +webcolors==24.8.0 # via blockdiag zipp==3.20.0 # via diff --git a/requirements/doc.txt b/requirements/doc.txt index 1623f18c928..91ad44582d4 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -83,7 +83,7 @@ towncrier==23.11.0 # sphinxcontrib-towncrier urllib3==2.2.2 # via requests -webcolors==1.13 +webcolors==24.8.0 # via blockdiag zipp==3.20.0 # via From 400feb641e51f3b9afe0eee9c73cafcb8a0d3744 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 26 Aug 2024 15:16:12 +0100 Subject: [PATCH 070/296] Return 500 error when handler has wrong return type (#8845) (#8891) (cherry picked from commit 48a5e07ad833bd1a8fcb2ce6f85a41ad0cef9dc6) --- CHANGES/8845.bugfix.rst | 1 + aiohttp/web_protocol.py | 36 ++++++++++++++++++------------------ tests/test_web_functional.py | 17 ++++------------- 3 files changed, 23 insertions(+), 31 deletions(-) create mode 100644 CHANGES/8845.bugfix.rst diff --git a/CHANGES/8845.bugfix.rst b/CHANGES/8845.bugfix.rst new file mode 100644 index 00000000000..ff0016ac14b --- /dev/null +++ b/CHANGES/8845.bugfix.rst @@ -0,0 +1 @@ +Changed behaviour when returning an invalid response to send a 500 response -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index 39e1c8be50e..a4941d103ef 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -38,7 +38,7 @@ from .log import access_logger, server_logger from .streams import EMPTY_PAYLOAD, StreamReader from .tcp_helpers import tcp_keepalive -from .web_exceptions import HTTPException +from .web_exceptions import HTTPException, HTTPInternalServerError from .web_log import AccessLogger from .web_request import BaseRequest from .web_response import Response, StreamResponse @@ -464,16 +464,16 @@ async def _handle_request( self._current_request = None except HTTPException as exc: resp = exc - reset = await self.finish_response(request, resp, start_time) + resp, reset = await self.finish_response(request, resp, start_time) except asyncio.CancelledError: raise except asyncio.TimeoutError as exc: self.log_debug("Request handler timed out.", exc_info=exc) resp = self.handle_error(request, 504) - reset = await self.finish_response(request, resp, start_time) + resp, reset = await self.finish_response(request, resp, start_time) except Exception as exc: resp = self.handle_error(request, 500, exc) - reset = await self.finish_response(request, resp, start_time) + resp, reset = await self.finish_response(request, resp, start_time) else: # Deprecation warning (See #2415) if getattr(resp, "__http_exception__", False): @@ -484,7 +484,7 @@ async def _handle_request( DeprecationWarning, ) - reset = await self.finish_response(request, resp, start_time) + resp, reset = await self.finish_response(request, resp, start_time) finally: self._handler_waiter.set_result(None) @@ -584,10 +584,6 @@ async def start(self) -> None: except asyncio.CancelledError: self.log_debug("Ignored premature client disconnection ") break - except RuntimeError as exc: - if self.debug: - self.log_exception("Unhandled runtime exception", exc_info=exc) - self.force_close() except Exception as exc: self.log_exception("Unhandled exception", exc_info=exc) self.force_close() @@ -616,7 +612,7 @@ async def start(self) -> None: async def finish_response( self, request: BaseRequest, resp: StreamResponse, start_time: float - ) -> bool: + ) -> Tuple[StreamResponse, bool]: """Prepare the response and write_eof, then log access. This has to @@ -635,22 +631,26 @@ async def finish_response( prepare_meth = resp.prepare except AttributeError: if resp is None: - raise RuntimeError("Missing return " "statement on request handler") + self.log_exception("Missing return statement on request handler") else: - raise RuntimeError( - "Web-handler should return " - "a response instance, " + self.log_exception( + "Web-handler should return a response instance, " "got {!r}".format(resp) ) + exc = HTTPInternalServerError() + resp = Response( + status=exc.status, reason=exc.reason, text=exc.text, headers=exc.headers + ) + prepare_meth = resp.prepare try: await prepare_meth(request) await resp.write_eof() except ConnectionError: self.log_access(request, resp, start_time) - return True - else: - self.log_access(request, resp, start_time) - return False + return resp, True + + self.log_access(request, resp, start_time) + return resp, False def handle_error( self, diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index 96a4f82ba9f..431971de288 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -101,12 +101,8 @@ async def handler(request): server = await aiohttp_server(app, logger=logger) client = await aiohttp_client(server) - with pytest.raises(aiohttp.ServerDisconnectedError): - await client.get("/") - - logger.exception.assert_called_with( - "Unhandled runtime exception", exc_info=mock.ANY - ) + async with client.get("/") as resp: + assert resp.status == 500 async def test_handler_returns_none(aiohttp_server, aiohttp_client) -> None: @@ -121,13 +117,8 @@ async def handler(request): server = await aiohttp_server(app, logger=logger) client = await aiohttp_client(server) - with pytest.raises(aiohttp.ServerDisconnectedError): - await client.get("/") - - # Actual error text is placed in exc_info - logger.exception.assert_called_with( - "Unhandled runtime exception", exc_info=mock.ANY - ) + async with client.get("/") as resp: + assert resp.status == 500 async def test_head_returns_empty_body(aiohttp_client) -> None: From 86b97776aa454984daf2a9c42b686eb05b65bdf7 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 26 Aug 2024 15:16:23 +0100 Subject: [PATCH 071/296] Return 500 error when handler has wrong return type (#8845) (#8892) (cherry picked from commit 48a5e07ad833bd1a8fcb2ce6f85a41ad0cef9dc6) --- CHANGES/8845.bugfix.rst | 1 + aiohttp/web_protocol.py | 36 ++++++++++++++++++------------------ tests/test_web_functional.py | 17 ++++------------- 3 files changed, 23 insertions(+), 31 deletions(-) create mode 100644 CHANGES/8845.bugfix.rst diff --git a/CHANGES/8845.bugfix.rst b/CHANGES/8845.bugfix.rst new file mode 100644 index 00000000000..ff0016ac14b --- /dev/null +++ b/CHANGES/8845.bugfix.rst @@ -0,0 +1 @@ +Changed behaviour when returning an invalid response to send a 500 response -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index 39e1c8be50e..a4941d103ef 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -38,7 +38,7 @@ from .log import access_logger, server_logger from .streams import EMPTY_PAYLOAD, StreamReader from .tcp_helpers import tcp_keepalive -from .web_exceptions import HTTPException +from .web_exceptions import HTTPException, HTTPInternalServerError from .web_log import AccessLogger from .web_request import BaseRequest from .web_response import Response, StreamResponse @@ -464,16 +464,16 @@ async def _handle_request( self._current_request = None except HTTPException as exc: resp = exc - reset = await self.finish_response(request, resp, start_time) + resp, reset = await self.finish_response(request, resp, start_time) except asyncio.CancelledError: raise except asyncio.TimeoutError as exc: self.log_debug("Request handler timed out.", exc_info=exc) resp = self.handle_error(request, 504) - reset = await self.finish_response(request, resp, start_time) + resp, reset = await self.finish_response(request, resp, start_time) except Exception as exc: resp = self.handle_error(request, 500, exc) - reset = await self.finish_response(request, resp, start_time) + resp, reset = await self.finish_response(request, resp, start_time) else: # Deprecation warning (See #2415) if getattr(resp, "__http_exception__", False): @@ -484,7 +484,7 @@ async def _handle_request( DeprecationWarning, ) - reset = await self.finish_response(request, resp, start_time) + resp, reset = await self.finish_response(request, resp, start_time) finally: self._handler_waiter.set_result(None) @@ -584,10 +584,6 @@ async def start(self) -> None: except asyncio.CancelledError: self.log_debug("Ignored premature client disconnection ") break - except RuntimeError as exc: - if self.debug: - self.log_exception("Unhandled runtime exception", exc_info=exc) - self.force_close() except Exception as exc: self.log_exception("Unhandled exception", exc_info=exc) self.force_close() @@ -616,7 +612,7 @@ async def start(self) -> None: async def finish_response( self, request: BaseRequest, resp: StreamResponse, start_time: float - ) -> bool: + ) -> Tuple[StreamResponse, bool]: """Prepare the response and write_eof, then log access. This has to @@ -635,22 +631,26 @@ async def finish_response( prepare_meth = resp.prepare except AttributeError: if resp is None: - raise RuntimeError("Missing return " "statement on request handler") + self.log_exception("Missing return statement on request handler") else: - raise RuntimeError( - "Web-handler should return " - "a response instance, " + self.log_exception( + "Web-handler should return a response instance, " "got {!r}".format(resp) ) + exc = HTTPInternalServerError() + resp = Response( + status=exc.status, reason=exc.reason, text=exc.text, headers=exc.headers + ) + prepare_meth = resp.prepare try: await prepare_meth(request) await resp.write_eof() except ConnectionError: self.log_access(request, resp, start_time) - return True - else: - self.log_access(request, resp, start_time) - return False + return resp, True + + self.log_access(request, resp, start_time) + return resp, False def handle_error( self, diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index 96a4f82ba9f..431971de288 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -101,12 +101,8 @@ async def handler(request): server = await aiohttp_server(app, logger=logger) client = await aiohttp_client(server) - with pytest.raises(aiohttp.ServerDisconnectedError): - await client.get("/") - - logger.exception.assert_called_with( - "Unhandled runtime exception", exc_info=mock.ANY - ) + async with client.get("/") as resp: + assert resp.status == 500 async def test_handler_returns_none(aiohttp_server, aiohttp_client) -> None: @@ -121,13 +117,8 @@ async def handler(request): server = await aiohttp_server(app, logger=logger) client = await aiohttp_client(server) - with pytest.raises(aiohttp.ServerDisconnectedError): - await client.get("/") - - # Actual error text is placed in exc_info - logger.exception.assert_called_with( - "Unhandled runtime exception", exc_info=mock.ANY - ) + async with client.get("/") as resp: + assert resp.status == 500 async def test_head_returns_empty_body(aiohttp_client) -> None: From b2e1294f700eae58a87085b6342d7eb5ed105700 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 15:16:42 +0100 Subject: [PATCH 072/296] [PR #5344/bff76313 backport][3.11] Fix StreamResponse.prepared not returning True after EOF is sent (#8894) **This is a backport of PR #5344 as merged into master (bff76313298d8b2c7539f9dc496801ee86ab7097).** --- CHANGES/5343.bugfix | 1 + aiohttp/web_response.py | 2 +- tests/test_web_response.py | 15 ++++++++++----- 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 CHANGES/5343.bugfix diff --git a/CHANGES/5343.bugfix b/CHANGES/5343.bugfix new file mode 100644 index 00000000000..4e33071ea94 --- /dev/null +++ b/CHANGES/5343.bugfix @@ -0,0 +1 @@ +Fixed StreamResponse.prepared to return True after EOF is sent -- by :user:`arthurdarcet`. diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 95028a929ff..7074542621b 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -102,7 +102,7 @@ def __init__( @property def prepared(self) -> bool: - return self._payload_writer is not None + return self._eof_sent or self._payload_writer is not None @property def task(self) -> "Optional[asyncio.Task[None]]": diff --git a/tests/test_web_response.py b/tests/test_web_response.py index ad1286ca91e..c3dab10c310 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -773,11 +773,8 @@ async def test___repr___after_eof() -> None: resp = StreamResponse() await resp.prepare(make_request("GET", "/")) - assert resp.prepared - await resp.write(b"data") await resp.write_eof() - assert not resp.prepared resp_repr = repr(resp) assert resp_repr == "" @@ -1243,14 +1240,22 @@ def test_content_type_with_set_body() -> None: assert resp.content_type == "application/octet-stream" -def test_started_when_not_started() -> None: +def test_prepared_when_not_started() -> None: resp = StreamResponse() assert not resp.prepared -async def test_started_when_started() -> None: +async def test_prepared_when_started() -> None: + resp = StreamResponse() + await resp.prepare(make_request("GET", "/")) + assert resp.prepared + + +async def test_prepared_after_eof() -> None: resp = StreamResponse() await resp.prepare(make_request("GET", "/")) + await resp.write(b"data") + await resp.write_eof() assert resp.prepared From b78cef374c1e0bcce62faef144b9f5067c3bf53e Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 15:16:55 +0100 Subject: [PATCH 073/296] [PR #5344/bff76313 backport][3.10] Fix StreamResponse.prepared not returning True after EOF is sent (#8893) **This is a backport of PR #5344 as merged into master (bff76313298d8b2c7539f9dc496801ee86ab7097).** Co-authored-by: Arthur Darcet --- CHANGES/5343.bugfix | 1 + aiohttp/web_response.py | 2 +- tests/test_web_response.py | 15 ++++++++++----- 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 CHANGES/5343.bugfix diff --git a/CHANGES/5343.bugfix b/CHANGES/5343.bugfix new file mode 100644 index 00000000000..4e33071ea94 --- /dev/null +++ b/CHANGES/5343.bugfix @@ -0,0 +1 @@ +Fixed StreamResponse.prepared to return True after EOF is sent -- by :user:`arthurdarcet`. diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 95028a929ff..7074542621b 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -102,7 +102,7 @@ def __init__( @property def prepared(self) -> bool: - return self._payload_writer is not None + return self._eof_sent or self._payload_writer is not None @property def task(self) -> "Optional[asyncio.Task[None]]": diff --git a/tests/test_web_response.py b/tests/test_web_response.py index ad1286ca91e..c3dab10c310 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -773,11 +773,8 @@ async def test___repr___after_eof() -> None: resp = StreamResponse() await resp.prepare(make_request("GET", "/")) - assert resp.prepared - await resp.write(b"data") await resp.write_eof() - assert not resp.prepared resp_repr = repr(resp) assert resp_repr == "" @@ -1243,14 +1240,22 @@ def test_content_type_with_set_body() -> None: assert resp.content_type == "application/octet-stream" -def test_started_when_not_started() -> None: +def test_prepared_when_not_started() -> None: resp = StreamResponse() assert not resp.prepared -async def test_started_when_started() -> None: +async def test_prepared_when_started() -> None: + resp = StreamResponse() + await resp.prepare(make_request("GET", "/")) + assert resp.prepared + + +async def test_prepared_after_eof() -> None: resp = StreamResponse() await resp.prepare(make_request("GET", "/")) + await resp.write(b"data") + await resp.write_eof() assert resp.prepared From 275985cbbd034e83053a775cf3e21b0a44d71c21 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 16:39:40 +0100 Subject: [PATCH 074/296] [PR #8874/d6a677d1 backport][3.11] Update testing utility examples (#8897) **This is a backport of PR #8874 as merged into master (d6a677d1dee563a7e6be7d68da015d824ecbfe80).** Co-authored-by: Sam Bull --- docs/testing.rst | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index 828b5072b4d..a7b93e714f6 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -449,14 +449,12 @@ Framework Agnostic Utilities High level test creation:: - from aiohttp.test_utils import TestClient, TestServer, loop_context + from aiohttp.test_utils import TestClient, TestServer from aiohttp import request - # loop_context is provided as a utility. You can use any - # asyncio.BaseEventLoop class in its place. - with loop_context() as loop: + async def test(): app = _create_example_app() - with TestClient(TestServer(app), loop=loop) as client: + async with TestClient(TestServer(app)) as client: async def test_get_route(): nonlocal client @@ -465,7 +463,7 @@ High level test creation:: text = await resp.text() assert "Hello, world" in text - loop.run_until_complete(test_get_route()) + await test_get_route() If it's preferred to handle the creation / teardown on a more granular @@ -473,10 +471,10 @@ basis, the TestClient object can be used directly:: from aiohttp.test_utils import TestClient, TestServer - with loop_context() as loop: + async def test(): app = _create_example_app() - client = TestClient(TestServer(app), loop=loop) - loop.run_until_complete(client.start_server()) + client = TestClient(TestServer(app)) + await client.start_server() root = "http://127.0.0.1:{}".format(port) async def test_get_route(): @@ -485,8 +483,8 @@ basis, the TestClient object can be used directly:: text = await resp.text() assert "Hello, world" in text - loop.run_until_complete(test_get_route()) - loop.run_until_complete(client.close()) + await test_get_route() + await client.close() A full list of the utilities provided can be found at the From 4179aba72d570854d69294344f2e12c0d62af144 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 16:39:58 +0100 Subject: [PATCH 075/296] [PR #8874/d6a677d1 backport][3.10] Update testing utility examples (#8896) **This is a backport of PR #8874 as merged into master (d6a677d1dee563a7e6be7d68da015d824ecbfe80).** Co-authored-by: Sam Bull --- docs/testing.rst | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index 828b5072b4d..a7b93e714f6 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -449,14 +449,12 @@ Framework Agnostic Utilities High level test creation:: - from aiohttp.test_utils import TestClient, TestServer, loop_context + from aiohttp.test_utils import TestClient, TestServer from aiohttp import request - # loop_context is provided as a utility. You can use any - # asyncio.BaseEventLoop class in its place. - with loop_context() as loop: + async def test(): app = _create_example_app() - with TestClient(TestServer(app), loop=loop) as client: + async with TestClient(TestServer(app)) as client: async def test_get_route(): nonlocal client @@ -465,7 +463,7 @@ High level test creation:: text = await resp.text() assert "Hello, world" in text - loop.run_until_complete(test_get_route()) + await test_get_route() If it's preferred to handle the creation / teardown on a more granular @@ -473,10 +471,10 @@ basis, the TestClient object can be used directly:: from aiohttp.test_utils import TestClient, TestServer - with loop_context() as loop: + async def test(): app = _create_example_app() - client = TestClient(TestServer(app), loop=loop) - loop.run_until_complete(client.start_server()) + client = TestClient(TestServer(app)) + await client.start_server() root = "http://127.0.0.1:{}".format(port) async def test_get_route(): @@ -485,8 +483,8 @@ basis, the TestClient object can be used directly:: text = await resp.text() assert "Hello, world" in text - loop.run_until_complete(test_get_route()) - loop.run_until_complete(client.close()) + await test_get_route() + await client.close() A full list of the utilities provided can be found at the From a74e8187bf55679772c3771036ce74ca28ddfa36 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 26 Aug 2024 17:27:33 +0100 Subject: [PATCH 076/296] Fix server disconnect when error after 100-continue (#8876) (#8899) (cherry picked from commit e058cbd14dd754c2b0138fdef28df58c5e3a2c5f) --- CHANGES/8876.bugfix.rst | 1 + aiohttp/web_urldispatcher.py | 2 ++ tests/test_web_functional.py | 16 +++++++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 CHANGES/8876.bugfix.rst diff --git a/CHANGES/8876.bugfix.rst b/CHANGES/8876.bugfix.rst new file mode 100644 index 00000000000..539eeb4c7d3 --- /dev/null +++ b/CHANGES/8876.bugfix.rst @@ -0,0 +1 @@ +Fixed error handling after 100-continue so server sends 500 response instead of disconnecting -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 558fb7d0c9b..aee7aecd2a9 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -341,6 +341,8 @@ async def _default_expect_handler(request: Request) -> None: if request.version == HttpVersion11: if expect.lower() == "100-continue": await request.writer.write(b"HTTP/1.1 100 Continue\r\n\r\n") + # Reset output_size as we haven't started the main body yet. + request.writer.output_size = 0 else: raise HTTPExpectationFailed(text="Unknown Expect: %s" % expect) diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index 431971de288..6f612ffc011 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -4,7 +4,7 @@ import pathlib import socket import zlib -from typing import Any, Optional +from typing import Any, NoReturn, Optional from unittest import mock import pytest @@ -121,6 +121,20 @@ async def handler(request): assert resp.status == 500 +async def test_handler_returns_not_response_after_100expect( + aiohttp_server, aiohttp_client +) -> None: + async def handler(request: web.Request) -> NoReturn: + raise Exception("foo") + + app = web.Application() + app.router.add_get("/", handler) + client = await aiohttp_client(app) + + async with client.get("/", expect100=True) as resp: + assert resp.status == 500 + + async def test_head_returns_empty_body(aiohttp_client) -> None: async def handler(request): return web.Response(body=b"test") From e2631c7c058796ac8fab88722cffd582300cdc0e Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 26 Aug 2024 20:01:21 +0100 Subject: [PATCH 077/296] Fix server disconnect when error after 100-continue (#8876) (#8900) (cherry picked from commit e058cbd14dd754c2b0138fdef28df58c5e3a2c5f) --- CHANGES/8876.bugfix.rst | 1 + aiohttp/web_urldispatcher.py | 2 ++ tests/test_web_functional.py | 16 +++++++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 CHANGES/8876.bugfix.rst diff --git a/CHANGES/8876.bugfix.rst b/CHANGES/8876.bugfix.rst new file mode 100644 index 00000000000..539eeb4c7d3 --- /dev/null +++ b/CHANGES/8876.bugfix.rst @@ -0,0 +1 @@ +Fixed error handling after 100-continue so server sends 500 response instead of disconnecting -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 558fb7d0c9b..aee7aecd2a9 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -341,6 +341,8 @@ async def _default_expect_handler(request: Request) -> None: if request.version == HttpVersion11: if expect.lower() == "100-continue": await request.writer.write(b"HTTP/1.1 100 Continue\r\n\r\n") + # Reset output_size as we haven't started the main body yet. + request.writer.output_size = 0 else: raise HTTPExpectationFailed(text="Unknown Expect: %s" % expect) diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index 431971de288..6f612ffc011 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -4,7 +4,7 @@ import pathlib import socket import zlib -from typing import Any, Optional +from typing import Any, NoReturn, Optional from unittest import mock import pytest @@ -121,6 +121,20 @@ async def handler(request): assert resp.status == 500 +async def test_handler_returns_not_response_after_100expect( + aiohttp_server, aiohttp_client +) -> None: + async def handler(request: web.Request) -> NoReturn: + raise Exception("foo") + + app = web.Application() + app.router.add_get("/", handler) + client = await aiohttp_client(app) + + async with client.get("/", expect100=True) as resp: + assert resp.status == 500 + + async def test_head_returns_empty_body(aiohttp_client) -> None: async def handler(request): return web.Response(body=b"test") From 68629b523f4f91651ab75b23a534881794f82a26 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 26 Aug 2024 09:25:02 -1000 Subject: [PATCH 078/296] Cache the hash generation of the ConnectionKey (#8895) --- aiohttp/client_reqrep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 0e67607f5d9..d8578a5daed 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -209,7 +209,7 @@ def _merge_ssl_params( return ssl -@attr.s(auto_attribs=True, slots=True, frozen=True) +@attr.s(auto_attribs=True, slots=True, frozen=True, cache_hash=True) class ConnectionKey: # the key should contain an information about used proxy / TLS # to prevent reusing wrong connections from a pool From 804e16c39b8cd91cdd092e68ffcd73b2f724e05c Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 26 Aug 2024 20:59:45 +0100 Subject: [PATCH 079/296] Ensure error is raised when reading from closed client response. (#8878) (#8902) --- CHANGES/8878.bugfix.rst | 1 + aiohttp/streams.py | 3 +++ tests/test_client_functional.py | 19 ++++++++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 CHANGES/8878.bugfix.rst diff --git a/CHANGES/8878.bugfix.rst b/CHANGES/8878.bugfix.rst new file mode 100644 index 00000000000..df53dea3c35 --- /dev/null +++ b/CHANGES/8878.bugfix.rst @@ -0,0 +1 @@ +Fixed response reading from closed session to throw an error immediately instead of timing out -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/streams.py b/aiohttp/streams.py index b9b9c3fd96f..c927cfbb1b3 100644 --- a/aiohttp/streams.py +++ b/aiohttp/streams.py @@ -296,6 +296,9 @@ def end_http_chunk_receiving(self) -> None: set_result(waiter, None) async def _wait(self, func_name: str) -> None: + if not self._protocol.connected: + raise RuntimeError("Connection closed.") + # StreamReader uses a future to link the protocol feed_data() method # to a read coroutine. Running two read coroutines at the same time # would have an unexpected behaviour. It would not possible to know diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 566c47522ce..18fb5fe9f86 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -29,7 +29,7 @@ SocketTimeoutError, TooManyRedirects, ) -from aiohttp.pytest_plugin import AiohttpClient, TestClient +from aiohttp.pytest_plugin import AiohttpClient, AiohttpServer, TestClient from aiohttp.test_utils import unused_port @@ -3645,3 +3645,20 @@ async def handler(_: web.Request) -> web.Response: session = await aiohttp_client(app, raise_for_status=None) # type: ignore[arg-type] await session.get("/") + + +async def test_exception_when_read_outside_of_session( + aiohttp_server: AiohttpServer, +) -> None: + async def handler(request: web.Request) -> web.Response: + return web.Response(body=b"1" * 1000000) + + app = web.Application() + app.router.add_get("/", handler) + + server = await aiohttp_server(app) + async with aiohttp.ClientSession() as sess: + resp = await sess.get(server.make_url("/")) + + with pytest.raises(RuntimeError, match="Connection closed"): + await resp.read() From 9360a918cdc828c79ce6dd311875768ed3b548af Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 19:59:50 +0000 Subject: [PATCH 080/296] [PR #8895/68629b52 backport][3.10] Cache the hash generation of the ConnectionKey (#8903) Co-authored-by: J. Nick Koston --- aiohttp/client_reqrep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 0e67607f5d9..d8578a5daed 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -209,7 +209,7 @@ def _merge_ssl_params( return ssl -@attr.s(auto_attribs=True, slots=True, frozen=True) +@attr.s(auto_attribs=True, slots=True, frozen=True, cache_hash=True) class ConnectionKey: # the key should contain an information about used proxy / TLS # to prevent reusing wrong connections from a pool From 6c7aebbe099c2e4ced4340349eaac7d7ba15a419 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 26 Aug 2024 21:00:07 +0100 Subject: [PATCH 081/296] Ensure error is raised when reading from closed client response. (#8878) (#8901) --- CHANGES/8878.bugfix.rst | 1 + aiohttp/streams.py | 3 +++ tests/test_client_functional.py | 19 ++++++++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 CHANGES/8878.bugfix.rst diff --git a/CHANGES/8878.bugfix.rst b/CHANGES/8878.bugfix.rst new file mode 100644 index 00000000000..df53dea3c35 --- /dev/null +++ b/CHANGES/8878.bugfix.rst @@ -0,0 +1 @@ +Fixed response reading from closed session to throw an error immediately instead of timing out -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/streams.py b/aiohttp/streams.py index b9b9c3fd96f..c927cfbb1b3 100644 --- a/aiohttp/streams.py +++ b/aiohttp/streams.py @@ -296,6 +296,9 @@ def end_http_chunk_receiving(self) -> None: set_result(waiter, None) async def _wait(self, func_name: str) -> None: + if not self._protocol.connected: + raise RuntimeError("Connection closed.") + # StreamReader uses a future to link the protocol feed_data() method # to a read coroutine. Running two read coroutines at the same time # would have an unexpected behaviour. It would not possible to know diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 566c47522ce..18fb5fe9f86 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -29,7 +29,7 @@ SocketTimeoutError, TooManyRedirects, ) -from aiohttp.pytest_plugin import AiohttpClient, TestClient +from aiohttp.pytest_plugin import AiohttpClient, AiohttpServer, TestClient from aiohttp.test_utils import unused_port @@ -3645,3 +3645,20 @@ async def handler(_: web.Request) -> web.Response: session = await aiohttp_client(app, raise_for_status=None) # type: ignore[arg-type] await session.get("/") + + +async def test_exception_when_read_outside_of_session( + aiohttp_server: AiohttpServer, +) -> None: + async def handler(request: web.Request) -> web.Response: + return web.Response(body=b"1" * 1000000) + + app = web.Application() + app.router.add_get("/", handler) + + server = await aiohttp_server(app) + async with aiohttp.ClientSession() as sess: + resp = await sess.get(server.make_url("/")) + + with pytest.raises(RuntimeError, match="Connection closed"): + await resp.read() From c19cc7791353b780cb1ca4051230bc9ce50fbcc7 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 20:16:47 +0000 Subject: [PATCH 082/296] [PR #8847/6d974274 backport][3.10] Improve performance of handling skip_auto_headers (#8904) Co-authored-by: J. Nick Koston --- CHANGES/8847.misc.rst | 1 + aiohttp/client.py | 2 +- aiohttp/client_reqrep.py | 24 ++++++++++++++---------- 3 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 CHANGES/8847.misc.rst diff --git a/CHANGES/8847.misc.rst b/CHANGES/8847.misc.rst new file mode 100644 index 00000000000..58f61d48420 --- /dev/null +++ b/CHANGES/8847.misc.rst @@ -0,0 +1 @@ +Improved performance of making requests when there are no auto headers to skip -- by :user:`bdraco`. diff --git a/aiohttp/client.py b/aiohttp/client.py index 3d1045f355a..8edd14d01ff 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -627,7 +627,7 @@ async def _request( url, params=params, headers=headers, - skip_auto_headers=skip_headers, + skip_auto_headers=skip_headers if skip_headers else None, data=data, cookies=all_cookies, auth=auth, diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index d8578a5daed..d055e70e87c 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -265,7 +265,7 @@ def __init__( *, params: Optional[Mapping[str, str]] = None, headers: Optional[LooseHeaders] = None, - skip_auto_headers: Iterable[str] = frozenset(), + skip_auto_headers: Optional[Iterable[str]] = None, data: Any = None, cookies: Optional[LooseCookies] = None, auth: Optional[BasicAuth] = None, @@ -451,12 +451,18 @@ def update_headers(self, headers: Optional[LooseHeaders]) -> None: else: self.headers.add(key, value) - def update_auto_headers(self, skip_auto_headers: Iterable[str]) -> None: - self.skip_auto_headers = CIMultiDict( - (hdr, None) for hdr in sorted(skip_auto_headers) - ) - used_headers = self.headers.copy() - used_headers.extend(self.skip_auto_headers) # type: ignore[arg-type] + def update_auto_headers(self, skip_auto_headers: Optional[Iterable[str]]) -> None: + if skip_auto_headers is not None: + self.skip_auto_headers = CIMultiDict( + (hdr, None) for hdr in sorted(skip_auto_headers) + ) + used_headers = self.headers.copy() + used_headers.extend(self.skip_auto_headers) # type: ignore[arg-type] + else: + # Fast path when there are no headers to skip + # which is the most common case. + self.skip_auto_headers = CIMultiDict() + used_headers = self.headers for hdr, val in self.DEFAULT_HEADERS.items(): if hdr not in used_headers: @@ -573,9 +579,7 @@ def update_body_from_data(self, body: Any) -> None: # copy payload headers assert body.headers for key, value in body.headers.items(): - if key in self.headers: - continue - if key in self.skip_auto_headers: + if key in self.headers or key in self.skip_auto_headers: continue self.headers[key] = value From 770fdcb1080fe02c7903b09eb1abfa76fc3734bb Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 20:17:03 +0000 Subject: [PATCH 083/296] [PR #8847/6d974274 backport][3.11] Improve performance of handling skip_auto_headers (#8905) Co-authored-by: J. Nick Koston --- CHANGES/8847.misc.rst | 1 + aiohttp/client.py | 2 +- aiohttp/client_reqrep.py | 24 ++++++++++++++---------- 3 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 CHANGES/8847.misc.rst diff --git a/CHANGES/8847.misc.rst b/CHANGES/8847.misc.rst new file mode 100644 index 00000000000..58f61d48420 --- /dev/null +++ b/CHANGES/8847.misc.rst @@ -0,0 +1 @@ +Improved performance of making requests when there are no auto headers to skip -- by :user:`bdraco`. diff --git a/aiohttp/client.py b/aiohttp/client.py index 3d1045f355a..8edd14d01ff 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -627,7 +627,7 @@ async def _request( url, params=params, headers=headers, - skip_auto_headers=skip_headers, + skip_auto_headers=skip_headers if skip_headers else None, data=data, cookies=all_cookies, auth=auth, diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index d8578a5daed..d055e70e87c 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -265,7 +265,7 @@ def __init__( *, params: Optional[Mapping[str, str]] = None, headers: Optional[LooseHeaders] = None, - skip_auto_headers: Iterable[str] = frozenset(), + skip_auto_headers: Optional[Iterable[str]] = None, data: Any = None, cookies: Optional[LooseCookies] = None, auth: Optional[BasicAuth] = None, @@ -451,12 +451,18 @@ def update_headers(self, headers: Optional[LooseHeaders]) -> None: else: self.headers.add(key, value) - def update_auto_headers(self, skip_auto_headers: Iterable[str]) -> None: - self.skip_auto_headers = CIMultiDict( - (hdr, None) for hdr in sorted(skip_auto_headers) - ) - used_headers = self.headers.copy() - used_headers.extend(self.skip_auto_headers) # type: ignore[arg-type] + def update_auto_headers(self, skip_auto_headers: Optional[Iterable[str]]) -> None: + if skip_auto_headers is not None: + self.skip_auto_headers = CIMultiDict( + (hdr, None) for hdr in sorted(skip_auto_headers) + ) + used_headers = self.headers.copy() + used_headers.extend(self.skip_auto_headers) # type: ignore[arg-type] + else: + # Fast path when there are no headers to skip + # which is the most common case. + self.skip_auto_headers = CIMultiDict() + used_headers = self.headers for hdr, val in self.DEFAULT_HEADERS.items(): if hdr not in used_headers: @@ -573,9 +579,7 @@ def update_body_from_data(self, body: Any) -> None: # copy payload headers assert body.headers for key, value in body.headers.items(): - if key in self.headers: - continue - if key in self.skip_auto_headers: + if key in self.headers or key in self.skip_auto_headers: continue self.headers[key] = value From adcdf5c9d6e0c0aa2e6f24b3b634eeb965d03738 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 22:00:10 +0100 Subject: [PATCH 084/296] [PR #8875/0478f143 backport][3.11] Fix unclosed transport warning (#8907) **This is a backport of PR #8875 as merged into master (0478f143caba33c7f6b355b8513e3c26d8db7d4d).** Co-authored-by: Sam Bull --- CHANGES/8875.bugfix.rst | 1 + aiohttp/web_protocol.py | 15 +++------------ setup.cfg | 2 -- 3 files changed, 4 insertions(+), 14 deletions(-) create mode 100644 CHANGES/8875.bugfix.rst diff --git a/CHANGES/8875.bugfix.rst b/CHANGES/8875.bugfix.rst new file mode 100644 index 00000000000..fa33df05ae2 --- /dev/null +++ b/CHANGES/8875.bugfix.rst @@ -0,0 +1 @@ +Fixed an unclosed transport ``ResourceWarning`` on web handlers -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index a4941d103ef..a2f159c3b7c 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -260,9 +260,6 @@ async def shutdown(self, timeout: Optional[float] = 15.0) -> None: if self._keepalive_handle is not None: self._keepalive_handle.cancel() - if self._waiter: - self._waiter.cancel() - # Wait for graceful handler completion if self._handler_waiter is not None: with suppress(asyncio.CancelledError, asyncio.TimeoutError): @@ -281,9 +278,7 @@ async def shutdown(self, timeout: Optional[float] = 15.0) -> None: if self._task_handler is not None: self._task_handler.cancel() - if self.transport is not None: - self.transport.close() - self.transport = None + self.force_close() def connection_made(self, transport: asyncio.BaseTransport) -> None: super().connection_made(transport) @@ -307,13 +302,12 @@ def connection_lost(self, exc: Optional[BaseException]) -> None: return self._manager.connection_lost(self, exc) - super().connection_lost(exc) - # Grab value before setting _manager to None. handler_cancellation = self._manager.handler_cancellation + self.force_close() + super().connection_lost(exc) self._manager = None - self._force_close = True self._request_factory = None self._request_handler = None self._request_parser = None @@ -326,9 +320,6 @@ def connection_lost(self, exc: Optional[BaseException]) -> None: exc = ConnectionResetError("Connection lost") self._current_request._cancel(exc) - if self._waiter is not None: - self._waiter.cancel() - if handler_cancellation and self._task_handler is not None: self._task_handler.cancel() diff --git a/setup.cfg b/setup.cfg index 4000b5a40a7..c058fc2f05f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -137,8 +137,6 @@ addopts = filterwarnings = error ignore:module 'ssl' has no attribute 'OP_NO_COMPRESSION'. The Python interpreter is compiled against OpenSSL < 1.0.0. Ref. https.//docs.python.org/3/library/ssl.html#ssl.OP_NO_COMPRESSION:UserWarning - ignore:unclosed transport :ResourceWarning ignore:Unclosed client session Date: Mon, 26 Aug 2024 22:05:32 +0100 Subject: [PATCH 085/296] [PR #8875/0478f143 backport][3.10] Fix unclosed transport warning (#8906) **This is a backport of PR #8875 as merged into master (0478f143caba33c7f6b355b8513e3c26d8db7d4d).** Co-authored-by: Sam Bull --- CHANGES/8875.bugfix.rst | 1 + aiohttp/web_protocol.py | 15 +++------------ setup.cfg | 2 -- 3 files changed, 4 insertions(+), 14 deletions(-) create mode 100644 CHANGES/8875.bugfix.rst diff --git a/CHANGES/8875.bugfix.rst b/CHANGES/8875.bugfix.rst new file mode 100644 index 00000000000..fa33df05ae2 --- /dev/null +++ b/CHANGES/8875.bugfix.rst @@ -0,0 +1 @@ +Fixed an unclosed transport ``ResourceWarning`` on web handlers -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index a4941d103ef..a2f159c3b7c 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -260,9 +260,6 @@ async def shutdown(self, timeout: Optional[float] = 15.0) -> None: if self._keepalive_handle is not None: self._keepalive_handle.cancel() - if self._waiter: - self._waiter.cancel() - # Wait for graceful handler completion if self._handler_waiter is not None: with suppress(asyncio.CancelledError, asyncio.TimeoutError): @@ -281,9 +278,7 @@ async def shutdown(self, timeout: Optional[float] = 15.0) -> None: if self._task_handler is not None: self._task_handler.cancel() - if self.transport is not None: - self.transport.close() - self.transport = None + self.force_close() def connection_made(self, transport: asyncio.BaseTransport) -> None: super().connection_made(transport) @@ -307,13 +302,12 @@ def connection_lost(self, exc: Optional[BaseException]) -> None: return self._manager.connection_lost(self, exc) - super().connection_lost(exc) - # Grab value before setting _manager to None. handler_cancellation = self._manager.handler_cancellation + self.force_close() + super().connection_lost(exc) self._manager = None - self._force_close = True self._request_factory = None self._request_handler = None self._request_parser = None @@ -326,9 +320,6 @@ def connection_lost(self, exc: Optional[BaseException]) -> None: exc = ConnectionResetError("Connection lost") self._current_request._cancel(exc) - if self._waiter is not None: - self._waiter.cancel() - if handler_cancellation and self._task_handler is not None: self._task_handler.cancel() diff --git a/setup.cfg b/setup.cfg index 71ed6b98e0e..bd93b00cb2f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -138,8 +138,6 @@ addopts = filterwarnings = error ignore:module 'ssl' has no attribute 'OP_NO_COMPRESSION'. The Python interpreter is compiled against OpenSSL < 1.0.0. Ref. https.//docs.python.org/3/library/ssl.html#ssl.OP_NO_COMPRESSION:UserWarning - ignore:unclosed transport :ResourceWarning ignore:Unclosed client session Date: Tue, 27 Aug 2024 10:32:29 +0000 Subject: [PATCH 086/296] Bump wheel from 0.37.0 to 0.44.0 (#8910) Bumps [wheel](https://github.com/pypa/wheel) from 0.37.0 to 0.44.0.
Release notes

Sourced from wheel's releases.

0.44.0

  • Canonicalized requirements in METADATA file (PR by Wim Jeantine-Glenn)
  • Deprecated the bdist_wheel module, as the code was migrated to setuptools itself

0.43.0

  • Dropped support for Python 3.7
  • Updated vendored packaging to 24.0

0.42.0

  • Allowed removing build tag with wheel tags --build ""
  • Fixed wheel pack and wheel tags writing updated WHEEL fields after a blank line, causing other tools to ignore them
  • Fixed wheel pack and wheel tags writing WHEEL with CRLF line endings or a mix of CRLF and LF
  • Fixed wheel pack --build-number "" not removing build tag from WHEEL (above changes by Benjamin Gilbert)

0.41.3

  • Updated vendored packaging to 23.2
  • Fixed ABI tag generation for CPython 3.13a1 on Windows (PR by Sam Gross)
Changelog

Sourced from wheel's changelog.

Release Notes

0.44.0 (2024-08-04)

  • Canonicalized requirements in METADATA file (PR by Wim Jeantine-Glenn)
  • Deprecated the bdist_wheel module, as the code was migrated to setuptools itself

0.43.0 (2024-03-11)

  • Dropped support for Python 3.7
  • Updated vendored packaging to 24.0

0.42.0 (2023-11-26)

  • Allowed removing build tag with wheel tags --build ""
  • Fixed wheel pack and wheel tags writing updated WHEEL fields after a blank line, causing other tools to ignore them
  • Fixed wheel pack and wheel tags writing WHEEL with CRLF line endings or a mix of CRLF and LF
  • Fixed wheel pack --build-number "" not removing build tag from WHEEL (above changes by Benjamin Gilbert)

0.41.3 (2023-10-30)

  • Updated vendored packaging to 23.2
  • Fixed ABI tag generation for CPython 3.13a1 on Windows (PR by Sam Gross)

0.41.2 (2023-08-22)

  • Fixed platform tag detection for GraalPy and 32-bit python running on an aarch64 kernel (PR by Matthieu Darbois)
  • Fixed wheel tags to not list directories in RECORD files (PR by Mike Taves)
  • Fixed ABI tag generation for GraalPy (PR by Michael Simacek)

0.41.1 (2023-08-05)

  • Fixed naming of the data_dir directory in the presence of local version segment given via egg_info.tag_build (PR by Anderson Bravalheri)
  • Fixed version specifiers in Requires-Dist being wrapped in parentheses

0.41.0 (2023-07-22)

  • Added full support of the build tag syntax to wheel tags (you can now set a build tag like 123mytag)
  • Fixed warning on Python 3.12 about onerror deprecation. (PR by Henry Schreiner)
  • Support testing on Python 3.12 betas (PR by Ewout ter Hoeven)

... (truncated)

Commits
  • 7bb46d7 Created a new release
  • 0add7d6 Deprecated bdist_wheel and updated the README (#631)
  • 46c2389 chore: make sure local ruff runs don't touch vendored (#618)
  • 78b9ea9 Updated Cirrus CI config to use FreeBSD 14
  • 3d3916a [pre-commit.ci] pre-commit autoupdate (#627)
  • 1e00742 Revert "Apply new ruff/pyupgrade rule UP032 (#617)"
  • 16206e6 Apply new ruff/pyupgrade rule UP032 (#617)
  • 0b7771e Updated pre-commit modules and applied ruff fixes
  • bd8ab85 Extended the ruff rule list and applied fixes
  • 376373b Allow bdist_wheel working without ctypes (#613)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=wheel&package-manager=pip&previous-version=0.37.0&new-version=0.44.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 f921bb0e786..3881dcfc314 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -285,7 +285,7 @@ wait-for-it==2.2.2 # via -r requirements/test.in webcolors==24.8.0 # via blockdiag -wheel==0.37.0 +wheel==0.44.0 # via pip-tools yarl==1.9.4 # via -r requirements/runtime-deps.in diff --git a/requirements/dev.txt b/requirements/dev.txt index 35a83a15360..314ba878d45 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -277,7 +277,7 @@ wait-for-it==2.2.2 # via -r requirements/test.in webcolors==24.8.0 # via blockdiag -wheel==0.41.0 +wheel==0.44.0 # via pip-tools yarl==1.9.4 # via -r requirements/runtime-deps.in From fe85c54692fcbecbb002e3808277c39878b4c9ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 10:39:39 +0000 Subject: [PATCH 087/296] Bump pyproject-hooks from 1.0.0 to 1.1.0 (#8911) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [pyproject-hooks](https://github.com/pypa/pyproject-hooks) from 1.0.0 to 1.1.0.
Changelog

Sourced from pyproject-hooks's changelog.

Changelog

v1.1

  • Add type annotations to the public API.
  • More careful handling of the backend-path key from pyproject.toml. Previous versions would load the backend and then check that it was loaded from the specified path; the new version only loads it from the specified path. The BackendInvalid exception is now a synonym for :exc:BackendUnavailable, and code should move to using the latter name.

v1.0

  • Rename package to pyproject_hooks (from pep517).
  • Remove deprecated modules (.build, .check and .envbuild). Use the build <https://pypa-build.readthedocs.io/en/stable/>_ project instead for this higher-level functionality of setting up a temporary build environment.
  • Require Python 3.7 or above.
  • Use tomllib from the standard library on Python 3.11. pyproject_hooks now has no external dependencies when installed in Python 3.11.
  • Avoid chaining exceptions when using the fallback implementation for :meth:.prepare_metadata_for_build_wheel.
  • Fix propagating error message for :exc:.BackendInvalid errors.

v0.13

  • Remove support for end-of-life Pythons. Now requires Python3.6+.
  • Remove support for toml package. Now requires tomli.
  • Rely on preferred "files" API on Python 3.9 and later (#140).

v0.12

  • Add method for pip to check if build_editable hook is supported. This is a private API for now.

v0.11.1

  • Fix DeprecationWarning in tomli.

v0.11

  • Support editable hooks (PEP 660 <https://www.python.org/dev/peps/pep-0660/>_).

... (truncated)

Commits
  • 903ad91 Merge pull request #189 from pypa/ci-rm-mac-py37
  • 9a22b3e Exclude Mac + Python 3.7 from CI matrix
  • d79646f Merge pull request #188 from pypa/prepare-1.1
  • 7de0160 Bump version: 1.0.0 → 1.1.0
  • dd15b2a Mention backend-path changes in changelog
  • debf816 Add back BackendInvalid as a synonym for BackendUnavailable
  • c667aeb Merge pull request #187 from pypa/gha-trusted-publish
  • 79a2edf Remove trailing space
  • f66e6a1 Specify environment for publishing job
  • 1248ac2 Set up trusted publishing for making releases to PyPI
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pyproject-hooks&package-manager=pip&previous-version=1.0.0&new-version=1.1.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 3881dcfc314..831e2b9553e 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -166,7 +166,7 @@ pyjwt==2.9.0 # via # gidgethub # pyjwt -pyproject-hooks==1.0.0 +pyproject-hooks==1.1.0 # via # build # pip-tools @@ -245,7 +245,6 @@ tomli==2.0.1 # incremental # mypy # pip-tools - # pyproject-hooks # pytest # slotscheck # towncrier diff --git a/requirements/dev.txt b/requirements/dev.txt index 314ba878d45..84ee10dc526 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -161,7 +161,7 @@ pyjwt==2.8.0 # via # gidgethub # pyjwt -pyproject-hooks==1.0.0 +pyproject-hooks==1.1.0 # via # build # pip-tools @@ -237,7 +237,6 @@ tomli==2.0.1 # incremental # mypy # pip-tools - # pyproject-hooks # pytest # slotscheck # towncrier From 124181f207c8cfd3257bc2063527322b1ed8ed3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:26:09 +0000 Subject: [PATCH 088/296] Bump rich from 13.7.1 to 13.8.0 (#8913) Bumps [rich](https://github.com/Textualize/rich) from 13.7.1 to 13.8.0.
Release notes

Sourced from rich's releases.

The Thanks for your patience Release

This is a fairly large update. Mostly an accumulation of small fixes and enhancements. Nothing qualifies as a *breaking change (for some definition), but there may be some subtly changes to output. Check below for anything that might affect you!

[13.8.0] - 2024-08-26

Fixed

Changed

  • RichHandler errors and warnings will now use different colors (red and yellow) Textualize/rich#2825
  • Removed the empty line printed in jupyter while using Progress Textualize/rich#2616
  • Running tests in environment with FORCE_COLOR or NO_COLOR environment variables
  • ansi decoder will now strip problematic private escape sequences (like \x1b7) Textualize/rich#3278
  • Tree's ASCII_GUIDES and TREE_GUIDES constants promoted to class attributes

Added

  • Adds a case_sensitive parameter to prompt.Prompt. This determines if the response is treated as case-sensitive. Defaults to True.
  • Added Console.on_broken_pipe Textualize/rich#3468
Changelog

Sourced from rich's changelog.

[13.8.0] - 2024-08-26

Fixed

Changed

  • RichHandler errors and warnings will now use different colors (red and yellow) Textualize/rich#2825
  • Removed the empty line printed in jupyter while using Progress Textualize/rich#2616
  • Running tests in environment with FORCE_COLOR or NO_COLOR environment variables
  • ansi decoder will now strip problematic private escape sequences (like \x1b7) Textualize/rich#3278
  • Tree's ASCII_GUIDES and TREE_GUIDES constants promoted to class attributes

Added

  • Adds a case_sensitive parameter to prompt.Prompt. This determines if the response is treated as case-sensitive. Defaults to True.
  • Added Console.on_broken_pipe Textualize/rich#3468
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rich&package-manager=pip&previous-version=13.7.1&new-version=13.8.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 831e2b9553e..6f298fcfed9 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -201,7 +201,7 @@ requests==2.32.3 # cherry-picker # python-on-whales # sphinx -rich==13.7.1 +rich==13.8.0 # via typer setuptools-git==1.2 # via -r requirements/test.in diff --git a/requirements/dev.txt b/requirements/dev.txt index 84ee10dc526..a829b815d0b 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -196,7 +196,7 @@ requests==2.32.3 # cherry-picker # python-on-whales # sphinx -rich==13.7.1 +rich==13.8.0 # via typer setuptools-git==1.2 # via -r requirements/test.in diff --git a/requirements/lint.txt b/requirements/lint.txt index 1184ed4653b..a643194a1cb 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -86,7 +86,7 @@ pyyaml==6.0.2 # via pre-commit requests==2.32.3 # via python-on-whales -rich==13.7.1 +rich==13.8.0 # via typer shellingham==1.5.4 # via typer diff --git a/requirements/test.txt b/requirements/test.txt index cc98efc6d0f..73287916d52 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -102,7 +102,7 @@ regex==2024.7.24 # via re-assert requests==2.32.3 # via python-on-whales -rich==13.7.1 +rich==13.8.0 # via typer setuptools-git==1.2 # via -r requirements/test.in From a0ec9497e271444f13d554383b7f680f11fc1482 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:26:13 +0000 Subject: [PATCH 089/296] Bump importlib-resources from 6.1.1 to 6.4.4 (#8915) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [importlib-resources](https://github.com/python/importlib_resources) from 6.1.1 to 6.4.4.
Changelog

Sourced from importlib-resources's changelog.

v6.4.4

No significant changes.

v6.4.3

Bugfixes

  • When inferring the caller in files()python/cpython#123085

v6.4.2

Bugfixes

  • Merged fix for UTF-16 BOM handling in functional tests. (#312)

v6.4.1

Bugfixes

  • python/cpython#121735

v6.4.0

Features

  • The functions is_resource(), open_binary(), open_text(), path(), read_binary(), and read_text() are un-deprecated, and support subdirectories via multiple positional arguments. The contents() function also allows subdirectories, but remains deprecated. (#303)
  • python/cpython#109829

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=importlib-resources&package-manager=pip&previous-version=6.1.1&new-version=6.4.4)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 6f298fcfed9..6b9beaf9d5a 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -99,7 +99,7 @@ importlib-metadata==8.4.0 # via # build # sphinx -importlib-resources==6.1.1 +importlib-resources==6.4.4 # via towncrier incremental==24.7.2 # via towncrier diff --git a/requirements/dev.txt b/requirements/dev.txt index a829b815d0b..ec6aea6146a 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -97,7 +97,7 @@ importlib-metadata==8.4.0 # via # build # sphinx -importlib-resources==6.1.1 +importlib-resources==6.4.4 # via towncrier incremental==24.7.2 # via towncrier diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 57bc9bc47b6..4d55d216acb 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -28,7 +28,7 @@ imagesize==1.4.1 # via sphinx importlib-metadata==8.4.0 # via sphinx -importlib-resources==6.1.1 +importlib-resources==6.4.4 # via towncrier incremental==24.7.2 # via towncrier diff --git a/requirements/doc.txt b/requirements/doc.txt index 91ad44582d4..fbfc99f686f 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -28,7 +28,7 @@ imagesize==1.4.1 # via sphinx importlib-metadata==8.4.0 # via sphinx -importlib-resources==6.1.1 +importlib-resources==6.4.4 # via towncrier incremental==24.7.2 # via towncrier From 94d2cb51dbae7038cbaece79a2225bab3d024be6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:31:28 +0000 Subject: [PATCH 090/296] Bump click from 8.0.3 to 8.1.7 (#8917) Bumps [click](https://github.com/pallets/click) from 8.0.3 to 8.1.7.
Release notes

Sourced from click's releases.

8.1.7

This is a fix release for the 8.1.x feature branch.

8.1.6

This is a fix release for the 8.1.x feature branch. If you were having issues with type checking tools like pyright or mypy not accepting uses of Click's decorators, this should fix that.

8.1.5

This is a fix release for the 8.1.x feature branch. This fixes an issue with decorator type annotations that caused type checkers to fail for valid code. There are no runtime behavior changes.

8.1.4

This is a fix release for the 8.1.x feature branch.

8.1.3

This is a fix release for the 8.1.0 feature release.

8.1.2

This is a fix release for the 8.1.0 feature release.

8.1.1

This is a fix release for the 8.1.0 feature release.

8.1.0

This is a feature release, which includes new features and removes previously deprecated features. The 8.1.x branch is now the supported bugfix branch, the 8.0.x branch will become a tag marking the end of support for that branch. We encourage everyone to upgrade, and to use a tool such as pip-tools to pin all dependencies and control upgrades.

8.0.4

... (truncated)

Changelog

Sourced from click's changelog.

Version 8.1.7

Released 2023-08-17

  • Fix issue with regex flags in shell completion. :issue:2581
  • Bash version detection issues a warning instead of an error. :issue:2574
  • Fix issue with completion script for Fish shell. :issue:2567

Version 8.1.6

Released 2023-07-18

  • Fix an issue with type hints for @click.group(). :issue:2558

Version 8.1.5

Released 2023-07-13

  • Fix an issue with type hints for @click.command(), @click.option(), and other decorators. Introduce typing tests. :issue:2558

Version 8.1.4

Released 2023-07-06

  • Replace all typing.Dict occurrences to typing.MutableMapping for parameter hints. :issue:2255
  • Improve type hinting for decorators and give all generic types parameters. :issue:2398
  • Fix return value and type signature of shell_completion.add_completion_class function. :pr:2421
  • Bash version detection doesn't fail on Windows. :issue:2461
  • Completion works if there is a dot (.) in the program name. :issue:2166
  • Improve type annotations for pyright type checker. :issue:2268
  • Improve responsiveness of click.clear(). :issue:2284
  • Improve command name detection when using Shiv or PEX. :issue:2332
  • Avoid showing empty lines if command help text is empty. :issue:2368
  • ZSH completion script works when loaded from fpath. :issue:2344.
  • EOFError and KeyboardInterrupt tracebacks are not suppressed when standalone_mode is disabled. :issue:2380
  • @group.command does not fail if the group was created with a custom command_class. :issue:2416
  • multiple=True is allowed for flag options again and does not require

... (truncated)

Commits
  • 874ca2b release version 8.1.7
  • 6e1f6d3 completion(fish): add back ; as line endings in fish script (#2570)
  • a955c77 update fish enabling script
  • 3c1529e add back semicolons in fish script
  • a260ca6 Replace bash shell completion version error with warning (#2576)
  • d9db70c bash version support shows warning instead of error
  • 22b9b1c Fix incorrect passing of flags to re.sub (#2581)
  • d69d210 fix flake8 finding
  • af2da1e Fix incorrect passing of flags to re.sub
  • bb6a872 start version 8.1.7
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=click&package-manager=pip&previous-version=8.0.3&new-version=8.1.7)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- requirements/lint.txt | 2 +- requirements/test.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 6b9beaf9d5a..6d7e5c08543 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -46,7 +46,7 @@ charset-normalizer==3.3.2 # via requests cherry-picker==2.2.0 # via -r requirements/dev.in -click==8.0.3 +click==8.1.7 # via # cherry-picker # pip-tools diff --git a/requirements/dev.txt b/requirements/dev.txt index ec6aea6146a..e1c9f8f57a2 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -46,7 +46,7 @@ charset-normalizer==3.3.2 # via requests cherry-picker==2.2.0 # via -r requirements/dev.in -click==8.1.6 +click==8.1.7 # via # cherry-picker # pip-tools diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 4d55d216acb..5108b774343 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -16,7 +16,7 @@ certifi==2024.7.4 # via requests charset-normalizer==3.3.2 # via requests -click==8.1.6 +click==8.1.7 # via towncrier docutils==0.20.1 # via sphinx diff --git a/requirements/doc.txt b/requirements/doc.txt index fbfc99f686f..1382a7bb892 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -16,7 +16,7 @@ certifi==2024.7.4 # via requests charset-normalizer==3.3.2 # via requests -click==8.1.6 +click==8.1.7 # via towncrier docutils==0.20.1 # via sphinx diff --git a/requirements/lint.txt b/requirements/lint.txt index a643194a1cb..6d0647239ee 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -22,7 +22,7 @@ cfgv==3.4.0 # via pre-commit charset-normalizer==3.3.2 # via requests -click==8.1.6 +click==8.1.7 # via # slotscheck # typer diff --git a/requirements/test.txt b/requirements/test.txt index 73287916d52..2c63a08a37f 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -26,7 +26,7 @@ cffi==1.17.0 # pycares charset-normalizer==3.3.2 # via requests -click==8.1.6 +click==8.1.7 # via # typer # wait-for-it From 36647c6d6b5980c8ab969daf0c77a46ad9978f45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:47:44 +0000 Subject: [PATCH 091/296] Bump alabaster from 0.7.12 to 0.7.13 (#8918) Bumps [alabaster](https://github.com/sphinx-doc/alabaster) from 0.7.12 to 0.7.13.
Changelog

Sourced from alabaster's changelog.

:git_tag:0.7.13 -- 2023-01-13

  • Modernized the project: s/Travis/Circle/ for CI, README badges, setup.cfg removal, metadata refresh, etc.

  • Dropped support for Python 2 and Python <3.6. This includes various minor updates to work correctly with modern versions of Sphinx (1.6 at the very least). Thanks to Adam Turner for a pile of patches here.

    .. warning:: This change is backwards incompatible if you're on an old Python version.

  • Tweak CSS somewhat for compatibility with modern Sphinx versions' base stylesheet.

Commits
  • e5c0583 Cut 0.7.13
  • af4588b dev-reqs update, including temp invoke@git
  • c258ede Oh right, no tests = no coverage. copypasta fail!
  • b492a0c Use newer sphinx on newer pythons (in test matrix)
  • 9dfc356 Use param for base docs job
  • 160d645 Add black
  • e630edb Matrix far too big lol
  • 610fe18 Migrate to CircleCI, or start to anyhow.
  • f43d162 extra URLs for pypi
  • ec2c9c6 badges in readme
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=alabaster&package-manager=pip&previous-version=0.7.12&new-version=0.7.13)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 6d7e5c08543..56d9f88c633 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -16,7 +16,7 @@ aioredis==2.0.1 # via -r requirements/lint.in aiosignal==1.3.1 # via -r requirements/runtime-deps.in -alabaster==0.7.12 +alabaster==0.7.13 # via sphinx annotated-types==0.7.0 # via pydantic From b0a97da9e1a02a1f3f77d04a6b077061b839f096 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 11:04:19 +0000 Subject: [PATCH 092/296] Bump zipp from 3.20.0 to 3.20.1 (#8923) Bumps [zipp](https://github.com/jaraco/zipp) from 3.20.0 to 3.20.1.
Changelog

Sourced from zipp's changelog.

v3.20.1

Bugfixes

  • python/cpython#123270
Commits
  • c23e549 Finalize
  • c2b9015 Merge pull request #124 from jaraco/bugfix/gh-123270-supported-names
  • 774a3ac Add TODO to consolidate this behavior in CPython.
  • cc61e61 Prefer simpler path.rstrip to consolidate checks for empty or only paths.
  • bec712f Mark unused code as uncovered.
  • fde82dc Add news fragment.
  • a421f7e Invent DirtyZipInfo to create an unsanitized zipfile with backslashes.
  • 0a3a7b4 Refine expectation that paths with leading slashes are simply not visible.
  • f89b93f Address infinite loop when zipfile begins with more than one leading slash.
  • 3cb5609 Removed SanitizedNames.
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=zipp&package-manager=pip&previous-version=3.20.0&new-version=3.20.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 56d9f88c633..11621911a1e 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -288,7 +288,7 @@ wheel==0.44.0 # via pip-tools yarl==1.9.4 # via -r requirements/runtime-deps.in -zipp==3.20.0 +zipp==3.20.1 # via # importlib-metadata # importlib-resources diff --git a/requirements/dev.txt b/requirements/dev.txt index e1c9f8f57a2..180756490ac 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -280,7 +280,7 @@ wheel==0.44.0 # via pip-tools yarl==1.9.4 # via -r requirements/runtime-deps.in -zipp==3.20.0 +zipp==3.20.1 # via # importlib-metadata # importlib-resources diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 5108b774343..2645cebaad6 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -90,7 +90,7 @@ urllib3==2.2.2 # via requests webcolors==24.8.0 # via blockdiag -zipp==3.20.0 +zipp==3.20.1 # via # importlib-metadata # importlib-resources diff --git a/requirements/doc.txt b/requirements/doc.txt index 1382a7bb892..1a22efd4b55 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -85,7 +85,7 @@ urllib3==2.2.2 # via requests webcolors==24.8.0 # via blockdiag -zipp==3.20.0 +zipp==3.20.1 # via # importlib-metadata # importlib-resources From a69a25ad962dd9e5f94bcdac6db7eb5f06fa8f6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 11:17:36 +0000 Subject: [PATCH 093/296] Bump setuptools from 73.0.1 to 74.0.0 (#8924) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [setuptools](https://github.com/pypa/setuptools) from 73.0.1 to 74.0.0.
Changelog

Sourced from setuptools's changelog.

v74.0.0

Features

  • Changed the type of error raised by setuptools.command.easy_install.CommandSpec.from_param on unsupported argument from AttributeError to TypeError -- by :user:Avasam (#4548)
  • Added detection of ARM64 variant of MSVC -- by :user:saschanaz (#4553)
  • Made setuptools.package_index.Credential a typing.NamedTuple -- by :user:Avasam (#4585)
  • Reraise error from setuptools.command.easy_install.auto_chmod instead of nonsensical TypeError: 'Exception' object is not subscriptable -- by :user:Avasam (#4593)
  • Fully typed all collection attributes in pkg_resources -- by :user:Avasam (#4598)
  • Automatically exclude .tox|.nox|.venv directories from sdist. (#4603)

Deprecations and Removals

  • Removed the monkeypatching of distutils._msvccompiler. Now all compiler logic is consolidated in distutils. (#4600)
  • Synced with pypa/distutils@58fe058e4, including consolidating Visual Studio 2017 support (#4600, pypa/distutils#289pypa/distutils#287#4606)

Misc

Commits
  • 98ad794 Bump version: 73.0.1 → 74.0.0
  • b4fb917 Merge pull request #4600 from pypa/debt/msvc-monkey
  • 18a44d8 Add news fragment.
  • 5f8215d Merge pull request #4548 from Avasam/from_param-TypeError
  • 6928048 Merge branch 'main' into debt/msvc-monkey
  • 11a6b59 Merge pull request #4606 from pypa/distutils-58fe058e4
  • 903604b Reraise sensible errors from auto_chmod (#4593)
  • 8ec5b5a Add missing news fragment for PR 4603
  • e90dfd5 Exclude top-level .tox|.nox|.venv from sdist (#4603)
  • ef2957a Reraise sensible errors from auto_chmod
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=setuptools&package-manager=pip&previous-version=73.0.1&new-version=74.0.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 11621911a1e..30e89bf4ba9 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -296,7 +296,7 @@ zipp==3.20.1 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via pip-tools -setuptools==73.0.1 +setuptools==74.0.0 # via # blockdiag # incremental diff --git a/requirements/dev.txt b/requirements/dev.txt index 180756490ac..a3eea2d8d88 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -288,7 +288,7 @@ zipp==3.20.1 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via pip-tools -setuptools==73.0.1 +setuptools==74.0.0 # via # blockdiag # incremental diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 2645cebaad6..ae5f3a95597 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -96,7 +96,7 @@ zipp==3.20.1 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -setuptools==73.0.1 +setuptools==74.0.0 # via # blockdiag # incremental diff --git a/requirements/doc.txt b/requirements/doc.txt index 1a22efd4b55..6e247c15b6e 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -91,7 +91,7 @@ zipp==3.20.1 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -setuptools==73.0.1 +setuptools==74.0.0 # via # blockdiag # incremental From 5e498df77b6034455d5b65f7a963f2cb2f605990 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Wed, 28 Aug 2024 16:06:06 +0100 Subject: [PATCH 094/296] Support credentials in URL with empty user (#6494) (#6495) (#8926) (cherry picked from commit ce9c4eb0f895f356e775ca268d7ccef908f4c936) Co-authored-by: Chris Shucksmith --- CHANGES/6494.bugfix.rst | 1 + CONTRIBUTORS.txt | 1 + aiohttp/client_reqrep.py | 4 ++-- aiohttp/helpers.py | 4 ++-- tests/test_client_request.py | 7 +++++++ tests/test_helpers.py | 8 ++++++++ 6 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 CHANGES/6494.bugfix.rst diff --git a/CHANGES/6494.bugfix.rst b/CHANGES/6494.bugfix.rst new file mode 100644 index 00000000000..3827644f0d1 --- /dev/null +++ b/CHANGES/6494.bugfix.rst @@ -0,0 +1 @@ +Added support for URL credentials with empty (zero-length) username, e.g. ``https://:password@host`` -- by :user:`shuckc` diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 202193375dd..57a4d2dbcf3 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -74,6 +74,7 @@ Chih-Yuan Chen Chris AtLee Chris Laws Chris Moore +Chris Shucksmith Christopher Schmitt Claudiu Popa Colin Dunklau diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index d055e70e87c..933f3275e28 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -408,8 +408,8 @@ def update_host(self, url: URL) -> None: # basic auth info username, password = url.user, url.password - if username: - self.auth = helpers.BasicAuth(username, password or "") + if username or password: + self.auth = helpers.BasicAuth(username or "", password or "") def update_version(self, version: Union[http.HttpVersion, str]) -> None: """Convert request version to two elements tuple. diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index f759bddc099..0327d31d961 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -164,9 +164,9 @@ def from_url(cls, url: URL, *, encoding: str = "latin1") -> Optional["BasicAuth" """Create BasicAuth from url.""" if not isinstance(url, URL): raise TypeError("url should be yarl.URL instance") - if url.user is None: + if url.user is None and url.password is None: return None - return cls(url.user, url.password or "", encoding=encoding) + return cls(url.user or "", url.password or "", encoding=encoding) def encode(self) -> str: """Encode credentials.""" diff --git a/tests/test_client_request.py b/tests/test_client_request.py index 7d9f69b52f0..2d70ebdd4f2 100644 --- a/tests/test_client_request.py +++ b/tests/test_client_request.py @@ -453,6 +453,13 @@ def test_basic_auth_from_url(make_request) -> None: assert "python.org" == req.host +def test_basic_auth_no_user_from_url(make_request) -> None: + req = make_request("get", "http://:1234@python.org") + assert "AUTHORIZATION" in req.headers + assert "Basic OjEyMzQ=" == req.headers["AUTHORIZATION"] + assert "python.org" == req.host + + def test_basic_auth_from_url_overridden(make_request) -> None: req = make_request( "get", "http://garbage@python.org", auth=aiohttp.BasicAuth("nkim", "1234") diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 67af32dc3be..827a417c299 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -189,6 +189,14 @@ def test_basic_auth_from_url() -> None: assert auth.password == "pass" +def test_basic_auth_no_user_from_url() -> None: + url = URL("http://:pass@example.com") + auth = helpers.BasicAuth.from_url(url) + assert auth is not None + assert auth.login == "" + assert auth.password == "pass" + + def test_basic_auth_from_not_url() -> None: with pytest.raises(TypeError): helpers.BasicAuth.from_url("http://user:pass@example.com") From 672cb20b38b10a329b5dfa8f1845fb022a426bf7 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Wed, 28 Aug 2024 17:34:48 +0100 Subject: [PATCH 095/296] Support credentials in URL with empty user (#6494) (#6495) (#8927) (cherry picked from commit ce9c4eb0f895f356e775ca268d7ccef908f4c936) Co-authored-by: Chris Shucksmith --- CHANGES/6494.bugfix.rst | 1 + CONTRIBUTORS.txt | 1 + aiohttp/client_reqrep.py | 4 ++-- aiohttp/helpers.py | 4 ++-- tests/test_client_request.py | 7 +++++++ tests/test_helpers.py | 8 ++++++++ 6 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 CHANGES/6494.bugfix.rst diff --git a/CHANGES/6494.bugfix.rst b/CHANGES/6494.bugfix.rst new file mode 100644 index 00000000000..3827644f0d1 --- /dev/null +++ b/CHANGES/6494.bugfix.rst @@ -0,0 +1 @@ +Added support for URL credentials with empty (zero-length) username, e.g. ``https://:password@host`` -- by :user:`shuckc` diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 202193375dd..57a4d2dbcf3 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -74,6 +74,7 @@ Chih-Yuan Chen Chris AtLee Chris Laws Chris Moore +Chris Shucksmith Christopher Schmitt Claudiu Popa Colin Dunklau diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index d055e70e87c..933f3275e28 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -408,8 +408,8 @@ def update_host(self, url: URL) -> None: # basic auth info username, password = url.user, url.password - if username: - self.auth = helpers.BasicAuth(username, password or "") + if username or password: + self.auth = helpers.BasicAuth(username or "", password or "") def update_version(self, version: Union[http.HttpVersion, str]) -> None: """Convert request version to two elements tuple. diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index f759bddc099..0327d31d961 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -164,9 +164,9 @@ def from_url(cls, url: URL, *, encoding: str = "latin1") -> Optional["BasicAuth" """Create BasicAuth from url.""" if not isinstance(url, URL): raise TypeError("url should be yarl.URL instance") - if url.user is None: + if url.user is None and url.password is None: return None - return cls(url.user, url.password or "", encoding=encoding) + return cls(url.user or "", url.password or "", encoding=encoding) def encode(self) -> str: """Encode credentials.""" diff --git a/tests/test_client_request.py b/tests/test_client_request.py index 7d9f69b52f0..2d70ebdd4f2 100644 --- a/tests/test_client_request.py +++ b/tests/test_client_request.py @@ -453,6 +453,13 @@ def test_basic_auth_from_url(make_request) -> None: assert "python.org" == req.host +def test_basic_auth_no_user_from_url(make_request) -> None: + req = make_request("get", "http://:1234@python.org") + assert "AUTHORIZATION" in req.headers + assert "Basic OjEyMzQ=" == req.headers["AUTHORIZATION"] + assert "python.org" == req.host + + def test_basic_auth_from_url_overridden(make_request) -> None: req = make_request( "get", "http://garbage@python.org", auth=aiohttp.BasicAuth("nkim", "1234") diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 67af32dc3be..827a417c299 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -189,6 +189,14 @@ def test_basic_auth_from_url() -> None: assert auth.password == "pass" +def test_basic_auth_no_user_from_url() -> None: + url = URL("http://:pass@example.com") + auth = helpers.BasicAuth.from_url(url) + assert auth is not None + assert auth.login == "" + assert auth.password == "pass" + + def test_basic_auth_from_not_url() -> None: with pytest.raises(TypeError): helpers.BasicAuth.from_url("http://user:pass@example.com") From daa4d5f683f791c6fb3da8204a5d59b6245ef363 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 19:04:22 +0100 Subject: [PATCH 096/296] [PR #8930/1575360e backport][3.11] Fix limit docs (#8932) **This is a backport of PR #8930 as merged into master (1575360ef32b5ad63d3d6822fc790a8b084cb0d3).** Co-authored-by: Sam Bull --- docs/client_reference.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 91444d117b1..03e812ff611 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -990,7 +990,7 @@ is controlled by *force_close* constructor's parameter). flag. :param int limit: total number simultaneous connections. If *limit* is - ``None`` the connector has no limit (default: 100). + ``0`` the connector has no limit (default: 100). :param int limit_per_host: limit simultaneous connections to the same endpoint. Endpoints are the same if they are @@ -1035,7 +1035,7 @@ is controlled by *force_close* constructor's parameter). Endpoints are the same if they are have equal ``(host, port, is_ssl)`` triple. - If *limit_per_host* is ``None`` the connector has no limit per host. + If *limit_per_host* is ``0`` the connector has no limit per host. Read-only property. @@ -1130,7 +1130,7 @@ is controlled by *force_close* constructor's parameter). updated refreshing each entry after N seconds. :param int limit: total number simultaneous connections. If *limit* is - ``None`` the connector has no limit (default: 100). + ``0`` the connector has no limit (default: 100). :param int limit_per_host: limit simultaneous connections to the same endpoint. Endpoints are the same if they are From b9189df64353acc64e9a758b642c5310012ef3eb Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 19:32:44 +0100 Subject: [PATCH 097/296] [PR #8930/1575360e backport][3.10] Fix limit docs (#8931) **This is a backport of PR #8930 as merged into master (1575360ef32b5ad63d3d6822fc790a8b084cb0d3).** Co-authored-by: Sam Bull --- docs/client_reference.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 91444d117b1..03e812ff611 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -990,7 +990,7 @@ is controlled by *force_close* constructor's parameter). flag. :param int limit: total number simultaneous connections. If *limit* is - ``None`` the connector has no limit (default: 100). + ``0`` the connector has no limit (default: 100). :param int limit_per_host: limit simultaneous connections to the same endpoint. Endpoints are the same if they are @@ -1035,7 +1035,7 @@ is controlled by *force_close* constructor's parameter). Endpoints are the same if they are have equal ``(host, port, is_ssl)`` triple. - If *limit_per_host* is ``None`` the connector has no limit per host. + If *limit_per_host* is ``0`` the connector has no limit per host. Read-only property. @@ -1130,7 +1130,7 @@ is controlled by *force_close* constructor's parameter). updated refreshing each entry after N seconds. :param int limit: total number simultaneous connections. If *limit* is - ``None`` the connector has no limit (default: 100). + ``0`` the connector has no limit (default: 100). :param int limit_per_host: limit simultaneous connections to the same endpoint. Endpoints are the same if they are From 6c6d45b7ca392f0d2b5d9baba7f37002cfeaf1e0 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 21:02:31 +0100 Subject: [PATCH 098/296] [PR #8929/c0c3376e backport][3.10] Fix Site.name with empty host (#8934) **This is a backport of PR #8929 as merged into master (c0c3376e6699d0741424c43ac1b25beea366b1fd).** Co-authored-by: Sam Bull --- CHANGES/8929.bugfix.rst | 1 + aiohttp/web_runner.py | 2 +- tests/test_web_runner.py | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 CHANGES/8929.bugfix.rst diff --git a/CHANGES/8929.bugfix.rst b/CHANGES/8929.bugfix.rst new file mode 100644 index 00000000000..229d5abd0e7 --- /dev/null +++ b/CHANGES/8929.bugfix.rst @@ -0,0 +1 @@ +Fixed ``Site.name`` when host is an empty string -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_runner.py b/aiohttp/web_runner.py index 2fe229c4e50..0a237ede2c5 100644 --- a/aiohttp/web_runner.py +++ b/aiohttp/web_runner.py @@ -108,7 +108,7 @@ def __init__( @property def name(self) -> str: scheme = "https" if self._ssl_context else "http" - host = "0.0.0.0" if self._host is None else self._host + host = "0.0.0.0" if not self._host else self._host return str(URL.build(scheme=scheme, host=host, port=self._port)) async def start(self) -> None: diff --git a/tests/test_web_runner.py b/tests/test_web_runner.py index c7c94263234..b71c34fe912 100644 --- a/tests/test_web_runner.py +++ b/tests/test_web_runner.py @@ -165,6 +165,13 @@ async def mock_create_server(*args, **kwargs): assert port == 8080 +async def test_tcpsite_empty_str_host(make_runner: Any) -> None: + runner = make_runner() + await runner.setup() + site = web.TCPSite(runner, host="") + assert site.name == "http://0.0.0.0:8080" + + def test_run_after_asyncio_run() -> None: async def nothing(): pass From 37b2604d10e78b96b1fed51bab7f22221c868fc9 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 21:02:53 +0100 Subject: [PATCH 099/296] [PR #8929/c0c3376e backport][3.11] Fix Site.name with empty host (#8935) **This is a backport of PR #8929 as merged into master (c0c3376e6699d0741424c43ac1b25beea366b1fd).** Co-authored-by: Sam Bull --- CHANGES/8929.bugfix.rst | 1 + aiohttp/web_runner.py | 2 +- tests/test_web_runner.py | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 CHANGES/8929.bugfix.rst diff --git a/CHANGES/8929.bugfix.rst b/CHANGES/8929.bugfix.rst new file mode 100644 index 00000000000..229d5abd0e7 --- /dev/null +++ b/CHANGES/8929.bugfix.rst @@ -0,0 +1 @@ +Fixed ``Site.name`` when host is an empty string -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_runner.py b/aiohttp/web_runner.py index 2fe229c4e50..0a237ede2c5 100644 --- a/aiohttp/web_runner.py +++ b/aiohttp/web_runner.py @@ -108,7 +108,7 @@ def __init__( @property def name(self) -> str: scheme = "https" if self._ssl_context else "http" - host = "0.0.0.0" if self._host is None else self._host + host = "0.0.0.0" if not self._host else self._host return str(URL.build(scheme=scheme, host=host, port=self._port)) async def start(self) -> None: diff --git a/tests/test_web_runner.py b/tests/test_web_runner.py index c7c94263234..b71c34fe912 100644 --- a/tests/test_web_runner.py +++ b/tests/test_web_runner.py @@ -165,6 +165,13 @@ async def mock_create_server(*args, **kwargs): assert port == 8080 +async def test_tcpsite_empty_str_host(make_runner: Any) -> None: + runner = make_runner() + await runner.setup() + site = web.TCPSite(runner, host="") + assert site.name == "http://0.0.0.0:8080" + + def test_run_after_asyncio_run() -> None: async def nothing(): pass From 1dc3cd6d733f5707285afc623bd81d109acff812 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Thu, 29 Aug 2024 22:40:26 +0100 Subject: [PATCH 100/296] Fix Response.text when body is Payload (#6485) (#8937) (cherry picked from commit 9418a4a1486beed0ae8a5c47277ecd67758eb5e2) --- CHANGES/6485.bugfix.rst | 1 + aiohttp/multipart.py | 17 ++++++++++++++ aiohttp/payload.py | 36 ++++++++++++++++++++++++++-- aiohttp/payload_streamer.py | 3 +++ aiohttp/web_response.py | 27 +++++++++------------ tests/test_payload.py | 3 +++ tests/test_web_response.py | 47 ++++++++++++++++++++++++++++++++++++- 7 files changed, 115 insertions(+), 19 deletions(-) create mode 100644 CHANGES/6485.bugfix.rst diff --git a/CHANGES/6485.bugfix.rst b/CHANGES/6485.bugfix.rst new file mode 100644 index 00000000000..b1d912f1579 --- /dev/null +++ b/CHANGES/6485.bugfix.rst @@ -0,0 +1 @@ +Fixed ``Response.text`` when body is a ``Payload`` -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/multipart.py b/aiohttp/multipart.py index e3680a7b2a1..965e4f279d3 100644 --- a/aiohttp/multipart.py +++ b/aiohttp/multipart.py @@ -561,6 +561,8 @@ def filename(self) -> Optional[str]: @payload_type(BodyPartReader, order=Order.try_first) class BodyPartReaderPayload(Payload): + _value: BodyPartReader + def __init__(self, value: BodyPartReader, *args: Any, **kwargs: Any) -> None: super().__init__(value, *args, **kwargs) @@ -573,6 +575,9 @@ def __init__(self, value: BodyPartReader, *args: Any, **kwargs: Any) -> None: if params: self.set_content_disposition("attachment", True, **params) + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + raise TypeError("Unable to decode.") + async def write(self, writer: Any) -> None: field = self._value chunk = await field.read_chunk(size=2**16) @@ -790,6 +795,8 @@ async def _maybe_release_last_part(self) -> None: class MultipartWriter(Payload): """Multipart body writer.""" + _value: None + def __init__(self, subtype: str = "mixed", boundary: Optional[str] = None) -> None: boundary = boundary if boundary is not None else uuid.uuid4().hex # The underlying Payload API demands a str (utf-8), not bytes, @@ -970,6 +977,16 @@ def size(self) -> Optional[int]: total += 2 + len(self._boundary) + 4 # b'--'+self._boundary+b'--\r\n' return total + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + return "".join( + "--" + + self.boundary + + "\n" + + part._binary_headers.decode(encoding, errors) + + part.decode() + for part, _e, _te in self._parts + ) + async def write(self, writer: Any, close_boundary: bool = True) -> None: """Write body.""" for part, encoding, te_encoding in self._parts: diff --git a/aiohttp/payload.py b/aiohttp/payload.py index 5271393612a..e7039b46d1f 100644 --- a/aiohttp/payload.py +++ b/aiohttp/payload.py @@ -207,6 +207,13 @@ def set_content_disposition( disptype, quote_fields=quote_fields, _charset=_charset, **params ) + @abstractmethod + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + """Return string representation of the value. + + This is named decode() to allow compatibility with bytes objects. + """ + @abstractmethod async def write(self, writer: AbstractStreamWriter) -> None: """Write payload. @@ -216,6 +223,8 @@ async def write(self, writer: AbstractStreamWriter) -> None: class BytesPayload(Payload): + _value: bytes + def __init__( self, value: Union[bytes, bytearray, memoryview], *args: Any, **kwargs: Any ) -> None: @@ -242,6 +251,9 @@ def __init__( **kwargs, ) + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + return self._value.decode(encoding, errors) + async def write(self, writer: AbstractStreamWriter) -> None: await writer.write(self._value) @@ -283,7 +295,7 @@ def __init__(self, value: IO[str], *args: Any, **kwargs: Any) -> None: class IOBasePayload(Payload): - _value: IO[Any] + _value: io.IOBase def __init__( self, value: IO[Any], disposition: str = "attachment", *args: Any, **kwargs: Any @@ -307,9 +319,12 @@ async def write(self, writer: AbstractStreamWriter) -> None: finally: await loop.run_in_executor(None, self._value.close) + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + return "".join(r.decode(encoding, errors) for r in self._value.readlines()) + class TextIOPayload(IOBasePayload): - _value: TextIO + _value: io.TextIOBase def __init__( self, @@ -346,6 +361,9 @@ def size(self) -> Optional[int]: except OSError: return None + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + return self._value.read() + async def write(self, writer: AbstractStreamWriter) -> None: loop = asyncio.get_event_loop() try: @@ -363,6 +381,8 @@ async def write(self, writer: AbstractStreamWriter) -> None: class BytesIOPayload(IOBasePayload): + _value: io.BytesIO + @property def size(self) -> int: position = self._value.tell() @@ -370,8 +390,13 @@ def size(self) -> int: self._value.seek(position) return end - position + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + return self._value.read().decode(encoding, errors) + class BufferedReaderPayload(IOBasePayload): + _value: io.BufferedIOBase + @property def size(self) -> Optional[int]: try: @@ -381,6 +406,9 @@ def size(self) -> Optional[int]: # io.BufferedReader(io.BytesIO(b'data')) return None + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + return self._value.read().decode(encoding, errors) + class JsonPayload(BytesPayload): def __init__( @@ -417,6 +445,7 @@ def __init__( class AsyncIterablePayload(Payload): _iter: Optional[_AsyncIterator] = None + _value: _AsyncIterable def __init__(self, value: _AsyncIterable, *args: Any, **kwargs: Any) -> None: if not isinstance(value, AsyncIterable): @@ -444,6 +473,9 @@ async def write(self, writer: AbstractStreamWriter) -> None: except StopAsyncIteration: self._iter = None + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + raise TypeError("Unable to decode.") + class StreamReaderPayload(AsyncIterablePayload): def __init__(self, value: StreamReader, *args: Any, **kwargs: Any) -> None: diff --git a/aiohttp/payload_streamer.py b/aiohttp/payload_streamer.py index 364f763ae74..831fdc0a77f 100644 --- a/aiohttp/payload_streamer.py +++ b/aiohttp/payload_streamer.py @@ -65,6 +65,9 @@ class StreamWrapperPayload(Payload): async def write(self, writer: AbstractStreamWriter) -> None: await self._value(writer) + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + raise TypeError("Unable to decode.") + @payload_type(streamer) class StreamPayload(StreamWrapperPayload): diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 7074542621b..f583789d82e 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -72,6 +72,8 @@ class StreamResponse(BaseClass, HeadersMixin): _length_check = True + _body: Union[None, bytes, bytearray, Payload] + def __init__( self, *, @@ -650,21 +652,17 @@ def body(self) -> Optional[Union[bytes, Payload]]: return self._body @body.setter - def body(self, body: bytes) -> None: + def body(self, body: Any) -> None: if body is None: - self._body: Optional[bytes] = None - self._body_payload: bool = False + self._body = None elif isinstance(body, (bytes, bytearray)): self._body = body - self._body_payload = False else: try: self._body = body = payload.PAYLOAD_REGISTRY.get(body) except payload.LookupError: raise ValueError("Unsupported body type %r" % type(body)) - self._body_payload = True - headers = self._headers # set content-type @@ -697,7 +695,6 @@ def text(self, text: str) -> None: self.charset = "utf-8" self._body = text.encode(self.charset) - self._body_payload = False self._compressed_body = None @property @@ -711,7 +708,7 @@ def content_length(self) -> Optional[int]: if self._compressed_body is not None: # Return length of the compressed body return len(self._compressed_body) - elif self._body_payload: + elif isinstance(self._body, Payload): # A payload without content length, or a compressed payload return None elif self._body is not None: @@ -736,9 +733,8 @@ async def write_eof(self, data: bytes = b"") -> None: if body is not None: if self._must_be_empty_body: await super().write_eof() - elif self._body_payload: - payload = cast(Payload, body) - await payload.write(self._payload_writer) + elif isinstance(self._body, Payload): + await self._body.write(self._payload_writer) await super().write_eof() else: await super().write_eof(cast(bytes, body)) @@ -750,10 +746,9 @@ async def _start(self, request: "BaseRequest") -> AbstractStreamWriter: if hdrs.CONTENT_LENGTH in self._headers: del self._headers[hdrs.CONTENT_LENGTH] elif not self._chunked and hdrs.CONTENT_LENGTH not in self._headers: - if self._body_payload: - size = cast(Payload, self._body).size - if size is not None: - self._headers[hdrs.CONTENT_LENGTH] = str(size) + if isinstance(self._body, Payload): + if self._body.size is not None: + self._headers[hdrs.CONTENT_LENGTH] = str(self._body.size) else: body_len = len(self._body) if self._body else "0" # https://www.rfc-editor.org/rfc/rfc9110.html#section-8.6-7 @@ -765,7 +760,7 @@ async def _start(self, request: "BaseRequest") -> AbstractStreamWriter: return await super()._start(request) async def _do_start_compression(self, coding: ContentCoding) -> None: - if self._body_payload or self._chunked: + if self._chunked or isinstance(self._body, Payload): return await super()._do_start_compression(coding) if coding != ContentCoding.identity: diff --git a/tests/test_payload.py b/tests/test_payload.py index c8681cb5ebe..0e2db91135b 100644 --- a/tests/test_payload.py +++ b/tests/test_payload.py @@ -17,6 +17,9 @@ def registry(): class Payload(payload.Payload): + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + assert False + async def write(self, writer): pass diff --git a/tests/test_web_response.py b/tests/test_web_response.py index c3dab10c310..2e1e332e0a5 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -1,8 +1,10 @@ import collections.abc import datetime import gzip +import io import json from concurrent.futures import ThreadPoolExecutor +from typing import AsyncIterator, Optional from unittest import mock import aiosignal @@ -13,7 +15,8 @@ from aiohttp import HttpVersion, HttpVersion10, HttpVersion11, hdrs from aiohttp.helpers import ETag from aiohttp.http_writer import StreamWriter, _serialize_headers -from aiohttp.payload import BytesPayload +from aiohttp.multipart import BodyPartReader, MultipartWriter +from aiohttp.payload import BytesPayload, StringPayload from aiohttp.test_utils import make_mocked_coro, make_mocked_request from aiohttp.web import ContentCoding, Response, StreamResponse, json_response @@ -1119,6 +1122,48 @@ def test_assign_nonstr_text() -> None: assert 4 == resp.content_length +mpwriter = MultipartWriter(boundary="x") +mpwriter.append_payload(StringPayload("test")) + + +async def async_iter() -> AsyncIterator[str]: + yield "foo" # pragma: no cover + + +class CustomIO(io.IOBase): + def __init__(self): + self._lines = [b"", b"", b"test"] + + def read(self, size: int = -1) -> bytes: + return self._lines.pop() + + +@pytest.mark.parametrize( + "payload,expected", + ( + ("test", "test"), + (CustomIO(), "test"), + (io.StringIO("test"), "test"), + (io.TextIOWrapper(io.BytesIO(b"test")), "test"), + (io.BytesIO(b"test"), "test"), + (io.BufferedReader(io.BytesIO(b"test")), "test"), + (async_iter(), None), + (BodyPartReader("x", CIMultiDictProxy(CIMultiDict()), mock.Mock()), None), + ( + mpwriter, + "--x\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: 4\r\n\r\ntest", + ), + ), +) +def test_payload_body_get_text(payload, expected: Optional[str]) -> None: + resp = Response(body=payload) + if expected is None: + with pytest.raises(TypeError): + resp.text + else: + assert resp.text == expected + + def test_response_set_content_length() -> None: resp = Response() with pytest.raises(RuntimeError): From 948ca8c1063c4b9cb880137f11bf428d2e3d0de1 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Thu, 29 Aug 2024 22:40:37 +0100 Subject: [PATCH 101/296] Fix Response.text when body is Payload (#6485) (#8938) (cherry picked from commit 9418a4a1486beed0ae8a5c47277ecd67758eb5e2) --- CHANGES/6485.bugfix.rst | 1 + aiohttp/multipart.py | 17 ++++++++++++++ aiohttp/payload.py | 36 ++++++++++++++++++++++++++-- aiohttp/payload_streamer.py | 3 +++ aiohttp/web_response.py | 27 +++++++++------------ tests/test_payload.py | 3 +++ tests/test_web_response.py | 47 ++++++++++++++++++++++++++++++++++++- 7 files changed, 115 insertions(+), 19 deletions(-) create mode 100644 CHANGES/6485.bugfix.rst diff --git a/CHANGES/6485.bugfix.rst b/CHANGES/6485.bugfix.rst new file mode 100644 index 00000000000..b1d912f1579 --- /dev/null +++ b/CHANGES/6485.bugfix.rst @@ -0,0 +1 @@ +Fixed ``Response.text`` when body is a ``Payload`` -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/multipart.py b/aiohttp/multipart.py index e3680a7b2a1..965e4f279d3 100644 --- a/aiohttp/multipart.py +++ b/aiohttp/multipart.py @@ -561,6 +561,8 @@ def filename(self) -> Optional[str]: @payload_type(BodyPartReader, order=Order.try_first) class BodyPartReaderPayload(Payload): + _value: BodyPartReader + def __init__(self, value: BodyPartReader, *args: Any, **kwargs: Any) -> None: super().__init__(value, *args, **kwargs) @@ -573,6 +575,9 @@ def __init__(self, value: BodyPartReader, *args: Any, **kwargs: Any) -> None: if params: self.set_content_disposition("attachment", True, **params) + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + raise TypeError("Unable to decode.") + async def write(self, writer: Any) -> None: field = self._value chunk = await field.read_chunk(size=2**16) @@ -790,6 +795,8 @@ async def _maybe_release_last_part(self) -> None: class MultipartWriter(Payload): """Multipart body writer.""" + _value: None + def __init__(self, subtype: str = "mixed", boundary: Optional[str] = None) -> None: boundary = boundary if boundary is not None else uuid.uuid4().hex # The underlying Payload API demands a str (utf-8), not bytes, @@ -970,6 +977,16 @@ def size(self) -> Optional[int]: total += 2 + len(self._boundary) + 4 # b'--'+self._boundary+b'--\r\n' return total + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + return "".join( + "--" + + self.boundary + + "\n" + + part._binary_headers.decode(encoding, errors) + + part.decode() + for part, _e, _te in self._parts + ) + async def write(self, writer: Any, close_boundary: bool = True) -> None: """Write body.""" for part, encoding, te_encoding in self._parts: diff --git a/aiohttp/payload.py b/aiohttp/payload.py index 5271393612a..e7039b46d1f 100644 --- a/aiohttp/payload.py +++ b/aiohttp/payload.py @@ -207,6 +207,13 @@ def set_content_disposition( disptype, quote_fields=quote_fields, _charset=_charset, **params ) + @abstractmethod + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + """Return string representation of the value. + + This is named decode() to allow compatibility with bytes objects. + """ + @abstractmethod async def write(self, writer: AbstractStreamWriter) -> None: """Write payload. @@ -216,6 +223,8 @@ async def write(self, writer: AbstractStreamWriter) -> None: class BytesPayload(Payload): + _value: bytes + def __init__( self, value: Union[bytes, bytearray, memoryview], *args: Any, **kwargs: Any ) -> None: @@ -242,6 +251,9 @@ def __init__( **kwargs, ) + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + return self._value.decode(encoding, errors) + async def write(self, writer: AbstractStreamWriter) -> None: await writer.write(self._value) @@ -283,7 +295,7 @@ def __init__(self, value: IO[str], *args: Any, **kwargs: Any) -> None: class IOBasePayload(Payload): - _value: IO[Any] + _value: io.IOBase def __init__( self, value: IO[Any], disposition: str = "attachment", *args: Any, **kwargs: Any @@ -307,9 +319,12 @@ async def write(self, writer: AbstractStreamWriter) -> None: finally: await loop.run_in_executor(None, self._value.close) + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + return "".join(r.decode(encoding, errors) for r in self._value.readlines()) + class TextIOPayload(IOBasePayload): - _value: TextIO + _value: io.TextIOBase def __init__( self, @@ -346,6 +361,9 @@ def size(self) -> Optional[int]: except OSError: return None + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + return self._value.read() + async def write(self, writer: AbstractStreamWriter) -> None: loop = asyncio.get_event_loop() try: @@ -363,6 +381,8 @@ async def write(self, writer: AbstractStreamWriter) -> None: class BytesIOPayload(IOBasePayload): + _value: io.BytesIO + @property def size(self) -> int: position = self._value.tell() @@ -370,8 +390,13 @@ def size(self) -> int: self._value.seek(position) return end - position + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + return self._value.read().decode(encoding, errors) + class BufferedReaderPayload(IOBasePayload): + _value: io.BufferedIOBase + @property def size(self) -> Optional[int]: try: @@ -381,6 +406,9 @@ def size(self) -> Optional[int]: # io.BufferedReader(io.BytesIO(b'data')) return None + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + return self._value.read().decode(encoding, errors) + class JsonPayload(BytesPayload): def __init__( @@ -417,6 +445,7 @@ def __init__( class AsyncIterablePayload(Payload): _iter: Optional[_AsyncIterator] = None + _value: _AsyncIterable def __init__(self, value: _AsyncIterable, *args: Any, **kwargs: Any) -> None: if not isinstance(value, AsyncIterable): @@ -444,6 +473,9 @@ async def write(self, writer: AbstractStreamWriter) -> None: except StopAsyncIteration: self._iter = None + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + raise TypeError("Unable to decode.") + class StreamReaderPayload(AsyncIterablePayload): def __init__(self, value: StreamReader, *args: Any, **kwargs: Any) -> None: diff --git a/aiohttp/payload_streamer.py b/aiohttp/payload_streamer.py index 364f763ae74..831fdc0a77f 100644 --- a/aiohttp/payload_streamer.py +++ b/aiohttp/payload_streamer.py @@ -65,6 +65,9 @@ class StreamWrapperPayload(Payload): async def write(self, writer: AbstractStreamWriter) -> None: await self._value(writer) + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + raise TypeError("Unable to decode.") + @payload_type(streamer) class StreamPayload(StreamWrapperPayload): diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 7074542621b..f583789d82e 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -72,6 +72,8 @@ class StreamResponse(BaseClass, HeadersMixin): _length_check = True + _body: Union[None, bytes, bytearray, Payload] + def __init__( self, *, @@ -650,21 +652,17 @@ def body(self) -> Optional[Union[bytes, Payload]]: return self._body @body.setter - def body(self, body: bytes) -> None: + def body(self, body: Any) -> None: if body is None: - self._body: Optional[bytes] = None - self._body_payload: bool = False + self._body = None elif isinstance(body, (bytes, bytearray)): self._body = body - self._body_payload = False else: try: self._body = body = payload.PAYLOAD_REGISTRY.get(body) except payload.LookupError: raise ValueError("Unsupported body type %r" % type(body)) - self._body_payload = True - headers = self._headers # set content-type @@ -697,7 +695,6 @@ def text(self, text: str) -> None: self.charset = "utf-8" self._body = text.encode(self.charset) - self._body_payload = False self._compressed_body = None @property @@ -711,7 +708,7 @@ def content_length(self) -> Optional[int]: if self._compressed_body is not None: # Return length of the compressed body return len(self._compressed_body) - elif self._body_payload: + elif isinstance(self._body, Payload): # A payload without content length, or a compressed payload return None elif self._body is not None: @@ -736,9 +733,8 @@ async def write_eof(self, data: bytes = b"") -> None: if body is not None: if self._must_be_empty_body: await super().write_eof() - elif self._body_payload: - payload = cast(Payload, body) - await payload.write(self._payload_writer) + elif isinstance(self._body, Payload): + await self._body.write(self._payload_writer) await super().write_eof() else: await super().write_eof(cast(bytes, body)) @@ -750,10 +746,9 @@ async def _start(self, request: "BaseRequest") -> AbstractStreamWriter: if hdrs.CONTENT_LENGTH in self._headers: del self._headers[hdrs.CONTENT_LENGTH] elif not self._chunked and hdrs.CONTENT_LENGTH not in self._headers: - if self._body_payload: - size = cast(Payload, self._body).size - if size is not None: - self._headers[hdrs.CONTENT_LENGTH] = str(size) + if isinstance(self._body, Payload): + if self._body.size is not None: + self._headers[hdrs.CONTENT_LENGTH] = str(self._body.size) else: body_len = len(self._body) if self._body else "0" # https://www.rfc-editor.org/rfc/rfc9110.html#section-8.6-7 @@ -765,7 +760,7 @@ async def _start(self, request: "BaseRequest") -> AbstractStreamWriter: return await super()._start(request) async def _do_start_compression(self, coding: ContentCoding) -> None: - if self._body_payload or self._chunked: + if self._chunked or isinstance(self._body, Payload): return await super()._do_start_compression(coding) if coding != ContentCoding.identity: diff --git a/tests/test_payload.py b/tests/test_payload.py index c8681cb5ebe..0e2db91135b 100644 --- a/tests/test_payload.py +++ b/tests/test_payload.py @@ -17,6 +17,9 @@ def registry(): class Payload(payload.Payload): + def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: + assert False + async def write(self, writer): pass diff --git a/tests/test_web_response.py b/tests/test_web_response.py index c3dab10c310..2e1e332e0a5 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -1,8 +1,10 @@ import collections.abc import datetime import gzip +import io import json from concurrent.futures import ThreadPoolExecutor +from typing import AsyncIterator, Optional from unittest import mock import aiosignal @@ -13,7 +15,8 @@ from aiohttp import HttpVersion, HttpVersion10, HttpVersion11, hdrs from aiohttp.helpers import ETag from aiohttp.http_writer import StreamWriter, _serialize_headers -from aiohttp.payload import BytesPayload +from aiohttp.multipart import BodyPartReader, MultipartWriter +from aiohttp.payload import BytesPayload, StringPayload from aiohttp.test_utils import make_mocked_coro, make_mocked_request from aiohttp.web import ContentCoding, Response, StreamResponse, json_response @@ -1119,6 +1122,48 @@ def test_assign_nonstr_text() -> None: assert 4 == resp.content_length +mpwriter = MultipartWriter(boundary="x") +mpwriter.append_payload(StringPayload("test")) + + +async def async_iter() -> AsyncIterator[str]: + yield "foo" # pragma: no cover + + +class CustomIO(io.IOBase): + def __init__(self): + self._lines = [b"", b"", b"test"] + + def read(self, size: int = -1) -> bytes: + return self._lines.pop() + + +@pytest.mark.parametrize( + "payload,expected", + ( + ("test", "test"), + (CustomIO(), "test"), + (io.StringIO("test"), "test"), + (io.TextIOWrapper(io.BytesIO(b"test")), "test"), + (io.BytesIO(b"test"), "test"), + (io.BufferedReader(io.BytesIO(b"test")), "test"), + (async_iter(), None), + (BodyPartReader("x", CIMultiDictProxy(CIMultiDict()), mock.Mock()), None), + ( + mpwriter, + "--x\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: 4\r\n\r\ntest", + ), + ), +) +def test_payload_body_get_text(payload, expected: Optional[str]) -> None: + resp = Response(body=payload) + if expected is None: + with pytest.raises(TypeError): + resp.text + else: + assert resp.text == expected + + def test_response_set_content_length() -> None: resp = Response() with pytest.raises(RuntimeError): From 817393235285ac0fd497ee2f14549471929a68df Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 23:02:01 +0100 Subject: [PATCH 102/296] [PR #8920/5cf5db56 backport][3.11] Re-enable keep-alive on proxies (#8939) **This is a backport of PR #8920 as merged into master (5cf5db569c8f32b5ecd6a1586e4b8c349767d941).** Co-authored-by: Sam Bull --- CHANGES/8920.misc.rst | 1 + aiohttp/client_proto.py | 3 --- aiohttp/connector.py | 5 ----- 3 files changed, 1 insertion(+), 8 deletions(-) create mode 100644 CHANGES/8920.misc.rst diff --git a/CHANGES/8920.misc.rst b/CHANGES/8920.misc.rst new file mode 100644 index 00000000000..2e8640593a4 --- /dev/null +++ b/CHANGES/8920.misc.rst @@ -0,0 +1 @@ +Enabled keep-alive support on proxies (which was originally disabled several years ago) -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/client_proto.py b/aiohttp/client_proto.py index f8c83240209..e612450c746 100644 --- a/aiohttp/client_proto.py +++ b/aiohttp/client_proto.py @@ -62,9 +62,6 @@ def should_close(self) -> bool: or bool(self._tail) ) - def force_close(self) -> None: - self._should_close = True - def close(self) -> None: transport = self.transport if transport is not None: diff --git a/aiohttp/connector.py b/aiohttp/connector.py index 04115c36a24..91174e319ab 100644 --- a/aiohttp/connector.py +++ b/aiohttp/connector.py @@ -1376,11 +1376,6 @@ async def _create_proxy_connection( proxy_req, [], timeout, client_error=ClientProxyConnectionError ) - # Many HTTP proxies has buggy keepalive support. Let's not - # reuse connection but close it after processing every - # response. - proto.force_close() - auth = proxy_req.headers.pop(hdrs.AUTHORIZATION, None) if auth is not None: if not req.is_ssl(): From 297ddaec57bbe07c1c51bbe5909a01eedab2ff87 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Fri, 30 Aug 2024 12:07:30 +0100 Subject: [PATCH 103/296] Fix tarfile file-like objects used as data (#6747) (#8940) (cherry picked from commit 768123537ec988ea629a829623e8d72e7aec1c41) Co-authored-by: Xavier Halloran <75104372+ReallyReivax@users.noreply.github.com> --- CHANGES/6732.bugfix | 1 + CONTRIBUTORS.txt | 1 + aiohttp/payload.py | 4 ++- tests/test_client_functional.py | 57 +++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 CHANGES/6732.bugfix diff --git a/CHANGES/6732.bugfix b/CHANGES/6732.bugfix new file mode 100644 index 00000000000..a460d7cd695 --- /dev/null +++ b/CHANGES/6732.bugfix @@ -0,0 +1 @@ +Fixed handling of some file-like objects (e.g. ``tarfile.extractfile()``) which raise ``AttributeError`` instead of ``OSError`` when ``fileno`` fails for streaming payload data -- by :user:`ReallyReivax`. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 57a4d2dbcf3..8f387459948 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -353,6 +353,7 @@ William Grzybowski William S. Wilson Ong wouter bolsterlee +Xavier Halloran Xiang Li Yang Zhou Yannick Koechlin diff --git a/aiohttp/payload.py b/aiohttp/payload.py index e7039b46d1f..27636977774 100644 --- a/aiohttp/payload.py +++ b/aiohttp/payload.py @@ -401,9 +401,11 @@ class BufferedReaderPayload(IOBasePayload): def size(self) -> Optional[int]: try: return os.fstat(self._value.fileno()).st_size - self._value.tell() - except OSError: + except (OSError, AttributeError): # data.fileno() is not supported, e.g. # io.BufferedReader(io.BytesIO(b'data')) + # For some file-like objects (e.g. tarfile), the fileno() attribute may + # not exist at all, and will instead raise an AttributeError. return None def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 18fb5fe9f86..1f9173bd3f7 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -9,7 +9,9 @@ import socket import ssl import sys +import tarfile import time +import zipfile from typing import Any, AsyncIterator, Type from unittest import mock @@ -511,6 +513,61 @@ async def handler(request): assert 200 == resp.status +async def test_post_data_zipfile_filelike(aiohttp_client: AiohttpClient) -> None: + data = b"This is a zip file payload text file." + + async def handler(request: web.Request) -> web.Response: + val = await request.read() + assert data == val, "Transmitted zipfile member failed to match original data." + return web.Response() + + app = web.Application() + app.router.add_route("POST", "/", handler) + client = await aiohttp_client(app) + + buf = io.BytesIO() + with zipfile.ZipFile(file=buf, mode="w") as zf: + with zf.open("payload1.txt", mode="w") as zip_filelike_writing: + zip_filelike_writing.write(data) + + buf.seek(0) + zf = zipfile.ZipFile(file=buf, mode="r") + resp = await client.post("/", data=zf.open("payload1.txt")) + assert 200 == resp.status + + +async def test_post_data_tarfile_filelike(aiohttp_client: AiohttpClient) -> None: + data = b"This is a tar file payload text file." + + async def handler(request: web.Request) -> web.Response: + val = await request.read() + assert data == val, "Transmitted tarfile member failed to match original data." + return web.Response() + + app = web.Application() + app.router.add_route("POST", "/", handler) + client = await aiohttp_client(app) + + buf = io.BytesIO() + with tarfile.open(fileobj=buf, mode="w") as tf: + ti = tarfile.TarInfo(name="payload1.txt") + ti.size = len(data) + tf.addfile(tarinfo=ti, fileobj=io.BytesIO(data)) + + # Random-access tarfile. + buf.seek(0) + tf = tarfile.open(fileobj=buf, mode="r:") + resp = await client.post("/", data=tf.extractfile("payload1.txt")) + assert 200 == resp.status + + # Streaming tarfile. + buf.seek(0) + tf = tarfile.open(fileobj=buf, mode="r|") + for entry in tf: + resp = await client.post("/", data=tf.extractfile(entry)) + assert 200 == resp.status + + async def test_ssl_client( aiohttp_server, ssl_ctx, From 950f72c18266b55a9fcb93717b32828eae37719c Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Fri, 30 Aug 2024 12:07:46 +0100 Subject: [PATCH 104/296] Fix tarfile file-like objects used as data (#6747) (#8941) (cherry picked from commit 768123537ec988ea629a829623e8d72e7aec1c41) Co-authored-by: Xavier Halloran <75104372+ReallyReivax@users.noreply.github.com> --- CHANGES/6732.bugfix | 1 + CONTRIBUTORS.txt | 1 + aiohttp/payload.py | 4 ++- tests/test_client_functional.py | 57 +++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 CHANGES/6732.bugfix diff --git a/CHANGES/6732.bugfix b/CHANGES/6732.bugfix new file mode 100644 index 00000000000..a460d7cd695 --- /dev/null +++ b/CHANGES/6732.bugfix @@ -0,0 +1 @@ +Fixed handling of some file-like objects (e.g. ``tarfile.extractfile()``) which raise ``AttributeError`` instead of ``OSError`` when ``fileno`` fails for streaming payload data -- by :user:`ReallyReivax`. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 57a4d2dbcf3..8f387459948 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -353,6 +353,7 @@ William Grzybowski William S. Wilson Ong wouter bolsterlee +Xavier Halloran Xiang Li Yang Zhou Yannick Koechlin diff --git a/aiohttp/payload.py b/aiohttp/payload.py index e7039b46d1f..27636977774 100644 --- a/aiohttp/payload.py +++ b/aiohttp/payload.py @@ -401,9 +401,11 @@ class BufferedReaderPayload(IOBasePayload): def size(self) -> Optional[int]: try: return os.fstat(self._value.fileno()).st_size - self._value.tell() - except OSError: + except (OSError, AttributeError): # data.fileno() is not supported, e.g. # io.BufferedReader(io.BytesIO(b'data')) + # For some file-like objects (e.g. tarfile), the fileno() attribute may + # not exist at all, and will instead raise an AttributeError. return None def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str: diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 18fb5fe9f86..1f9173bd3f7 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -9,7 +9,9 @@ import socket import ssl import sys +import tarfile import time +import zipfile from typing import Any, AsyncIterator, Type from unittest import mock @@ -511,6 +513,61 @@ async def handler(request): assert 200 == resp.status +async def test_post_data_zipfile_filelike(aiohttp_client: AiohttpClient) -> None: + data = b"This is a zip file payload text file." + + async def handler(request: web.Request) -> web.Response: + val = await request.read() + assert data == val, "Transmitted zipfile member failed to match original data." + return web.Response() + + app = web.Application() + app.router.add_route("POST", "/", handler) + client = await aiohttp_client(app) + + buf = io.BytesIO() + with zipfile.ZipFile(file=buf, mode="w") as zf: + with zf.open("payload1.txt", mode="w") as zip_filelike_writing: + zip_filelike_writing.write(data) + + buf.seek(0) + zf = zipfile.ZipFile(file=buf, mode="r") + resp = await client.post("/", data=zf.open("payload1.txt")) + assert 200 == resp.status + + +async def test_post_data_tarfile_filelike(aiohttp_client: AiohttpClient) -> None: + data = b"This is a tar file payload text file." + + async def handler(request: web.Request) -> web.Response: + val = await request.read() + assert data == val, "Transmitted tarfile member failed to match original data." + return web.Response() + + app = web.Application() + app.router.add_route("POST", "/", handler) + client = await aiohttp_client(app) + + buf = io.BytesIO() + with tarfile.open(fileobj=buf, mode="w") as tf: + ti = tarfile.TarInfo(name="payload1.txt") + ti.size = len(data) + tf.addfile(tarinfo=ti, fileobj=io.BytesIO(data)) + + # Random-access tarfile. + buf.seek(0) + tf = tarfile.open(fileobj=buf, mode="r:") + resp = await client.post("/", data=tf.extractfile("payload1.txt")) + assert 200 == resp.status + + # Streaming tarfile. + buf.seek(0) + tf = tarfile.open(fileobj=buf, mode="r|") + for entry in tf: + resp = await client.post("/", data=tf.extractfile(entry)) + assert 200 == resp.status + + async def test_ssl_client( aiohttp_server, ssl_ctx, From 45f8212d75bdfb0f02d4b005e7167c8933bf9fc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 11:23:25 +0000 Subject: [PATCH 105/296] Bump certifi from 2024.7.4 to 2024.8.30 (#8943) Bumps [certifi](https://github.com/certifi/python-certifi) from 2024.7.4 to 2024.8.30.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=certifi&package-manager=pip&previous-version=2024.7.4&new-version=2024.8.30)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- requirements/lint.txt | 2 +- requirements/test.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 30e89bf4ba9..479a31fd4b8 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -34,7 +34,7 @@ brotli==1.1.0 ; platform_python_implementation == "CPython" # via -r requirements/runtime-deps.in build==1.2.1 # via pip-tools -certifi==2024.7.4 +certifi==2024.8.30 # via requests cffi==1.17.0 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index a3eea2d8d88..91c137d42e0 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -34,7 +34,7 @@ brotli==1.1.0 ; platform_python_implementation == "CPython" # via -r requirements/runtime-deps.in build==1.2.1 # via pip-tools -certifi==2024.7.4 +certifi==2024.8.30 # via requests cffi==1.17.0 # via diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index ae5f3a95597..b0b6bb6d469 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -12,7 +12,7 @@ babel==2.16.0 # via sphinx blockdiag==3.0.0 # via sphinxcontrib-blockdiag -certifi==2024.7.4 +certifi==2024.8.30 # via requests charset-normalizer==3.3.2 # via requests diff --git a/requirements/doc.txt b/requirements/doc.txt index 6e247c15b6e..172dedd5016 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -12,7 +12,7 @@ babel==2.16.0 # via sphinx blockdiag==3.0.0 # via sphinxcontrib-blockdiag -certifi==2024.7.4 +certifi==2024.8.30 # via requests charset-normalizer==3.3.2 # via requests diff --git a/requirements/lint.txt b/requirements/lint.txt index 6d0647239ee..82efbf9a4ee 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -12,7 +12,7 @@ annotated-types==0.7.0 # via pydantic async-timeout==4.0.3 # via aioredis -certifi==2024.7.4 +certifi==2024.8.30 # via requests cffi==1.17.0 # via diff --git a/requirements/test.txt b/requirements/test.txt index 2c63a08a37f..ae1b806487f 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -18,7 +18,7 @@ attrs==24.2.0 # via -r requirements/runtime-deps.in brotli==1.1.0 ; platform_python_implementation == "CPython" # via -r requirements/runtime-deps.in -certifi==2024.7.4 +certifi==2024.8.30 # via requests cffi==1.17.0 # via From aa90362bca7a93c71d7a39a009c50dc60d8bb2ae Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 15:12:28 +0100 Subject: [PATCH 106/296] [PR #8936/76a00d1e backport][3.11] Test coverage of TextIOWrapper (#8945) **This is a backport of PR #8936 as merged into master (76a00d1ed405c87cf33c66f91d410c95e57c0312).** --------- Co-authored-by: Sam Bull --- tests/test_formdata.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_formdata.py b/tests/test_formdata.py index 4bb8aa07587..db1a3861c56 100644 --- a/tests/test_formdata.py +++ b/tests/test_formdata.py @@ -1,3 +1,4 @@ +import io from unittest import mock import pytest @@ -46,6 +47,16 @@ def test_invalid_formdata_params2() -> None: FormData("as") # 2-char str is not allowed +async def test_formdata_textio_charset(buf: bytearray, writer) -> None: + form = FormData() + body = io.TextIOWrapper(io.BytesIO(b"\xe6\x97\xa5\xe6\x9c\xac"), encoding="utf-8") + form.add_field("foo", body, content_type="text/plain; charset=shift-jis") + payload = form() + await payload.write(writer) + assert b"charset=shift-jis" in buf + assert b"\x93\xfa\x96{" in buf + + def test_invalid_formdata_content_type() -> None: form = FormData() invalid_vals = [0, 0.1, {}, [], b"foo"] From 181c042ed7e2c2a7fac9d1a23c7d580eee354200 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 15:12:40 +0100 Subject: [PATCH 107/296] [PR #8936/76a00d1e backport][3.10] Test coverage of TextIOWrapper (#8944) **This is a backport of PR #8936 as merged into master (76a00d1ed405c87cf33c66f91d410c95e57c0312).** --------- Co-authored-by: Sam Bull --- tests/test_formdata.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_formdata.py b/tests/test_formdata.py index 4bb8aa07587..db1a3861c56 100644 --- a/tests/test_formdata.py +++ b/tests/test_formdata.py @@ -1,3 +1,4 @@ +import io from unittest import mock import pytest @@ -46,6 +47,16 @@ def test_invalid_formdata_params2() -> None: FormData("as") # 2-char str is not allowed +async def test_formdata_textio_charset(buf: bytearray, writer) -> None: + form = FormData() + body = io.TextIOWrapper(io.BytesIO(b"\xe6\x97\xa5\xe6\x9c\xac"), encoding="utf-8") + form.add_field("foo", body, content_type="text/plain; charset=shift-jis") + payload = form() + await payload.write(writer) + assert b"charset=shift-jis" in buf + assert b"\x93\xfa\x96{" in buf + + def test_invalid_formdata_content_type() -> None: form = FormData() invalid_vals = [0, 0.1, {}, [], b"foo"] From a40dbad6f9ab2562892259716ff3fcda77ec759a Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 19:55:32 +0000 Subject: [PATCH 108/296] [PR #8948/cc6d7632 backport][3.10] Make n argument clearer (#8949) Co-authored-by: Sam Bull --- docs/streams.rst | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/streams.rst b/docs/streams.rst index 10eec6d6a43..9d49a80f1b6 100644 --- a/docs/streams.rst +++ b/docs/streams.rst @@ -26,13 +26,17 @@ Reading Methods .. method:: StreamReader.read(n=-1) :async: - Read up to *n* bytes. If *n* is not provided, or set to ``-1``, read until - EOF and return all read bytes. + Read up to a maximum of *n* bytes. If *n* is not provided, or set to ``-1``, + read until EOF and return all read bytes. + + When *n* is provided, data will be returned as soon as it is available. + Therefore it will return less than *n* bytes if there are less than *n* + bytes in the buffer. If the EOF was received and the internal buffer is empty, return an empty bytes object. - :param int n: how many bytes to read, ``-1`` for the whole stream. + :param int n: maximum number of bytes to read, ``-1`` for the whole stream. :return bytes: the given data @@ -127,6 +131,14 @@ size limit and over any available data. async for data in response.content.iter_chunked(1024): print(data) + To get chunks that are exactly *n* bytes, you could use the + `asyncstdlib.itertools `_ + module:: + + chunks = batched(chain.from_iterable(response.content.iter_chunked(n)), n) + async for data in chunks: + print(data) + .. method:: StreamReader.iter_any() :async: From 784604e480eef695179afd9f97bc5fb948db6244 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 20:01:47 +0000 Subject: [PATCH 109/296] [PR #8948/cc6d7632 backport][3.11] Make n argument clearer (#8950) Co-authored-by: Sam Bull --- docs/streams.rst | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/streams.rst b/docs/streams.rst index 10eec6d6a43..9d49a80f1b6 100644 --- a/docs/streams.rst +++ b/docs/streams.rst @@ -26,13 +26,17 @@ Reading Methods .. method:: StreamReader.read(n=-1) :async: - Read up to *n* bytes. If *n* is not provided, or set to ``-1``, read until - EOF and return all read bytes. + Read up to a maximum of *n* bytes. If *n* is not provided, or set to ``-1``, + read until EOF and return all read bytes. + + When *n* is provided, data will be returned as soon as it is available. + Therefore it will return less than *n* bytes if there are less than *n* + bytes in the buffer. If the EOF was received and the internal buffer is empty, return an empty bytes object. - :param int n: how many bytes to read, ``-1`` for the whole stream. + :param int n: maximum number of bytes to read, ``-1`` for the whole stream. :return bytes: the given data @@ -127,6 +131,14 @@ size limit and over any available data. async for data in response.content.iter_chunked(1024): print(data) + To get chunks that are exactly *n* bytes, you could use the + `asyncstdlib.itertools `_ + module:: + + chunks = batched(chain.from_iterable(response.content.iter_chunked(n)), n) + async for data in chunks: + print(data) + .. method:: StreamReader.iter_any() :async: From e8d66381f44d36d628a9e19d7c116be6909ce639 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sat, 31 Aug 2024 16:38:40 +0100 Subject: [PATCH 110/296] [PR #8953/51ea3b39 backport][3.11] Add clarification about auth parameter in ClientSession (#8955) **This is a backport of PR #8953 as merged into master (51ea3b39a454993dcea4bb9d3d3baa4f1a515eb0).** Co-authored-by: Maxim Zemskov --- CHANGES/6764.doc.rst | 1 + docs/client_reference.rst | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 CHANGES/6764.doc.rst diff --git a/CHANGES/6764.doc.rst b/CHANGES/6764.doc.rst new file mode 100644 index 00000000000..dea2019fc76 --- /dev/null +++ b/CHANGES/6764.doc.rst @@ -0,0 +1 @@ +Clarified that auth parameter in ClientSession will persist and be included with any request to any origin, even during redirects to different origins. -- by :user:`MaximZemskov`. diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 03e812ff611..4e63552cd5c 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -99,7 +99,10 @@ The client session supports the context manager protocol for self closing. Iterable of :class:`str` or :class:`~multidict.istr` (optional) :param aiohttp.BasicAuth auth: an object that represents HTTP Basic - Authorization (optional) + Authorization (optional). It will be included + with any request to any origin and will not be + removed, event during redirect to a different + origin. :param version: supported HTTP version, ``HTTP 1.1`` by default. From 96c8e3e7edd7b68fd8f582dda19bad238f4f5a3c Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sat, 31 Aug 2024 16:38:54 +0100 Subject: [PATCH 111/296] [PR #8953/51ea3b39 backport][3.10] Add clarification about auth parameter in ClientSession (#8954) **This is a backport of PR #8953 as merged into master (51ea3b39a454993dcea4bb9d3d3baa4f1a515eb0).** Co-authored-by: Maxim Zemskov --- CHANGES/6764.doc.rst | 1 + docs/client_reference.rst | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 CHANGES/6764.doc.rst diff --git a/CHANGES/6764.doc.rst b/CHANGES/6764.doc.rst new file mode 100644 index 00000000000..dea2019fc76 --- /dev/null +++ b/CHANGES/6764.doc.rst @@ -0,0 +1 @@ +Clarified that auth parameter in ClientSession will persist and be included with any request to any origin, even during redirects to different origins. -- by :user:`MaximZemskov`. diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 03e812ff611..4e63552cd5c 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -99,7 +99,10 @@ The client session supports the context manager protocol for self closing. Iterable of :class:`str` or :class:`~multidict.istr` (optional) :param aiohttp.BasicAuth auth: an object that represents HTTP Basic - Authorization (optional) + Authorization (optional). It will be included + with any request to any origin and will not be + removed, event during redirect to a different + origin. :param version: supported HTTP version, ``HTTP 1.1`` by default. From 747750379ac3c4d39a666306778f4adb3e2abce7 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 31 Aug 2024 19:50:55 +0100 Subject: [PATCH 112/296] Add parameters to del_cookie() (#8956) (#8959) (cherry picked from commit 97384260b53ac7d77ee4e5d00456a5a1962972a9) --- CHANGES/8956.feature.rst | 1 + aiohttp/web_response.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 CHANGES/8956.feature.rst diff --git a/CHANGES/8956.feature.rst b/CHANGES/8956.feature.rst new file mode 100644 index 00000000000..245b481089a --- /dev/null +++ b/CHANGES/8956.feature.rst @@ -0,0 +1 @@ +Added ``secure``/``httponly``/``samesite`` parameters to ``.del_cookie()`` -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index f583789d82e..0020afd46c8 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -251,7 +251,14 @@ def set_cookie( c["samesite"] = samesite def del_cookie( - self, name: str, *, domain: Optional[str] = None, path: str = "/" + self, + name: str, + *, + domain: Optional[str] = None, + path: str = "/", + secure: Optional[bool] = None, + httponly: Optional[bool] = None, + samesite: Optional[str] = None, ) -> None: """Delete cookie. @@ -266,6 +273,9 @@ def del_cookie( expires="Thu, 01 Jan 1970 00:00:00 GMT", domain=domain, path=path, + secure=secure, + httponly=httponly, + samesite=samesite, ) @property From 9ac6853114cde3ff0865fd5039dc2c19d1c25859 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 31 Aug 2024 20:28:37 +0100 Subject: [PATCH 113/296] Fix router matching pre-encoded URLs (#8898) (#8960) Co-authored-by: J. Nick Koston (cherry picked from commit 6be94520ea46fe1829e6c9d986e7fc9f7db50cad) --- CHANGES/8898.bugfix.rst | 1 + aiohttp/web_urldispatcher.py | 10 +++++----- requirements/base.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test.txt | 2 +- tests/test_urldispatch.py | 29 +++++++++++++++++------------ tests/test_web_urldispatcher.py | 12 ++++-------- 9 files changed, 32 insertions(+), 30 deletions(-) create mode 100644 CHANGES/8898.bugfix.rst diff --git a/CHANGES/8898.bugfix.rst b/CHANGES/8898.bugfix.rst new file mode 100644 index 00000000000..0de6646c8cb --- /dev/null +++ b/CHANGES/8898.bugfix.rst @@ -0,0 +1 @@ +Fixed web router not matching pre-encoded URLs (requires yarl 1.9.6+) -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index aee7aecd2a9..a1df64b8e61 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -381,7 +381,7 @@ def register_route(self, route: "ResourceRoute") -> None: async def resolve(self, request: Request) -> _Resolve: allowed_methods: Set[str] = set() - match_dict = self._match(request.rel_url.raw_path) + match_dict = self._match(request.rel_url.path) if match_dict is None: return None, allowed_methods @@ -650,7 +650,7 @@ def set_options_route(self, handler: Handler) -> None: ) async def resolve(self, request: Request) -> _Resolve: - path = request.rel_url.raw_path + path = request.rel_url.path method = request.method allowed_methods = set(self._routes) if not path.startswith(self._prefix2) and path != self._prefix: @@ -1040,7 +1040,7 @@ async def resolve(self, request: Request) -> UrlMappingMatchInfo: # candidates for a given url part because there are multiple resources # registered for the same canonical path, we resolve them in a linear # fashion to ensure registration order is respected. - url_part = request.rel_url.raw_path + url_part = request.rel_url.path while url_part: for candidate in resource_index.get(url_part, ()): match_dict, allowed = await candidate.resolve(request) @@ -1165,7 +1165,7 @@ def add_resource(self, path: str, *, name: Optional[str] = None) -> Resource: if resource.name == name and resource.raw_match(path): return cast(Resource, resource) if not ("{" in path or "}" in path or ROUTE_RE.search(path)): - resource = PlainResource(_requote_path(path), name=name) + resource = PlainResource(path, name=name) self.register_resource(resource) return resource resource = DynamicResource(path, name=name) @@ -1292,7 +1292,7 @@ def _quote_path(value: str) -> str: def _unquote_path(value: str) -> str: - return URL.build(path=value, encoded=True).path + return URL.build(path=value, encoded=True).path.replace("%2F", "/") def _requote_path(value: str) -> str: diff --git a/requirements/base.txt b/requirements/base.txt index 50817d4db17..18b5f471150 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -38,5 +38,5 @@ pycparser==2.22 # via cffi uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in -yarl==1.9.4 +yarl==1.9.6 # via -r requirements/runtime-deps.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 479a31fd4b8..869bb3d1b34 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -286,7 +286,7 @@ webcolors==24.8.0 # via blockdiag wheel==0.44.0 # via pip-tools -yarl==1.9.4 +yarl==1.9.6 # via -r requirements/runtime-deps.in zipp==3.20.1 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 91c137d42e0..9555838a4bf 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -278,7 +278,7 @@ webcolors==24.8.0 # via blockdiag wheel==0.44.0 # via pip-tools -yarl==1.9.4 +yarl==1.9.6 # via -r requirements/runtime-deps.in zipp==3.20.1 # via diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index a8c2eebe1c8..c876071fc19 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -32,5 +32,5 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -yarl==1.9.4 +yarl==1.9.6 # via -r requirements/runtime-deps.in diff --git a/requirements/test.txt b/requirements/test.txt index ae1b806487f..3caac33849b 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -136,5 +136,5 @@ uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpytho # via -r requirements/base.in wait-for-it==2.2.2 # via -r requirements/test.in -yarl==1.9.4 +yarl==1.9.6 # via -r requirements/runtime-deps.in diff --git a/tests/test_urldispatch.py b/tests/test_urldispatch.py index 2453ab5a235..f06f73edc21 100644 --- a/tests/test_urldispatch.py +++ b/tests/test_urldispatch.py @@ -457,7 +457,7 @@ def test_add_static_quoting(router) -> None: ) assert router["static"] is resource url = resource.url_for(filename="/1 2/файл%2F.txt") - assert url.path == "/пре /фикс/1 2/файл%2F.txt" + assert url.path == "/пре %2Fфикс/1 2/файл%2F.txt" assert str(url) == ( "/%D0%BF%D1%80%D0%B5%20%2F%D1%84%D0%B8%D0%BA%D1%81" "/1%202/%D1%84%D0%B0%D0%B9%D0%BB%252F.txt" @@ -530,19 +530,24 @@ def test_static_remove_trailing_slash(router) -> None: assert "/prefix" == route._prefix -async def test_add_route_with_re(router) -> None: +@pytest.mark.parametrize( + "pattern,url,expected", + ( + (r"{to:\d+}", r"1234", {"to": "1234"}), + ("{name}.html", "test.html", {"name": "test"}), + (r"{fn:\w+ \d+}", "abc 123", {"fn": "abc 123"}), + (r"{fn:\w+\s\d+}", "abc 123", {"fn": "abc 123"}), + ), +) +async def test_add_route_with_re( + router: web.UrlDispatcher, pattern: str, url: str, expected +) -> None: handler = make_handler() - router.add_route("GET", r"/handler/{to:\d+}", handler) - - req = make_mocked_request("GET", "/handler/1234") + router.add_route("GET", f"/handler/{pattern}", handler) + req = make_mocked_request("GET", f"/handler/{url}") info = await router.resolve(req) assert info is not None - assert {"to": "1234"} == info - - router.add_route("GET", r"/handler/{name}.html", handler) - req = make_mocked_request("GET", "/handler/test.html") - info = await router.resolve(req) - assert {"name": "test"} == info + assert info == expected async def test_add_route_with_re_and_slashes(router) -> None: @@ -625,7 +630,7 @@ def test_route_dynamic_quoting(router) -> None: route = router.add_route("GET", r"/пре %2Fфикс/{arg}", handler) url = route.url_for(arg="1 2/текст%2F") - assert url.path == "/пре /фикс/1 2/текст%2F" + assert url.path == "/пре %2Fфикс/1 2/текст%2F" assert str(url) == ( "/%D0%BF%D1%80%D0%B5%20%2F%D1%84%D0%B8%D0%BA%D1%81" "/1%202/%D1%82%D0%B5%D0%BA%D1%81%D1%82%252F" diff --git a/tests/test_web_urldispatcher.py b/tests/test_web_urldispatcher.py index 3a45b9355f5..8a97acf504d 100644 --- a/tests/test_web_urldispatcher.py +++ b/tests/test_web_urldispatcher.py @@ -856,18 +856,15 @@ async def get_foobar(request: web.Request) -> web.Response: assert (await resp.text()) == "success!" -@pytest.mark.xfail( - raises=AssertionError, - reason="Regression in v3.7: https://github.com/aio-libs/aiohttp/issues/5621", -) @pytest.mark.parametrize( ("route_definition", "urlencoded_path", "expected_http_resp_status"), ( ("/467,802,24834/hello", "/467%2C802%2C24834/hello", 200), ("/{user_ids:([0-9]+)(,([0-9]+))*}/hello", "/467%2C802%2C24834/hello", 200), + ("/467,802,24834/hello", "/467,802,24834/hello", 200), + ("/{user_ids:([0-9]+)(,([0-9]+))*}/hello", "/467,802,24834/hello", 200), ("/1%2C3/hello", "/1%2C3/hello", 404), ), - ids=("urldecoded_route", "urldecoded_route_with_regex", "urlencoded_route"), ) async def test_decoded_url_match( aiohttp_client: AiohttpClient, @@ -883,9 +880,8 @@ async def handler(request: web.Request) -> web.Response: app.router.add_get(route_definition, handler) client = await aiohttp_client(app) - r = await client.get(yarl.URL(urlencoded_path, encoded=True)) - assert r.status == expected_http_resp_status - await r.release() + async with client.get(yarl.URL(urlencoded_path, encoded=True)) as resp: + assert resp.status == expected_http_resp_status async def test_order_is_preserved(aiohttp_client: AiohttpClient) -> None: From b8f1b1272fe04dad41a3807199f8e1331301c7b1 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 31 Aug 2024 20:36:19 +0100 Subject: [PATCH 114/296] Fix router matching pre-encoded URLs (#8898) (#8961) Co-authored-by: J. Nick Koston (cherry picked from commit 6be94520ea46fe1829e6c9d986e7fc9f7db50cad) --- CHANGES/8898.bugfix.rst | 1 + aiohttp/web_urldispatcher.py | 10 +++++----- requirements/base.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test.txt | 2 +- tests/test_urldispatch.py | 29 +++++++++++++++++------------ tests/test_web_urldispatcher.py | 12 ++++-------- 9 files changed, 32 insertions(+), 30 deletions(-) create mode 100644 CHANGES/8898.bugfix.rst diff --git a/CHANGES/8898.bugfix.rst b/CHANGES/8898.bugfix.rst new file mode 100644 index 00000000000..0de6646c8cb --- /dev/null +++ b/CHANGES/8898.bugfix.rst @@ -0,0 +1 @@ +Fixed web router not matching pre-encoded URLs (requires yarl 1.9.6+) -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index aee7aecd2a9..a1df64b8e61 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -381,7 +381,7 @@ def register_route(self, route: "ResourceRoute") -> None: async def resolve(self, request: Request) -> _Resolve: allowed_methods: Set[str] = set() - match_dict = self._match(request.rel_url.raw_path) + match_dict = self._match(request.rel_url.path) if match_dict is None: return None, allowed_methods @@ -650,7 +650,7 @@ def set_options_route(self, handler: Handler) -> None: ) async def resolve(self, request: Request) -> _Resolve: - path = request.rel_url.raw_path + path = request.rel_url.path method = request.method allowed_methods = set(self._routes) if not path.startswith(self._prefix2) and path != self._prefix: @@ -1040,7 +1040,7 @@ async def resolve(self, request: Request) -> UrlMappingMatchInfo: # candidates for a given url part because there are multiple resources # registered for the same canonical path, we resolve them in a linear # fashion to ensure registration order is respected. - url_part = request.rel_url.raw_path + url_part = request.rel_url.path while url_part: for candidate in resource_index.get(url_part, ()): match_dict, allowed = await candidate.resolve(request) @@ -1165,7 +1165,7 @@ def add_resource(self, path: str, *, name: Optional[str] = None) -> Resource: if resource.name == name and resource.raw_match(path): return cast(Resource, resource) if not ("{" in path or "}" in path or ROUTE_RE.search(path)): - resource = PlainResource(_requote_path(path), name=name) + resource = PlainResource(path, name=name) self.register_resource(resource) return resource resource = DynamicResource(path, name=name) @@ -1292,7 +1292,7 @@ def _quote_path(value: str) -> str: def _unquote_path(value: str) -> str: - return URL.build(path=value, encoded=True).path + return URL.build(path=value, encoded=True).path.replace("%2F", "/") def _requote_path(value: str) -> str: diff --git a/requirements/base.txt b/requirements/base.txt index fbdc31772a4..0ab66407cde 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -38,5 +38,5 @@ pycparser==2.21 # via cffi uvloop==0.19.0 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in -yarl==1.9.4 +yarl==1.9.6 # via -r requirements/runtime-deps.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index d638e1c2a46..45a2ce5dea1 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -274,7 +274,7 @@ webcolors==1.11.1 # via blockdiag wheel==0.37.0 # via pip-tools -yarl==1.9.4 +yarl==1.9.6 # via -r requirements/runtime-deps.in zipp==3.17.0 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index a4e341be912..d7b0fbc81c7 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -261,7 +261,7 @@ webcolors==1.13 # via blockdiag wheel==0.41.0 # via pip-tools -yarl==1.9.4 +yarl==1.9.6 # via -r requirements/runtime-deps.in zipp==3.17.0 # via diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index 4c48153d142..3a0b956d3de 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -32,5 +32,5 @@ pycares==4.3.0 # via aiodns pycparser==2.21 # via cffi -yarl==1.9.4 +yarl==1.9.6 # via -r requirements/runtime-deps.in diff --git a/requirements/test.txt b/requirements/test.txt index c25b3b8ea44..daae1ddb6ef 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -125,5 +125,5 @@ uvloop==0.19.0 ; platform_system != "Windows" and implementation_name == "cpytho # via -r requirements/base.in wait-for-it==2.2.2 # via -r requirements/test.in -yarl==1.9.4 +yarl==1.9.6 # via -r requirements/runtime-deps.in diff --git a/tests/test_urldispatch.py b/tests/test_urldispatch.py index 2453ab5a235..f06f73edc21 100644 --- a/tests/test_urldispatch.py +++ b/tests/test_urldispatch.py @@ -457,7 +457,7 @@ def test_add_static_quoting(router) -> None: ) assert router["static"] is resource url = resource.url_for(filename="/1 2/файл%2F.txt") - assert url.path == "/пре /фикс/1 2/файл%2F.txt" + assert url.path == "/пре %2Fфикс/1 2/файл%2F.txt" assert str(url) == ( "/%D0%BF%D1%80%D0%B5%20%2F%D1%84%D0%B8%D0%BA%D1%81" "/1%202/%D1%84%D0%B0%D0%B9%D0%BB%252F.txt" @@ -530,19 +530,24 @@ def test_static_remove_trailing_slash(router) -> None: assert "/prefix" == route._prefix -async def test_add_route_with_re(router) -> None: +@pytest.mark.parametrize( + "pattern,url,expected", + ( + (r"{to:\d+}", r"1234", {"to": "1234"}), + ("{name}.html", "test.html", {"name": "test"}), + (r"{fn:\w+ \d+}", "abc 123", {"fn": "abc 123"}), + (r"{fn:\w+\s\d+}", "abc 123", {"fn": "abc 123"}), + ), +) +async def test_add_route_with_re( + router: web.UrlDispatcher, pattern: str, url: str, expected +) -> None: handler = make_handler() - router.add_route("GET", r"/handler/{to:\d+}", handler) - - req = make_mocked_request("GET", "/handler/1234") + router.add_route("GET", f"/handler/{pattern}", handler) + req = make_mocked_request("GET", f"/handler/{url}") info = await router.resolve(req) assert info is not None - assert {"to": "1234"} == info - - router.add_route("GET", r"/handler/{name}.html", handler) - req = make_mocked_request("GET", "/handler/test.html") - info = await router.resolve(req) - assert {"name": "test"} == info + assert info == expected async def test_add_route_with_re_and_slashes(router) -> None: @@ -625,7 +630,7 @@ def test_route_dynamic_quoting(router) -> None: route = router.add_route("GET", r"/пре %2Fфикс/{arg}", handler) url = route.url_for(arg="1 2/текст%2F") - assert url.path == "/пре /фикс/1 2/текст%2F" + assert url.path == "/пре %2Fфикс/1 2/текст%2F" assert str(url) == ( "/%D0%BF%D1%80%D0%B5%20%2F%D1%84%D0%B8%D0%BA%D1%81" "/1%202/%D1%82%D0%B5%D0%BA%D1%81%D1%82%252F" diff --git a/tests/test_web_urldispatcher.py b/tests/test_web_urldispatcher.py index 3a45b9355f5..8a97acf504d 100644 --- a/tests/test_web_urldispatcher.py +++ b/tests/test_web_urldispatcher.py @@ -856,18 +856,15 @@ async def get_foobar(request: web.Request) -> web.Response: assert (await resp.text()) == "success!" -@pytest.mark.xfail( - raises=AssertionError, - reason="Regression in v3.7: https://github.com/aio-libs/aiohttp/issues/5621", -) @pytest.mark.parametrize( ("route_definition", "urlencoded_path", "expected_http_resp_status"), ( ("/467,802,24834/hello", "/467%2C802%2C24834/hello", 200), ("/{user_ids:([0-9]+)(,([0-9]+))*}/hello", "/467%2C802%2C24834/hello", 200), + ("/467,802,24834/hello", "/467,802,24834/hello", 200), + ("/{user_ids:([0-9]+)(,([0-9]+))*}/hello", "/467,802,24834/hello", 200), ("/1%2C3/hello", "/1%2C3/hello", 404), ), - ids=("urldecoded_route", "urldecoded_route_with_regex", "urlencoded_route"), ) async def test_decoded_url_match( aiohttp_client: AiohttpClient, @@ -883,9 +880,8 @@ async def handler(request: web.Request) -> web.Response: app.router.add_get(route_definition, handler) client = await aiohttp_client(app) - r = await client.get(yarl.URL(urlencoded_path, encoded=True)) - assert r.status == expected_http_resp_status - await r.release() + async with client.get(yarl.URL(urlencoded_path, encoded=True)) as resp: + assert resp.status == expected_http_resp_status async def test_order_is_preserved(aiohttp_client: AiohttpClient) -> None: From 4614dea10aafd1c778fd5239144b4838fd6345e3 Mon Sep 17 00:00:00 2001 From: Hadock <49624805+Hadock-is-ok@users.noreply.github.com> Date: Sat, 31 Aug 2024 21:08:59 +0100 Subject: [PATCH 115/296] =?UTF-8?q?Add=20aiohttp.tracing.TraceRequestHeade?= =?UTF-8?q?rsSentParams=20into=20the=20top=20level=20=E2=80=A6=20(3.11)=20?= =?UTF-8?q?(#8963)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …file. (#8947) (cherry picked from commit e2437a48ec31f830100606a62bb822e44bab1114) --- CHANGES/8947.misc.rst | 1 + aiohttp/__init__.py | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 CHANGES/8947.misc.rst diff --git a/CHANGES/8947.misc.rst b/CHANGES/8947.misc.rst new file mode 100644 index 00000000000..277ba915c50 --- /dev/null +++ b/CHANGES/8947.misc.rst @@ -0,0 +1 @@ +Exported ``aiohttp.TraceRequestHeadersSentParams`` -- by :user:`Hadock-is-ok`. diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py index 79f40d6f8f3..043a83eef68 100644 --- a/aiohttp/__init__.py +++ b/aiohttp/__init__.py @@ -106,6 +106,7 @@ TraceRequestChunkSentParams as TraceRequestChunkSentParams, TraceRequestEndParams as TraceRequestEndParams, TraceRequestExceptionParams as TraceRequestExceptionParams, + TraceRequestHeadersSentParams as TraceRequestHeadersSentParams, TraceRequestRedirectParams as TraceRequestRedirectParams, TraceRequestStartParams as TraceRequestStartParams, TraceResponseChunkReceivedParams as TraceResponseChunkReceivedParams, @@ -224,6 +225,7 @@ "TraceRequestChunkSentParams", "TraceRequestEndParams", "TraceRequestExceptionParams", + "TraceRequestHeadersSentParams", "TraceRequestRedirectParams", "TraceRequestStartParams", "TraceResponseChunkReceivedParams", From 6881e0de763de8220a02ea9c504a2241ce26e42c Mon Sep 17 00:00:00 2001 From: Hadock <49624805+Hadock-is-ok@users.noreply.github.com> Date: Sat, 31 Aug 2024 21:09:34 +0100 Subject: [PATCH 116/296] =?UTF-8?q?Add=20aiohttp.tracing.TraceRequestHeade?= =?UTF-8?q?rsSentParams=20into=20the=20top=20level=20=E2=80=A6=20(3.10)=20?= =?UTF-8?q?(#8964)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …file. (#8947) (cherry picked from commit e2437a48ec31f830100606a62bb822e44bab1114) --- CHANGES/8947.misc.rst | 1 + aiohttp/__init__.py | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 CHANGES/8947.misc.rst diff --git a/CHANGES/8947.misc.rst b/CHANGES/8947.misc.rst new file mode 100644 index 00000000000..277ba915c50 --- /dev/null +++ b/CHANGES/8947.misc.rst @@ -0,0 +1 @@ +Exported ``aiohttp.TraceRequestHeadersSentParams`` -- by :user:`Hadock-is-ok`. diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py index bcc73f51ccd..f321cdaba45 100644 --- a/aiohttp/__init__.py +++ b/aiohttp/__init__.py @@ -106,6 +106,7 @@ TraceRequestChunkSentParams as TraceRequestChunkSentParams, TraceRequestEndParams as TraceRequestEndParams, TraceRequestExceptionParams as TraceRequestExceptionParams, + TraceRequestHeadersSentParams as TraceRequestHeadersSentParams, TraceRequestRedirectParams as TraceRequestRedirectParams, TraceRequestStartParams as TraceRequestStartParams, TraceResponseChunkReceivedParams as TraceResponseChunkReceivedParams, @@ -224,6 +225,7 @@ "TraceRequestChunkSentParams", "TraceRequestEndParams", "TraceRequestExceptionParams", + "TraceRequestHeadersSentParams", "TraceRequestRedirectParams", "TraceRequestStartParams", "TraceResponseChunkReceivedParams", From b73a4c170c64e2691331542d30133df3d54032c0 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 1 Sep 2024 02:24:32 +0100 Subject: [PATCH 117/296] Deprecate obsolete timeout in ClientSession.ws_connect() (#3946) (#8965) (cherry picked from commit c09c538e036619ae549280fb9051fd6084e8252c) --------- Co-authored-by: Artem Yushkovskiy --- CHANGES/3945.deprecation.rst | 1 + CHANGES/8612.feature.rst | 1 + aiohttp/__init__.py | 2 + aiohttp/client.py | 46 +++++++++++++++---- aiohttp/client_ws.py | 19 +++++--- docs/client_reference.rst | 34 ++++++++++---- tests/test_client_ws.py | 7 +-- tests/test_client_ws_functional.py | 71 +++++++++++++++++++++++++++--- 8 files changed, 150 insertions(+), 31 deletions(-) create mode 100644 CHANGES/3945.deprecation.rst create mode 100644 CHANGES/8612.feature.rst diff --git a/CHANGES/3945.deprecation.rst b/CHANGES/3945.deprecation.rst new file mode 100644 index 00000000000..07f8566881a --- /dev/null +++ b/CHANGES/3945.deprecation.rst @@ -0,0 +1 @@ +Deprecate obsolete `timeout: float` and `receive_timeout: Optional[float]` in `ClientSession.ws_connect()`. Change default websocket receive timeout from `None` to `10.0`. diff --git a/CHANGES/8612.feature.rst b/CHANGES/8612.feature.rst new file mode 100644 index 00000000000..96adcf6dc4c --- /dev/null +++ b/CHANGES/8612.feature.rst @@ -0,0 +1 @@ +Exported ``ClientWSTimeout`` to top-level namespace -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py index 043a83eef68..15602a7dc85 100644 --- a/aiohttp/__init__.py +++ b/aiohttp/__init__.py @@ -21,6 +21,7 @@ ClientSSLError, ClientTimeout, ClientWebSocketResponse, + ClientWSTimeout, ConnectionTimeoutError, ContentTypeError, Fingerprint, @@ -139,6 +140,7 @@ "ClientSession", "ClientTimeout", "ClientWebSocketResponse", + "ClientWSTimeout", "ConnectionTimeoutError", "ContentTypeError", "Fingerprint", diff --git a/aiohttp/client.py b/aiohttp/client.py index 8edd14d01ff..f9e3a5c5f65 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -73,7 +73,11 @@ RequestInfo as RequestInfo, _merge_ssl_params, ) -from .client_ws import ClientWebSocketResponse as ClientWebSocketResponse +from .client_ws import ( + DEFAULT_WS_CLIENT_TIMEOUT, + ClientWebSocketResponse as ClientWebSocketResponse, + ClientWSTimeout as ClientWSTimeout, +) from .connector import ( HTTP_AND_EMPTY_SCHEMA_SET, BaseConnector as BaseConnector, @@ -142,6 +146,7 @@ # client "ClientSession", "ClientTimeout", + "ClientWSTimeout", "request", ) @@ -820,7 +825,7 @@ def ws_connect( *, method: str = hdrs.METH_GET, protocols: Iterable[str] = (), - timeout: float = 10.0, + timeout: Union[ClientWSTimeout, _SENTINEL] = sentinel, receive_timeout: Optional[float] = None, autoclose: bool = True, autoping: bool = True, @@ -872,7 +877,7 @@ async def _ws_connect( *, method: str = hdrs.METH_GET, protocols: Iterable[str] = (), - timeout: float = 10.0, + timeout: Union[ClientWSTimeout, _SENTINEL] = sentinel, receive_timeout: Optional[float] = None, autoclose: bool = True, autoping: bool = True, @@ -891,6 +896,29 @@ async def _ws_connect( compress: int = 0, max_msg_size: int = 4 * 1024 * 1024, ) -> ClientWebSocketResponse: + if timeout is not sentinel: + if isinstance(timeout, ClientWSTimeout): + ws_timeout = timeout + else: + warnings.warn( + "parameter 'timeout' of type 'float' " + "is deprecated, please use " + "'timeout=ClientWSTimeout(ws_close=...)'", + DeprecationWarning, + stacklevel=2, + ) + ws_timeout = ClientWSTimeout(ws_close=timeout) + else: + ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT + if receive_timeout is not None: + warnings.warn( + "float parameter 'receive_timeout' " + "is deprecated, please use parameter " + "'timeout=ClientWSTimeout(ws_receive=...)'", + DeprecationWarning, + stacklevel=2, + ) + ws_timeout = attr.evolve(ws_timeout, ws_receive=receive_timeout) if headers is None: real_headers: CIMultiDict[str] = CIMultiDict() @@ -1021,12 +1049,13 @@ async def _ws_connect( # For WS connection the read_timeout must be either receive_timeout or greater # None == no timeout, i.e. infinite timeout, so None is the max timeout possible - if receive_timeout is None: + if ws_timeout.ws_receive is None: # Reset regardless - conn_proto.read_timeout = receive_timeout + conn_proto.read_timeout = None elif conn_proto.read_timeout is not None: - # If read_timeout was set check which wins - conn_proto.read_timeout = max(receive_timeout, conn_proto.read_timeout) + conn_proto.read_timeout = max( + ws_timeout.ws_receive, conn_proto.read_timeout + ) transport = conn.transport assert transport is not None @@ -1050,11 +1079,10 @@ async def _ws_connect( writer, protocol, resp, - timeout, + ws_timeout, autoclose, autoping, self._loop, - receive_timeout=receive_timeout, heartbeat=heartbeat, compress=compress, client_notakeover=notakeover, diff --git a/aiohttp/client_ws.py b/aiohttp/client_ws.py index 7b3a5bf952d..6246234b8e0 100644 --- a/aiohttp/client_ws.py +++ b/aiohttp/client_ws.py @@ -4,6 +4,8 @@ import sys from typing import Any, Optional, cast +import attr + from .client_exceptions import ClientError, ServerTimeoutError from .client_reqrep import ClientResponse from .helpers import calculate_timeout_when, set_result @@ -30,6 +32,15 @@ import async_timeout +@attr.s(frozen=True, slots=True) +class ClientWSTimeout: + ws_receive = attr.ib(type=Optional[float], default=None) + ws_close = attr.ib(type=Optional[float], default=None) + + +DEFAULT_WS_CLIENT_TIMEOUT = ClientWSTimeout(ws_receive=None, ws_close=10.0) + + class ClientWebSocketResponse: def __init__( self, @@ -37,12 +48,11 @@ def __init__( writer: WebSocketWriter, protocol: Optional[str], response: ClientResponse, - timeout: float, + timeout: ClientWSTimeout, autoclose: bool, autoping: bool, loop: asyncio.AbstractEventLoop, *, - receive_timeout: Optional[float] = None, heartbeat: Optional[float] = None, compress: int = 0, client_notakeover: bool = False, @@ -57,7 +67,6 @@ def __init__( self._closing = False self._close_code: Optional[int] = None self._timeout = timeout - self._receive_timeout = receive_timeout self._autoclose = autoclose self._autoping = autoping self._heartbeat = heartbeat @@ -268,7 +277,7 @@ async def close(self, *, code: int = WSCloseCode.OK, message: bytes = b"") -> bo while True: try: - async with async_timeout.timeout(self._timeout): + async with async_timeout.timeout(self._timeout.ws_close): msg = await self._reader.read() except asyncio.CancelledError: self._close_code = WSCloseCode.ABNORMAL_CLOSURE @@ -288,7 +297,7 @@ async def close(self, *, code: int = WSCloseCode.OK, message: bytes = b"") -> bo return False async def receive(self, timeout: Optional[float] = None) -> WSMessage: - receive_timeout = timeout or self._receive_timeout + receive_timeout = timeout or self._timeout.ws_receive while True: if self._waiting: diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 4e63552cd5c..f64df336755 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -690,8 +690,8 @@ The client session supports the context manager protocol for self closing. ` object. .. method:: ws_connect(url, *, method='GET', \ - protocols=(), timeout=10.0,\ - receive_timeout=None,\ + protocols=(), \ + timeout=sentinel,\ auth=None,\ autoclose=True,\ autoping=True,\ @@ -714,12 +714,11 @@ The client session supports the context manager protocol for self closing. :param tuple protocols: Websocket protocols - :param float timeout: Timeout for websocket to close. ``10`` seconds - by default - - :param float receive_timeout: Timeout for websocket to receive - complete message. ``None`` (unlimited) - seconds by default + :param timeout: a :class:`ClientWSTimeout` timeout for websocket. + By default, the value + `ClientWSTimeout(ws_receive=None, ws_close=10.0)` is used + (``10.0`` seconds for the websocket to close). + ``None`` means no timeout will be used. :param aiohttp.BasicAuth auth: an object that represents HTTP Basic Authorization (optional) @@ -1760,7 +1759,24 @@ Utilities :class:`float`, ``None`` by default. - .. versionadded:: 3.3 + +.. class:: ClientWSTimeout(*, ws_receive=None, ws_close=None) + + A data class for websocket client timeout settings. + + .. attribute:: ws_receive + + A timeout for websocket to receive a complete message. + + :class:`float`, ``None`` by default. + + .. attribute:: ws_close + + A timeout for the websocket to close. + + :class:`float`, ``10.0`` by default. + + .. versionadded:: 4.0 .. note:: diff --git a/tests/test_client_ws.py b/tests/test_client_ws.py index a790fba43ec..31ec7576c97 100644 --- a/tests/test_client_ws.py +++ b/tests/test_client_ws.py @@ -91,7 +91,7 @@ async def test_ws_connect_read_timeout_stays_inf( res = await aiohttp.ClientSession().ws_connect( "http://test.org", protocols=("t1", "t2", "chat"), - receive_timeout=0.5, + timeout=aiohttp.ClientWSTimeout(0.5), ) assert isinstance(res, client.ClientWebSocketResponse) @@ -122,7 +122,7 @@ async def test_ws_connect_read_timeout_reset_to_max( res = await aiohttp.ClientSession().ws_connect( "http://test.org", protocols=("t1", "t2", "chat"), - receive_timeout=1.0, + timeout=aiohttp.ClientWSTimeout(1.0), ) assert isinstance(res, client.ClientWebSocketResponse) @@ -600,8 +600,9 @@ async def test_reader_read_exception(ws_key, key_data, loop) -> None: async def test_receive_runtime_err(loop) -> None: + timeout = aiohttp.ClientWSTimeout(ws_receive=10.0) resp = client.ClientWebSocketResponse( - mock.Mock(), mock.Mock(), mock.Mock(), mock.Mock(), 10.0, True, True, loop + mock.Mock(), mock.Mock(), mock.Mock(), mock.Mock(), timeout, True, True, loop ) resp._waiting = True diff --git a/tests/test_client_ws_functional.py b/tests/test_client_ws_functional.py index 274092a189a..30da0dca802 100644 --- a/tests/test_client_ws_functional.py +++ b/tests/test_client_ws_functional.py @@ -7,6 +7,7 @@ import aiohttp from aiohttp import ServerTimeoutError, WSMsgType, hdrs, web +from aiohttp.client_ws import ClientWSTimeout from aiohttp.http import WSCloseCode from aiohttp.pytest_plugin import AiohttpClient @@ -394,7 +395,7 @@ async def handler(request): assert resp.closed -async def test_close_timeout(aiohttp_client) -> None: +async def test_close_timeout_sock_close_read(aiohttp_client) -> None: async def handler(request): ws = web.WebSocketResponse() await ws.prepare(request) @@ -406,7 +407,39 @@ async def handler(request): app = web.Application() app.router.add_route("GET", "/", handler) client = await aiohttp_client(app) - resp = await client.ws_connect("/", timeout=0.2, autoclose=False) + timeout = ClientWSTimeout(ws_close=0.2) + resp = await client.ws_connect("/", timeout=timeout, autoclose=False) + + await resp.send_bytes(b"ask") + + msg = await resp.receive() + assert msg.data == "test" + assert msg.type == aiohttp.WSMsgType.TEXT + + msg = await resp.close() + assert resp.closed + assert isinstance(resp.exception(), asyncio.TimeoutError) + + +async def test_close_timeout_deprecated(aiohttp_client) -> None: + async def handler(request): + ws = web.WebSocketResponse() + await ws.prepare(request) + await ws.receive_bytes() + await ws.send_str("test") + await asyncio.sleep(1) + return ws + + app = web.Application() + app.router.add_route("GET", "/", handler) + client = await aiohttp_client(app) + with pytest.warns( + DeprecationWarning, + match="parameter 'timeout' of type 'float' " + "is deprecated, please use " + r"'timeout=ClientWSTimeout\(ws_close=...\)'", + ): + resp = await client.ws_connect("/", timeout=0.2, autoclose=False) await resp.send_bytes(b"ask") @@ -535,7 +568,7 @@ async def handler(request): await resp.close() -async def test_receive_timeout(aiohttp_client) -> None: +async def test_receive_timeout_sock_read(aiohttp_client) -> None: async def handler(request): ws = web.WebSocketResponse() await ws.prepare(request) @@ -547,10 +580,38 @@ async def handler(request): app.router.add_route("GET", "/", handler) client = await aiohttp_client(app) - resp = await client.ws_connect("/", receive_timeout=0.1) + receive_timeout = ClientWSTimeout(ws_receive=0.1) + resp = await client.ws_connect("/", timeout=receive_timeout) with pytest.raises(asyncio.TimeoutError): - await resp.receive(0.05) + await resp.receive(timeout=0.05) + + await resp.close() + + +async def test_receive_timeout_deprecation(aiohttp_client) -> None: + + async def handler(request): + ws = web.WebSocketResponse() + await ws.prepare(request) + await ws.receive() + await ws.close() + return ws + + app = web.Application() + app.router.add_route("GET", "/", handler) + + client = await aiohttp_client(app) + with pytest.warns( + DeprecationWarning, + match="float parameter 'receive_timeout' " + "is deprecated, please use parameter " + r"'timeout=ClientWSTimeout\(ws_receive=...\)'", + ): + resp = await client.ws_connect("/", receive_timeout=0.1) + + with pytest.raises(asyncio.TimeoutError): + await resp.receive(timeout=0.05) await resp.close() From 9503cf7bc07bf23d9b53621f696c6e6c6867d25e Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:00:44 +0100 Subject: [PATCH 118/296] [PR #7168/8a525d98 backport][3.11] Fix `test_utils.make_mocked_request` behaviour for empty payload (#8970) **This is a backport of PR #7168 as merged into master (8a525d98c512e624311b16e9891cac9050cc8b8c).** Co-authored-by: Rahul Nahata --- CHANGES/7167.bugfix.rst | 1 + CONTRIBUTORS.txt | 1 + aiohttp/test_utils.py | 6 ++---- tests/test_test_utils.py | 5 +++++ 4 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 CHANGES/7167.bugfix.rst diff --git a/CHANGES/7167.bugfix.rst b/CHANGES/7167.bugfix.rst new file mode 100644 index 00000000000..766f1438b66 --- /dev/null +++ b/CHANGES/7167.bugfix.rst @@ -0,0 +1 @@ +Changed ``make_mocked_request()`` to use empty payload by default -- by :user:`rahulnht`. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 8f387459948..e7214dfedd4 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -274,6 +274,7 @@ Philipp A. Pieter van Beek Qiao Han Rafael Viotti +Rahul Nahata Raphael Bialon Raúl Cumplido Required Field diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py index 97c1469dd2a..328561fb6a7 100644 --- a/aiohttp/test_utils.py +++ b/aiohttp/test_utils.py @@ -31,6 +31,7 @@ from .client_ws import ClientWebSocketResponse from .helpers import sentinel from .http import HttpVersion, RawRequestMessage +from .streams import EMPTY_PAYLOAD, StreamReader from .typedefs import StrOrURL from .web import ( Application, @@ -631,7 +632,7 @@ def make_mocked_request( writer: Any = sentinel, protocol: Any = sentinel, transport: Any = sentinel, - payload: Any = sentinel, + payload: StreamReader = EMPTY_PAYLOAD, sslcontext: Optional[SSLContext] = None, client_max_size: int = 1024**2, loop: Any = ..., @@ -700,9 +701,6 @@ def make_mocked_request( protocol.transport = transport protocol.writer = writer - if payload is sentinel: - payload = mock.Mock() - req = Request( message, payload, protocol, writer, task, loop, client_max_size=client_max_size ) diff --git a/tests/test_test_utils.py b/tests/test_test_utils.py index 328f83c3fd4..77349246616 100644 --- a/tests/test_test_utils.py +++ b/tests/test_test_utils.py @@ -239,6 +239,11 @@ def test_make_mocked_request_content() -> None: assert req.content is payload +async def test_make_mocked_request_empty_payload() -> None: + req = make_mocked_request("GET", "/") + assert await req.read() == b"" + + def test_make_mocked_request_transport() -> None: transport = mock.Mock() req = make_mocked_request("GET", "/", transport=transport) From d6d0a22cc6c33df97dcb1b6165d53bf529107124 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:01:05 +0100 Subject: [PATCH 119/296] [PR #7168/8a525d98 backport][3.10] Fix `test_utils.make_mocked_request` behaviour for empty payload (#8969) **This is a backport of PR #7168 as merged into master (8a525d98c512e624311b16e9891cac9050cc8b8c).** Co-authored-by: Rahul Nahata --- CHANGES/7167.bugfix.rst | 1 + CONTRIBUTORS.txt | 1 + aiohttp/test_utils.py | 6 ++---- tests/test_test_utils.py | 5 +++++ 4 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 CHANGES/7167.bugfix.rst diff --git a/CHANGES/7167.bugfix.rst b/CHANGES/7167.bugfix.rst new file mode 100644 index 00000000000..766f1438b66 --- /dev/null +++ b/CHANGES/7167.bugfix.rst @@ -0,0 +1 @@ +Changed ``make_mocked_request()`` to use empty payload by default -- by :user:`rahulnht`. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 8f387459948..e7214dfedd4 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -274,6 +274,7 @@ Philipp A. Pieter van Beek Qiao Han Rafael Viotti +Rahul Nahata Raphael Bialon Raúl Cumplido Required Field diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py index 97c1469dd2a..328561fb6a7 100644 --- a/aiohttp/test_utils.py +++ b/aiohttp/test_utils.py @@ -31,6 +31,7 @@ from .client_ws import ClientWebSocketResponse from .helpers import sentinel from .http import HttpVersion, RawRequestMessage +from .streams import EMPTY_PAYLOAD, StreamReader from .typedefs import StrOrURL from .web import ( Application, @@ -631,7 +632,7 @@ def make_mocked_request( writer: Any = sentinel, protocol: Any = sentinel, transport: Any = sentinel, - payload: Any = sentinel, + payload: StreamReader = EMPTY_PAYLOAD, sslcontext: Optional[SSLContext] = None, client_max_size: int = 1024**2, loop: Any = ..., @@ -700,9 +701,6 @@ def make_mocked_request( protocol.transport = transport protocol.writer = writer - if payload is sentinel: - payload = mock.Mock() - req = Request( message, payload, protocol, writer, task, loop, client_max_size=client_max_size ) diff --git a/tests/test_test_utils.py b/tests/test_test_utils.py index 328f83c3fd4..77349246616 100644 --- a/tests/test_test_utils.py +++ b/tests/test_test_utils.py @@ -239,6 +239,11 @@ def test_make_mocked_request_content() -> None: assert req.content is payload +async def test_make_mocked_request_empty_payload() -> None: + req = make_mocked_request("GET", "/") + assert await req.read() == b"" + + def test_make_mocked_request_transport() -> None: transport = mock.Mock() req = make_mocked_request("GET", "/", transport=transport) From 0fdcc291d6f4a35352d8feba39a9acf5f822c1a9 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 1 Sep 2024 20:59:15 +0100 Subject: [PATCH 120/296] Fix CancelledError stopping other cleanup contexts completing (#8908) (#8972) (cherry picked from commit 032fb571f2c73605d855d4f17026b303c7441823) --- CHANGES/8908.bugfix.rst | 1 + aiohttp/web_app.py | 2 +- docs/web_advanced.rst | 14 ++++++-------- tests/test_web_app.py | 9 ++++++--- 4 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 CHANGES/8908.bugfix.rst diff --git a/CHANGES/8908.bugfix.rst b/CHANGES/8908.bugfix.rst new file mode 100644 index 00000000000..0eb450431db --- /dev/null +++ b/CHANGES/8908.bugfix.rst @@ -0,0 +1 @@ +Fixed ``CancelledError`` from one cleanup context stopping other contexts from completing -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index 3b4b6489e60..3510bffda60 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -579,7 +579,7 @@ async def _on_cleanup(self, app: Application) -> None: await it.__anext__() except StopAsyncIteration: pass - except Exception as exc: + except (Exception, asyncio.CancelledError) as exc: errors.append(exc) else: errors.append(RuntimeError(f"{it!r} has more than one 'yield'")) diff --git a/docs/web_advanced.rst b/docs/web_advanced.rst index dc94bea33bf..070bae34f10 100644 --- a/docs/web_advanced.rst +++ b/docs/web_advanced.rst @@ -1064,13 +1064,10 @@ below:: async with client.pubsub() as pubsub: await pubsub.subscribe(channel) while True: - try: - msg = await pubsub.get_message(ignore_subscribe_messages=True) - if msg is not None: - for ws in app["websockets"]: - await ws.send_str("{}: {}".format(channel, msg)) - except asyncio.CancelledError: - break + msg = await pubsub.get_message(ignore_subscribe_messages=True) + if msg is not None: + for ws in app["websockets"]: + await ws.send_str("{}: {}".format(channel, msg)) async def background_tasks(app): @@ -1079,7 +1076,8 @@ below:: yield app[redis_listener].cancel() - await app[redis_listener] + with contextlib.suppress(asyncio.CancelledError): + await app[redis_listener] app = web.Application() diff --git a/tests/test_web_app.py b/tests/test_web_app.py index 3d3aa2479f6..6a86a3458a3 100644 --- a/tests/test_web_app.py +++ b/tests/test_web_app.py @@ -1,6 +1,6 @@ import asyncio import gc -from typing import AsyncIterator, Callable, Iterator, NoReturn +from typing import AsyncIterator, Callable, Iterator, NoReturn, Type from unittest import mock import pytest @@ -476,7 +476,10 @@ async def fail_ctx(app: web.Application) -> AsyncIterator[NoReturn]: assert ctx_state == "CLEAN" -async def test_cleanup_ctx_exception_on_cleanup_multiple() -> None: +@pytest.mark.parametrize("exc_cls", (Exception, asyncio.CancelledError)) +async def test_cleanup_ctx_exception_on_cleanup_multiple( + exc_cls: Type[BaseException], +) -> None: app = web.Application() out = [] @@ -488,7 +491,7 @@ async def inner(app: web.Application) -> AsyncIterator[None]: yield None out.append("post_" + str(num)) if fail: - raise Exception("fail_" + str(num)) + raise exc_cls("fail_" + str(num)) return inner From 87342c791dfd4c916877ba3dffafb9345bb0491f Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 1 Sep 2024 20:59:35 +0100 Subject: [PATCH 121/296] Fix CancelledError stopping other cleanup contexts completing (#8908) (#8973) (cherry picked from commit 032fb571f2c73605d855d4f17026b303c7441823) --- CHANGES/8908.bugfix.rst | 1 + aiohttp/web_app.py | 2 +- docs/web_advanced.rst | 14 ++++++-------- tests/test_web_app.py | 9 ++++++--- 4 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 CHANGES/8908.bugfix.rst diff --git a/CHANGES/8908.bugfix.rst b/CHANGES/8908.bugfix.rst new file mode 100644 index 00000000000..0eb450431db --- /dev/null +++ b/CHANGES/8908.bugfix.rst @@ -0,0 +1 @@ +Fixed ``CancelledError`` from one cleanup context stopping other contexts from completing -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index 3b4b6489e60..3510bffda60 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -579,7 +579,7 @@ async def _on_cleanup(self, app: Application) -> None: await it.__anext__() except StopAsyncIteration: pass - except Exception as exc: + except (Exception, asyncio.CancelledError) as exc: errors.append(exc) else: errors.append(RuntimeError(f"{it!r} has more than one 'yield'")) diff --git a/docs/web_advanced.rst b/docs/web_advanced.rst index dc94bea33bf..070bae34f10 100644 --- a/docs/web_advanced.rst +++ b/docs/web_advanced.rst @@ -1064,13 +1064,10 @@ below:: async with client.pubsub() as pubsub: await pubsub.subscribe(channel) while True: - try: - msg = await pubsub.get_message(ignore_subscribe_messages=True) - if msg is not None: - for ws in app["websockets"]: - await ws.send_str("{}: {}".format(channel, msg)) - except asyncio.CancelledError: - break + msg = await pubsub.get_message(ignore_subscribe_messages=True) + if msg is not None: + for ws in app["websockets"]: + await ws.send_str("{}: {}".format(channel, msg)) async def background_tasks(app): @@ -1079,7 +1076,8 @@ below:: yield app[redis_listener].cancel() - await app[redis_listener] + with contextlib.suppress(asyncio.CancelledError): + await app[redis_listener] app = web.Application() diff --git a/tests/test_web_app.py b/tests/test_web_app.py index 3d3aa2479f6..6a86a3458a3 100644 --- a/tests/test_web_app.py +++ b/tests/test_web_app.py @@ -1,6 +1,6 @@ import asyncio import gc -from typing import AsyncIterator, Callable, Iterator, NoReturn +from typing import AsyncIterator, Callable, Iterator, NoReturn, Type from unittest import mock import pytest @@ -476,7 +476,10 @@ async def fail_ctx(app: web.Application) -> AsyncIterator[NoReturn]: assert ctx_state == "CLEAN" -async def test_cleanup_ctx_exception_on_cleanup_multiple() -> None: +@pytest.mark.parametrize("exc_cls", (Exception, asyncio.CancelledError)) +async def test_cleanup_ctx_exception_on_cleanup_multiple( + exc_cls: Type[BaseException], +) -> None: app = web.Application() out = [] @@ -488,7 +491,7 @@ async def inner(app: web.Application) -> AsyncIterator[None]: yield None out.append("post_" + str(num)) if fail: - raise Exception("fail_" + str(num)) + raise exc_cls("fail_" + str(num)) return inner From 306e42d8de2a9f4c2a911eab5fce6da36f949f11 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 1 Sep 2024 21:20:08 +0100 Subject: [PATCH 122/296] Avoid logging exception from run_app() that is also raised (#8951) (#8974) (cherry picked from commit 45d6e4f14572bfc4cfc3f32a2c7c72a9cc28f125) --- CHANGES/6807.bugfix.rst | 1 + aiohttp/web.py | 13 +++++++++---- tests/test_run_app.py | 19 ++++++++++++++++++- 3 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 CHANGES/6807.bugfix.rst diff --git a/CHANGES/6807.bugfix.rst b/CHANGES/6807.bugfix.rst new file mode 100644 index 00000000000..4eb07b9e0da --- /dev/null +++ b/CHANGES/6807.bugfix.rst @@ -0,0 +1 @@ +Stopped logging exceptions from ``web.run_app()`` that would be raised regardless -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web.py b/aiohttp/web.py index 8708f1fcbec..88bf14bf828 100644 --- a/aiohttp/web.py +++ b/aiohttp/web.py @@ -6,6 +6,7 @@ import warnings from argparse import ArgumentParser from collections.abc import Iterable +from contextlib import suppress from importlib import import_module from typing import ( Any, @@ -519,10 +520,14 @@ def run_app( except (GracefulExit, KeyboardInterrupt): # pragma: no cover pass finally: - _cancel_tasks({main_task}, loop) - _cancel_tasks(asyncio.all_tasks(loop), loop) - loop.run_until_complete(loop.shutdown_asyncgens()) - loop.close() + try: + main_task.cancel() + with suppress(asyncio.CancelledError): + loop.run_until_complete(main_task) + finally: + _cancel_tasks(asyncio.all_tasks(loop), loop) + loop.run_until_complete(loop.shutdown_asyncgens()) + loop.close() def main(argv: List[str]) -> None: diff --git a/tests/test_run_app.py b/tests/test_run_app.py index c1d5f8e14f4..74d8c79bf55 100644 --- a/tests/test_run_app.py +++ b/tests/test_run_app.py @@ -9,7 +9,7 @@ import subprocess import sys import time -from typing import Callable, NoReturn, Set +from typing import AsyncIterator, Callable, NoReturn, Set from unittest import mock from uuid import uuid4 @@ -906,6 +906,23 @@ async def init(): assert count == 3 +def test_run_app_raises_exception(patched_loop: asyncio.AbstractEventLoop) -> None: + async def context(app: web.Application) -> AsyncIterator[None]: + raise RuntimeError("foo") + yield # pragma: no cover + + app = web.Application() + app.cleanup_ctx.append(context) + + with mock.patch.object( + patched_loop, "call_exception_handler", autospec=True, spec_set=True + ) as m: + with pytest.raises(RuntimeError, match="foo"): + web.run_app(app, loop=patched_loop) + + assert not m.called + + class TestShutdown: def raiser(self) -> NoReturn: raise KeyboardInterrupt From 1c1c0ea353041c8814a6131c3a92978dc2373e52 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 1 Sep 2024 21:37:29 +0100 Subject: [PATCH 123/296] Avoid logging exception from run_app() that is also raised (#8951) (#8975) (cherry picked from commit 45d6e4f14572bfc4cfc3f32a2c7c72a9cc28f125) --- CHANGES/6807.bugfix.rst | 1 + aiohttp/web.py | 13 +++++++++---- tests/test_run_app.py | 19 ++++++++++++++++++- 3 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 CHANGES/6807.bugfix.rst diff --git a/CHANGES/6807.bugfix.rst b/CHANGES/6807.bugfix.rst new file mode 100644 index 00000000000..4eb07b9e0da --- /dev/null +++ b/CHANGES/6807.bugfix.rst @@ -0,0 +1 @@ +Stopped logging exceptions from ``web.run_app()`` that would be raised regardless -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web.py b/aiohttp/web.py index 8708f1fcbec..88bf14bf828 100644 --- a/aiohttp/web.py +++ b/aiohttp/web.py @@ -6,6 +6,7 @@ import warnings from argparse import ArgumentParser from collections.abc import Iterable +from contextlib import suppress from importlib import import_module from typing import ( Any, @@ -519,10 +520,14 @@ def run_app( except (GracefulExit, KeyboardInterrupt): # pragma: no cover pass finally: - _cancel_tasks({main_task}, loop) - _cancel_tasks(asyncio.all_tasks(loop), loop) - loop.run_until_complete(loop.shutdown_asyncgens()) - loop.close() + try: + main_task.cancel() + with suppress(asyncio.CancelledError): + loop.run_until_complete(main_task) + finally: + _cancel_tasks(asyncio.all_tasks(loop), loop) + loop.run_until_complete(loop.shutdown_asyncgens()) + loop.close() def main(argv: List[str]) -> None: diff --git a/tests/test_run_app.py b/tests/test_run_app.py index c1d5f8e14f4..74d8c79bf55 100644 --- a/tests/test_run_app.py +++ b/tests/test_run_app.py @@ -9,7 +9,7 @@ import subprocess import sys import time -from typing import Callable, NoReturn, Set +from typing import AsyncIterator, Callable, NoReturn, Set from unittest import mock from uuid import uuid4 @@ -906,6 +906,23 @@ async def init(): assert count == 3 +def test_run_app_raises_exception(patched_loop: asyncio.AbstractEventLoop) -> None: + async def context(app: web.Application) -> AsyncIterator[None]: + raise RuntimeError("foo") + yield # pragma: no cover + + app = web.Application() + app.cleanup_ctx.append(context) + + with mock.patch.object( + patched_loop, "call_exception_handler", autospec=True, spec_set=True + ) as m: + with pytest.raises(RuntimeError, match="foo"): + web.run_app(app, loop=patched_loop) + + assert not m.called + + class TestShutdown: def raiser(self) -> NoReturn: raise KeyboardInterrupt From be27f7926f0da9b4129d98cf628eea99dc9e3411 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:18:00 +0100 Subject: [PATCH 124/296] [PR #8966/f569894c backport][3.11] Fix auth reset logic during redirects to different origin when _base_url set (#8976) **This is a backport of PR #8966 as merged into master (f569894caa7cfbc2ec03fb5eed6021b9899dc4b4).** --------- Co-authored-by: Maxim Zemskov Co-authored-by: Sam Bull --- CHANGES/8966.feature.rst | 1 + aiohttp/client.py | 5 +- docs/client_reference.rst | 8 +- tests/test_client_functional.py | 132 ++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 CHANGES/8966.feature.rst diff --git a/CHANGES/8966.feature.rst b/CHANGES/8966.feature.rst new file mode 100644 index 00000000000..ab1dc45b60e --- /dev/null +++ b/CHANGES/8966.feature.rst @@ -0,0 +1 @@ +Updated ClientSession's auth logic to include default auth only if the request URL's origin matches _base_url; otherwise, the auth will not be included -- by :user:`MaximZemskov` diff --git a/aiohttp/client.py b/aiohttp/client.py index f9e3a5c5f65..f3c60d31f08 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -597,7 +597,10 @@ async def _request( if auth is None: auth = auth_from_url - if auth is None: + + if auth is None and ( + not self._base_url or self._base_url.origin() == url.origin() + ): auth = self._default_auth # It would be confusing if we support explicit # Authorization header with auth argument diff --git a/docs/client_reference.rst b/docs/client_reference.rst index f64df336755..afad40e2d83 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -100,9 +100,11 @@ The client session supports the context manager protocol for self closing. :param aiohttp.BasicAuth auth: an object that represents HTTP Basic Authorization (optional). It will be included - with any request to any origin and will not be - removed, event during redirect to a different - origin. + with any request. However, if the + ``_base_url`` parameter is set, the request + URL's origin must match the base URL's origin; + otherwise, the default auth will not be + included. :param version: supported HTTP version, ``HTTP 1.1`` by default. diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 1f9173bd3f7..c7c31c739b1 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -2905,6 +2905,138 @@ async def close(self): assert resp.status == 200 +async def test_auth_persist_on_redirect_to_other_host_with_global_auth( + create_server_for_url_and_handler, +) -> None: + url_from = URL("http://host1.com/path1") + url_to = URL("http://host2.com/path2") + + async def srv_from(request: web.Request): + assert request.host == url_from.host + assert request.headers["Authorization"] == "Basic dXNlcjpwYXNz" + raise web.HTTPFound(url_to) + + async def srv_to(request: web.Request) -> web.Response: + assert request.host == url_to.host + assert "Authorization" in request.headers, "Header was dropped" + return web.Response() + + server_from = await create_server_for_url_and_handler(url_from, srv_from) + server_to = await create_server_for_url_and_handler(url_to, srv_to) + + assert ( + url_from.host != url_to.host or server_from.scheme != server_to.scheme + ), "Invalid test case, host or scheme must differ" + + protocol_port_map = { + "http": 80, + "https": 443, + } + etc_hosts = { + (url_from.host, protocol_port_map[server_from.scheme]): server_from, + (url_to.host, protocol_port_map[server_to.scheme]): server_to, + } + + class FakeResolver(AbstractResolver): + async def resolve( + self, + host: str, + port: int = 0, + family: socket.AddressFamily = socket.AF_INET, + ): + server = etc_hosts[(host, port)] + assert server.port is not None + + return [ + { + "hostname": host, + "host": server.host, + "port": server.port, + "family": socket.AF_INET, + "proto": 0, + "flags": socket.AI_NUMERICHOST, + } + ] + + async def close(self) -> None: + """Dummy""" + + connector = aiohttp.TCPConnector(resolver=FakeResolver(), ssl=False) + + async with aiohttp.ClientSession( + connector=connector, auth=aiohttp.BasicAuth("user", "pass") + ) as client: + resp = await client.get(url_from) + assert resp.status == 200 + + +async def test_drop_auth_on_redirect_to_other_host_with_global_auth_and_base_url( + create_server_for_url_and_handler, +) -> None: + url_from = URL("http://host1.com/path1") + url_to = URL("http://host2.com/path2") + + async def srv_from(request: web.Request): + assert request.host == url_from.host + assert request.headers["Authorization"] == "Basic dXNlcjpwYXNz" + raise web.HTTPFound(url_to) + + async def srv_to(request: web.Request) -> web.Response: + assert request.host == url_to.host + assert "Authorization" not in request.headers, "Header was not dropped" + return web.Response() + + server_from = await create_server_for_url_and_handler(url_from, srv_from) + server_to = await create_server_for_url_and_handler(url_to, srv_to) + + assert ( + url_from.host != url_to.host or server_from.scheme != server_to.scheme + ), "Invalid test case, host or scheme must differ" + + protocol_port_map = { + "http": 80, + "https": 443, + } + etc_hosts = { + (url_from.host, protocol_port_map[server_from.scheme]): server_from, + (url_to.host, protocol_port_map[server_to.scheme]): server_to, + } + + class FakeResolver(AbstractResolver): + async def resolve( + self, + host: str, + port: int = 0, + family: socket.AddressFamily = socket.AF_INET, + ): + server = etc_hosts[(host, port)] + assert server.port is not None + + return [ + { + "hostname": host, + "host": server.host, + "port": server.port, + "family": socket.AF_INET, + "proto": 0, + "flags": socket.AI_NUMERICHOST, + } + ] + + async def close(self) -> None: + """Dummy""" + + connector = aiohttp.TCPConnector(resolver=FakeResolver(), ssl=False) + + async with aiohttp.ClientSession( + connector=connector, + base_url="http://host1.com", + auth=aiohttp.BasicAuth("user", "pass"), + ) as client: + resp = await client.get("/path1") + assert resp.status == 200 + + async def test_async_with_session() -> None: async with aiohttp.ClientSession() as session: pass From 5dd9534ba860f1167ab4e84e1a5e65e5732f76d3 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 13:36:27 -1000 Subject: [PATCH 125/296] [PR #8933/8f3b1f44 backport][3.10] Small cleanups to the websocket frame sender (#8978) Co-authored-by: J. Nick Koston --- aiohttp/http_websocket.py | 45 +++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/aiohttp/http_websocket.py b/aiohttp/http_websocket.py index db0cb429d83..2ea2c9191e1 100644 --- a/aiohttp/http_websocket.py +++ b/aiohttp/http_websocket.py @@ -115,6 +115,7 @@ class WSMsgType(IntEnum): PACK_RANDBITS = Struct("!L").pack MSG_SIZE: Final[int] = 2**14 DEFAULT_LIMIT: Final[int] = 2**16 +MASK_LEN: Final[int] = 4 class WSMessage(NamedTuple): @@ -625,12 +626,18 @@ async def _send_frame( if self._closing and not (opcode & WSMsgType.CLOSE): raise ConnectionResetError("Cannot write to closing transport") + # RSV are the reserved bits in the frame header. They are used to + # indicate that the frame is using an extension. + # https://datatracker.ietf.org/doc/html/rfc6455#section-5.2 rsv = 0 - # Only compress larger packets (disabled) # Does small packet needs to be compressed? # if self.compress and opcode < 8 and len(message) > 124: if (compress or self.compress) and opcode < 8: + # RSV1 (rsv = 0x40) is set for compressed frames + # https://datatracker.ietf.org/doc/html/rfc7692#section-7.2.3.1 + rsv = 0x40 + if compress: # Do not set self._compress if compressing is for this frame compressobj = self._make_compress_obj(compress) @@ -649,28 +656,39 @@ async def _send_frame( ) if message.endswith(_WS_DEFLATE_TRAILING): message = message[:-4] - rsv = rsv | 0x40 msg_length = len(message) use_mask = self.use_mask - if use_mask: - mask_bit = 0x80 - else: - mask_bit = 0 + mask_bit = 0x80 if use_mask else 0 + # Depending on the message length, the header is assembled differently. + # The first byte is reserved for the opcode and the RSV bits. + first_byte = 0x80 | rsv | opcode if msg_length < 126: - header = PACK_LEN1(0x80 | rsv | opcode, msg_length | mask_bit) + header = PACK_LEN1(first_byte, msg_length | mask_bit) + header_len = 2 elif msg_length < (1 << 16): - header = PACK_LEN2(0x80 | rsv | opcode, 126 | mask_bit, msg_length) + header = PACK_LEN2(first_byte, 126 | mask_bit, msg_length) + header_len = 4 else: - header = PACK_LEN3(0x80 | rsv | opcode, 127 | mask_bit, msg_length) + header = PACK_LEN3(first_byte, 127 | mask_bit, msg_length) + header_len = 10 + + # https://datatracker.ietf.org/doc/html/rfc6455#section-5.3 + # If we are using a mask, we need to generate it randomly + # and apply it to the message before sending it. A mask is + # a 32-bit value that is applied to the message using a + # bitwise XOR operation. It is used to prevent certain types + # of attacks on the websocket protocol. The mask is only used + # when aiohttp is acting as a client. Servers do not use a mask. if use_mask: mask = PACK_RANDBITS(self.get_random_bits()) message = bytearray(message) _websocket_mask(mask, message) self._write(header + mask + message) - self._output_size += len(header) + len(mask) + msg_length + self._output_size += header_len + MASK_LEN + msg_length + else: if msg_length > MSG_SIZE: self._write(header) @@ -678,11 +696,16 @@ async def _send_frame( else: self._write(header + message) - self._output_size += len(header) + msg_length + self._output_size += header_len + msg_length # It is safe to return control to the event loop when using compression # after this point as we have already sent or buffered all the data. + # Once we have written output_size up to the limit, we call the + # drain helper which waits for the transport to be ready to accept + # more data. This is a flow control mechanism to prevent the buffer + # from growing too large. The drain helper will return right away + # if the writer is not paused. if self._output_size > self._limit: self._output_size = 0 await self.protocol._drain_helper() From 2af23ab33a4ae3727e2345b2e60c4341243dee3d Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:41:12 +0000 Subject: [PATCH 126/296] [PR #8933/8f3b1f44 backport][3.11] Small cleanups to the websocket frame sender (#8979) Co-authored-by: J. Nick Koston --- aiohttp/http_websocket.py | 45 +++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/aiohttp/http_websocket.py b/aiohttp/http_websocket.py index db0cb429d83..2ea2c9191e1 100644 --- a/aiohttp/http_websocket.py +++ b/aiohttp/http_websocket.py @@ -115,6 +115,7 @@ class WSMsgType(IntEnum): PACK_RANDBITS = Struct("!L").pack MSG_SIZE: Final[int] = 2**14 DEFAULT_LIMIT: Final[int] = 2**16 +MASK_LEN: Final[int] = 4 class WSMessage(NamedTuple): @@ -625,12 +626,18 @@ async def _send_frame( if self._closing and not (opcode & WSMsgType.CLOSE): raise ConnectionResetError("Cannot write to closing transport") + # RSV are the reserved bits in the frame header. They are used to + # indicate that the frame is using an extension. + # https://datatracker.ietf.org/doc/html/rfc6455#section-5.2 rsv = 0 - # Only compress larger packets (disabled) # Does small packet needs to be compressed? # if self.compress and opcode < 8 and len(message) > 124: if (compress or self.compress) and opcode < 8: + # RSV1 (rsv = 0x40) is set for compressed frames + # https://datatracker.ietf.org/doc/html/rfc7692#section-7.2.3.1 + rsv = 0x40 + if compress: # Do not set self._compress if compressing is for this frame compressobj = self._make_compress_obj(compress) @@ -649,28 +656,39 @@ async def _send_frame( ) if message.endswith(_WS_DEFLATE_TRAILING): message = message[:-4] - rsv = rsv | 0x40 msg_length = len(message) use_mask = self.use_mask - if use_mask: - mask_bit = 0x80 - else: - mask_bit = 0 + mask_bit = 0x80 if use_mask else 0 + # Depending on the message length, the header is assembled differently. + # The first byte is reserved for the opcode and the RSV bits. + first_byte = 0x80 | rsv | opcode if msg_length < 126: - header = PACK_LEN1(0x80 | rsv | opcode, msg_length | mask_bit) + header = PACK_LEN1(first_byte, msg_length | mask_bit) + header_len = 2 elif msg_length < (1 << 16): - header = PACK_LEN2(0x80 | rsv | opcode, 126 | mask_bit, msg_length) + header = PACK_LEN2(first_byte, 126 | mask_bit, msg_length) + header_len = 4 else: - header = PACK_LEN3(0x80 | rsv | opcode, 127 | mask_bit, msg_length) + header = PACK_LEN3(first_byte, 127 | mask_bit, msg_length) + header_len = 10 + + # https://datatracker.ietf.org/doc/html/rfc6455#section-5.3 + # If we are using a mask, we need to generate it randomly + # and apply it to the message before sending it. A mask is + # a 32-bit value that is applied to the message using a + # bitwise XOR operation. It is used to prevent certain types + # of attacks on the websocket protocol. The mask is only used + # when aiohttp is acting as a client. Servers do not use a mask. if use_mask: mask = PACK_RANDBITS(self.get_random_bits()) message = bytearray(message) _websocket_mask(mask, message) self._write(header + mask + message) - self._output_size += len(header) + len(mask) + msg_length + self._output_size += header_len + MASK_LEN + msg_length + else: if msg_length > MSG_SIZE: self._write(header) @@ -678,11 +696,16 @@ async def _send_frame( else: self._write(header + message) - self._output_size += len(header) + msg_length + self._output_size += header_len + msg_length # It is safe to return control to the event loop when using compression # after this point as we have already sent or buffered all the data. + # Once we have written output_size up to the limit, we call the + # drain helper which waits for the transport to be ready to accept + # more data. This is a flow control mechanism to prevent the buffer + # from growing too large. The drain helper will return right away + # if the writer is not paused. if self._output_size > self._limit: self._output_size = 0 await self.protocol._drain_helper() From 553e311e0e66548ab8a4b90f7e7561c0901b25c3 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 01:57:51 +0100 Subject: [PATCH 127/296] [PR #8968/8daecf5c backport][3.11] List specific timeouts for each exception (#8982) **This is a backport of PR #8968 as merged into master (8daecf5cc357518957deec45a526dfd1703ec48b).** Co-authored-by: Sam Bull --- CHANGES/8968.doc.rst | 1 + docs/client_reference.rst | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 CHANGES/8968.doc.rst diff --git a/CHANGES/8968.doc.rst b/CHANGES/8968.doc.rst new file mode 100644 index 00000000000..3420794586f --- /dev/null +++ b/CHANGES/8968.doc.rst @@ -0,0 +1 @@ +Clarified which timeout exceptions happen on which timeouts -- by :user:`Dreamsorcerer`. diff --git a/docs/client_reference.rst b/docs/client_reference.rst index afad40e2d83..887b196fa62 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -2327,17 +2327,20 @@ Connection errors Server operation timeout: read timeout, etc. + To catch all timeouts, including the ``total`` timeout, use + :exc:`asyncio.TimeoutError`. + Derived from :exc:`ServerConnectionError` and :exc:`asyncio.TimeoutError` .. class:: ConnectionTimeoutError - Connection timeout on request: e.g. read timeout. + Connection timeout on ``connect`` and ``sock_connect`` timeouts. Derived from :exc:`ServerTimeoutError` .. class:: SocketTimeoutError - Reading from socket timeout. + Reading from socket timeout on ``sock_read`` timeout. Derived from :exc:`ServerTimeoutError` From c733c68d62220f304266bcf997b57f91a871b8a5 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 01:58:11 +0100 Subject: [PATCH 128/296] [PR #8968/8daecf5c backport][3.10] List specific timeouts for each exception (#8981) **This is a backport of PR #8968 as merged into master (8daecf5cc357518957deec45a526dfd1703ec48b).** Co-authored-by: Sam Bull --- CHANGES/8968.doc.rst | 1 + docs/client_reference.rst | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 CHANGES/8968.doc.rst diff --git a/CHANGES/8968.doc.rst b/CHANGES/8968.doc.rst new file mode 100644 index 00000000000..3420794586f --- /dev/null +++ b/CHANGES/8968.doc.rst @@ -0,0 +1 @@ +Clarified which timeout exceptions happen on which timeouts -- by :user:`Dreamsorcerer`. diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 4e63552cd5c..1af1cde68ba 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -2309,17 +2309,20 @@ Connection errors Server operation timeout: read timeout, etc. + To catch all timeouts, including the ``total`` timeout, use + :exc:`asyncio.TimeoutError`. + Derived from :exc:`ServerConnectionError` and :exc:`asyncio.TimeoutError` .. class:: ConnectionTimeoutError - Connection timeout on request: e.g. read timeout. + Connection timeout on ``connect`` and ``sock_connect`` timeouts. Derived from :exc:`ServerTimeoutError` .. class:: SocketTimeoutError - Reading from socket timeout. + Reading from socket timeout on ``sock_read`` timeout. Derived from :exc:`ServerTimeoutError` From ea316336f0b247bb85fd7ee0e4e41f5eac682228 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 2 Sep 2024 02:20:25 +0100 Subject: [PATCH 129/296] Fix resolve_host "Task was destroyed but it is pending" errors (#8967) (#8980) (cherry picked from commit cd761a347be2609deca503646b9b5fb3585b2fda) --- CHANGES/8967.bugfix.rst | 1 + aiohttp/connector.py | 6 ++++++ tests/test_connector.py | 38 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 CHANGES/8967.bugfix.rst diff --git a/CHANGES/8967.bugfix.rst b/CHANGES/8967.bugfix.rst new file mode 100644 index 00000000000..1046f36bd8b --- /dev/null +++ b/CHANGES/8967.bugfix.rst @@ -0,0 +1 @@ +Fixed resolve_host() 'Task was destroyed but is pending' errors -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/connector.py b/aiohttp/connector.py index 91174e319ab..93c78c62b08 100644 --- a/aiohttp/connector.py +++ b/aiohttp/connector.py @@ -824,12 +824,16 @@ def __init__( self._local_addr_infos = aiohappyeyeballs.addr_to_addr_infos(local_addr) self._happy_eyeballs_delay = happy_eyeballs_delay self._interleave = interleave + self._resolve_host_tasks: Set["asyncio.Task[List[ResolveResult]]"] = set() def close(self) -> Awaitable[None]: """Close all ongoing DNS calls.""" for ev in self._throttle_dns_events.values(): ev.cancel() + for t in self._resolve_host_tasks: + t.cancel() + return super().close() @property @@ -907,6 +911,8 @@ async def _resolve_host( resolved_host_task = asyncio.create_task( self._resolve_host_with_throttle(key, host, port, traces) ) + self._resolve_host_tasks.add(resolved_host_task) + resolved_host_task.add_done_callback(self._resolve_host_tasks.discard) try: return await asyncio.shield(resolved_host_task) except asyncio.CancelledError: diff --git a/tests/test_connector.py b/tests/test_connector.py index 8dd7a294b30..0129f0cc330 100644 --- a/tests/test_connector.py +++ b/tests/test_connector.py @@ -9,7 +9,7 @@ import sys import uuid from collections import deque -from contextlib import closing +from contextlib import closing, suppress from typing import Any, List, Optional, Type from unittest import mock @@ -1667,7 +1667,41 @@ async def test_close_cancels_cleanup_handle(loop) -> None: assert conn._cleanup_handle is None -async def test_close_abort_closed_transports(loop) -> None: +async def test_close_cancels_resolve_host(loop: asyncio.AbstractEventLoop) -> None: + cancelled = False + + async def delay_resolve_host(*args: object) -> None: + """Delay _resolve_host() task in order to test cancellation.""" + nonlocal cancelled + try: + await asyncio.sleep(10) + except asyncio.CancelledError: + cancelled = True + raise + + conn = aiohttp.TCPConnector() + req = ClientRequest( + "GET", URL("http://localhost:80"), loop=loop, response_class=mock.Mock() + ) + with mock.patch.object(conn, "_resolve_host_with_throttle", delay_resolve_host): + t = asyncio.create_task(conn.connect(req, [], ClientTimeout())) + # Let it create the internal task + await asyncio.sleep(0) + # Let that task start running + await asyncio.sleep(0) + + # We now have a task being tracked and can ensure that .close() cancels it. + assert len(conn._resolve_host_tasks) == 1 + await conn.close() + await asyncio.sleep(0.01) + assert cancelled + assert len(conn._resolve_host_tasks) == 0 + + with suppress(asyncio.CancelledError): + await t + + +async def test_close_abort_closed_transports(loop: asyncio.AbstractEventLoop) -> None: tr = mock.Mock() conn = aiohttp.BaseConnector(loop=loop) From 64a3d30a57952ec86caa2688d4a98696a486d3ea Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:48:42 +0100 Subject: [PATCH 130/296] [PR #8980/ea316336 backport][3.10] Fix resolve_host "Task was destroyed but it is pending" errors (#8967) (#8984) **This is a backport of PR #8980 as merged into 3.11 (ea316336f0b247bb85fd7ee0e4e41f5eac682228).** (cherry picked from commit cd761a347be2609deca503646b9b5fb3585b2fda) Co-authored-by: Sam Bull --- CHANGES/8967.bugfix.rst | 1 + aiohttp/connector.py | 6 ++++++ tests/test_connector.py | 38 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 CHANGES/8967.bugfix.rst diff --git a/CHANGES/8967.bugfix.rst b/CHANGES/8967.bugfix.rst new file mode 100644 index 00000000000..1046f36bd8b --- /dev/null +++ b/CHANGES/8967.bugfix.rst @@ -0,0 +1 @@ +Fixed resolve_host() 'Task was destroyed but is pending' errors -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/connector.py b/aiohttp/connector.py index 04115c36a24..7c6e747695e 100644 --- a/aiohttp/connector.py +++ b/aiohttp/connector.py @@ -824,12 +824,16 @@ def __init__( self._local_addr_infos = aiohappyeyeballs.addr_to_addr_infos(local_addr) self._happy_eyeballs_delay = happy_eyeballs_delay self._interleave = interleave + self._resolve_host_tasks: Set["asyncio.Task[List[ResolveResult]]"] = set() def close(self) -> Awaitable[None]: """Close all ongoing DNS calls.""" for ev in self._throttle_dns_events.values(): ev.cancel() + for t in self._resolve_host_tasks: + t.cancel() + return super().close() @property @@ -907,6 +911,8 @@ async def _resolve_host( resolved_host_task = asyncio.create_task( self._resolve_host_with_throttle(key, host, port, traces) ) + self._resolve_host_tasks.add(resolved_host_task) + resolved_host_task.add_done_callback(self._resolve_host_tasks.discard) try: return await asyncio.shield(resolved_host_task) except asyncio.CancelledError: diff --git a/tests/test_connector.py b/tests/test_connector.py index 8dd7a294b30..0129f0cc330 100644 --- a/tests/test_connector.py +++ b/tests/test_connector.py @@ -9,7 +9,7 @@ import sys import uuid from collections import deque -from contextlib import closing +from contextlib import closing, suppress from typing import Any, List, Optional, Type from unittest import mock @@ -1667,7 +1667,41 @@ async def test_close_cancels_cleanup_handle(loop) -> None: assert conn._cleanup_handle is None -async def test_close_abort_closed_transports(loop) -> None: +async def test_close_cancels_resolve_host(loop: asyncio.AbstractEventLoop) -> None: + cancelled = False + + async def delay_resolve_host(*args: object) -> None: + """Delay _resolve_host() task in order to test cancellation.""" + nonlocal cancelled + try: + await asyncio.sleep(10) + except asyncio.CancelledError: + cancelled = True + raise + + conn = aiohttp.TCPConnector() + req = ClientRequest( + "GET", URL("http://localhost:80"), loop=loop, response_class=mock.Mock() + ) + with mock.patch.object(conn, "_resolve_host_with_throttle", delay_resolve_host): + t = asyncio.create_task(conn.connect(req, [], ClientTimeout())) + # Let it create the internal task + await asyncio.sleep(0) + # Let that task start running + await asyncio.sleep(0) + + # We now have a task being tracked and can ensure that .close() cancels it. + assert len(conn._resolve_host_tasks) == 1 + await conn.close() + await asyncio.sleep(0.01) + assert cancelled + assert len(conn._resolve_host_tasks) == 0 + + with suppress(asyncio.CancelledError): + await t + + +async def test_close_abort_closed_transports(loop: asyncio.AbstractEventLoop) -> None: tr = mock.Mock() conn = aiohttp.BaseConnector(loop=loop) From a86689fb25754f239193c08e7f2afbaebd31c49d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:16:07 +0000 Subject: [PATCH 131/296] Bump yarl from 1.9.6 to 1.9.7 (#8986) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [yarl](https://github.com/aio-libs/yarl) from 1.9.6 to 1.9.7.
Release notes

Sourced from yarl's releases.

1.9.7

Removals and backward incompatible breaking changes

  • Removed support :rfc:3986#section-3.2.3 port normalization when the scheme is not one of http, https, wss, or ws -- by :user:bdraco.

    Support for port normalization was recently added in #1033 and contained code that would do blocking I/O if the scheme was not one of the four listed above. The code has been removed because this library is intended to be safe for usage with asyncio.

    Related issues and pull requests on GitHub: #1076.

Miscellaneous internal changes

  • Improved performance of property caching -- by :user:bdraco.

    The reify implementation from aiohttp was adapted to replace the internal cached_property implementation.

    Related issues and pull requests on GitHub: #1070.


Changelog

Sourced from yarl's changelog.

1.9.7

(2024-09-01)

Removals and backward incompatible breaking changes

  • Removed support :rfc:3986#section-3.2.3 port normalization when the scheme is not one of http, https, wss, or ws -- by :user:bdraco.

    Support for port normalization was recently added in :issue:1033 and contained code that would do blocking I/O if the scheme was not one of the four listed above. The code has been removed because this library is intended to be safe for usage with :mod:asyncio.

    Related issues and pull requests on GitHub: :issue:1076.

Miscellaneous internal changes

  • Improved performance of property caching -- by :user:bdraco.

    The reify implementation from aiohttp was adapted to replace the internal cached_property implementation.

    Related issues and pull requests on GitHub: :issue:1070.


Commits
  • 845f017 Release 1.9.7
  • 7c1220b Remove fallback to getservbyname in _get_default_port (#1076)
  • 6339581 Adapt aiohttp reify implementation to replace internal cached_property (#1070)
  • 2a3235e Add additional coverage for joining urls (#1066)
  • 94b6b51 🧪🚑 Fix coverage.py-included paths @ XML (#1074)
  • 3d5ce65 🧪 Bump MyPy to v1.11.2
  • 8aa3733 🧪 Add a MyPy run against Python 3.13 code paths
  • cbf0b9e 🧪💅 Lower-case bools in MyPy config
  • b253381 🧪 Drop leftover MyPy checked paths from config
  • a7f8007 🧪 Stop auto-installing MyPy type stubs
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=yarl&package-manager=pip&previous-version=1.9.6&new-version=1.9.7)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index 18b5f471150..5876b5881a7 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -38,5 +38,5 @@ pycparser==2.22 # via cffi uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in -yarl==1.9.6 +yarl==1.9.7 # via -r requirements/runtime-deps.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 869bb3d1b34..a2deb71956d 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -286,7 +286,7 @@ webcolors==24.8.0 # via blockdiag wheel==0.44.0 # via pip-tools -yarl==1.9.6 +yarl==1.9.7 # via -r requirements/runtime-deps.in zipp==3.20.1 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 9555838a4bf..2e40f0bb153 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -278,7 +278,7 @@ webcolors==24.8.0 # via blockdiag wheel==0.44.0 # via pip-tools -yarl==1.9.6 +yarl==1.9.7 # via -r requirements/runtime-deps.in zipp==3.20.1 # via diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index c876071fc19..00927852825 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -32,5 +32,5 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -yarl==1.9.6 +yarl==1.9.7 # via -r requirements/runtime-deps.in diff --git a/requirements/test.txt b/requirements/test.txt index 3caac33849b..a2bfc72a0a9 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -136,5 +136,5 @@ uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpytho # via -r requirements/base.in wait-for-it==2.2.2 # via -r requirements/test.in -yarl==1.9.6 +yarl==1.9.7 # via -r requirements/runtime-deps.in From ed5576dba118886c52d044dc2a73f291112ca30a Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:12:21 +0100 Subject: [PATCH 132/296] [PR #8987/5c3d50f9 backport][3.11] Move ContentDisposition in reference docs (#8989) **This is a backport of PR #8987 as merged into master (5c3d50f9006a0e86927cfc3f092ec2a30b490871).** Co-authored-by: Sam Bull --- docs/client_reference.rst | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 887b196fa62..c2d6b6ac979 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -1820,6 +1820,26 @@ Utilities .. versionadded:: 3.8 +.. class:: ContentDisposition + + A data class to represent the Content-Disposition header, + available as :attr:`ClientResponse.content_disposition` attribute. + + .. attribute:: type + + A :class:`str` instance. Value of Content-Disposition header + itself, e.g. ``attachment``. + + .. attribute:: filename + + A :class:`str` instance. Content filename extracted from + parameters. May be ``None``. + + .. attribute:: parameters + + Read-only mapping contains all parameters. + + .. class:: RequestInfo() A data class with request URL and headers from :class:`~aiohttp.ClientRequest` @@ -2170,25 +2190,6 @@ All exceptions are available as members of *aiohttp* module. Derived from :exc:`RedirectClientError` and :exc:`NonHttpUrlClientError` - -.. class:: ContentDisposition - - Represent Content-Disposition header - - .. attribute:: type - - A :class:`str` instance. Value of Content-Disposition header - itself, e.g. ``attachment``. - - .. attribute:: filename - - A :class:`str` instance. Content filename extracted from - parameters. May be ``None``. - - .. attribute:: parameters - - Read-only mapping contains all parameters. - Response errors ^^^^^^^^^^^^^^^ From 7c3ff649f66c45450c121e9f506b4a947861016f Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:12:36 +0100 Subject: [PATCH 133/296] [PR #8987/5c3d50f9 backport][3.10] Move ContentDisposition in reference docs (#8988) **This is a backport of PR #8987 as merged into master (5c3d50f9006a0e86927cfc3f092ec2a30b490871).** Co-authored-by: Sam Bull --- docs/client_reference.rst | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 1af1cde68ba..9de1ea401c7 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -1802,6 +1802,26 @@ Utilities .. versionadded:: 3.8 +.. class:: ContentDisposition + + A data class to represent the Content-Disposition header, + available as :attr:`ClientResponse.content_disposition` attribute. + + .. attribute:: type + + A :class:`str` instance. Value of Content-Disposition header + itself, e.g. ``attachment``. + + .. attribute:: filename + + A :class:`str` instance. Content filename extracted from + parameters. May be ``None``. + + .. attribute:: parameters + + Read-only mapping contains all parameters. + + .. class:: RequestInfo() A data class with request URL and headers from :class:`~aiohttp.ClientRequest` @@ -2152,25 +2172,6 @@ All exceptions are available as members of *aiohttp* module. Derived from :exc:`RedirectClientError` and :exc:`NonHttpUrlClientError` - -.. class:: ContentDisposition - - Represent Content-Disposition header - - .. attribute:: type - - A :class:`str` instance. Value of Content-Disposition header - itself, e.g. ``attachment``. - - .. attribute:: filename - - A :class:`str` instance. Content filename extracted from - parameters. May be ``None``. - - .. attribute:: parameters - - Read-only mapping contains all parameters. - Response errors ^^^^^^^^^^^^^^^ From effb57523e8cb705bc0d3f1cb1832c02b244f986 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 00:49:08 +0100 Subject: [PATCH 134/296] [PR #8991/1ba30112 backport][3.11] Update ClientSession reference (#8995) **This is a backport of PR #8991 as merged into master (1ba3011253e60e3bc35ea7dc93ab6a903e22cef0).** Co-authored-by: Sam Bull --- CHANGES/8991.doc.rst | 1 + docs/client_reference.rst | 145 ++++++++++++++------------------------ 2 files changed, 53 insertions(+), 93 deletions(-) create mode 100644 CHANGES/8991.doc.rst diff --git a/CHANGES/8991.doc.rst b/CHANGES/8991.doc.rst new file mode 100644 index 00000000000..c29850c4f3c --- /dev/null +++ b/CHANGES/8991.doc.rst @@ -0,0 +1 @@ +Updated ``ClientSession`` parameters to match current code -- by :user:`Dreamsorcerer`. diff --git a/docs/client_reference.rst b/docs/client_reference.rst index c2d6b6ac979..77230a755c6 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -41,17 +41,21 @@ The client session supports the context manager protocol for self closing. connector=None, cookies=None, \ headers=None, skip_auto_headers=None, \ auth=None, json_serialize=json.dumps, \ + request_class=ClientRequest, \ + response_class=ClientResponse, \ + ws_response_class=ClientWebSocketResponse, \ version=aiohttp.HttpVersion11, \ - cookie_jar=None, read_timeout=None, \ - conn_timeout=None, \ - timeout=sentinel, \ - raise_for_status=False, \ + cookie_jar=None, \ connector_owner=True, \ + raise_for_status=False, \ + timeout=sentinel, \ auto_decompress=True, \ - read_bufsize=2**16, \ - requote_redirect_url=True, \ trust_env=False, \ + requote_redirect_url=True, \ trace_configs=None, \ + read_bufsize=2**16, \ + max_line_size=8190, \ + max_field_size=8190, \ fallback_charset_resolver=lambda r, b: "utf-8") The class for creating client sessions and making requests. @@ -67,17 +71,6 @@ The client session supports the context manager protocol for self closing. :param aiohttp.BaseConnector connector: BaseConnector sub-class instance to support connection pooling. - :param loop: :ref:`event loop` used for - processing HTTP requests. - - If *loop* is ``None`` the constructor - borrows it from *connector* if specified. - - :func:`asyncio.get_event_loop` is used for getting default event - loop otherwise. - - .. deprecated:: 2.0 - :param dict cookies: Cookies to send with the request (optional) :param headers: HTTP Headers to send with every request (optional). @@ -106,6 +99,16 @@ The client session supports the context manager protocol for self closing. otherwise, the default auth will not be included. + :param collections.abc.Callable json_serialize: Json *serializer* callable. + + By default :func:`json.dumps` function. + + :param aiohttp.ClientRequest request_class: Custom class to use for client requests. + + :param ClientResponse response_class: Custom class to use for client responses. + + :param ClientWebSocketResponse ws_response_class: Custom class to use for websocket responses. + :param version: supported HTTP version, ``HTTP 1.1`` by default. :param cookie_jar: Cookie Jar, :class:`~aiohttp.abc.AbstractCookieJar` instance. @@ -121,9 +124,13 @@ The client session supports the context manager protocol for self closing. :class:`aiohttp.DummyCookieJar` instance can be provided. - :param collections.abc.Callable json_serialize: Json *serializer* callable. + :param bool connector_owner: - By default :func:`json.dumps` function. + Close connector instance on session closing. + + Setting the parameter to ``False`` allows to share + connection pool between sessions without sharing session state: + cookies etc. :param bool raise_for_status: @@ -163,39 +170,10 @@ The client session supports the context manager protocol for self closing. .. versionadded:: 3.3 - :param float read_timeout: Request operations timeout. ``read_timeout`` is - cumulative for all request operations (request, redirects, responses, - data consuming). By default, the read timeout is 5*60 seconds. - Use ``None`` or ``0`` to disable timeout checks. - - .. deprecated:: 3.3 - - Use ``timeout`` parameter instead. - - :param float conn_timeout: timeout for connection establishing - (optional). Values ``0`` or ``None`` mean no timeout. - - .. deprecated:: 3.3 - - Use ``timeout`` parameter instead. - - :param bool connector_owner: - - Close connector instance on session closing. - - Setting the parameter to ``False`` allows to share - connection pool between sessions without sharing session state: - cookies etc. - :param bool auto_decompress: Automatically decompress response body (``True`` by default). .. versionadded:: 2.3 - :param int read_bufsize: Size of the read buffer (:attr:`ClientResponse.content`). - 64 KiB by default. - - .. versionadded:: 3.7 - :param bool trust_env: Trust environment settings for proxy configuration if the parameter is ``True`` (``False`` by default). See :ref:`aiohttp-client-proxy-support` for more information. @@ -232,6 +210,15 @@ The client session supports the context manager protocol for self closing. disabling. See :ref:`aiohttp-client-tracing-reference` for more information. + :param int read_bufsize: Size of the read buffer (:attr:`ClientResponse.content`). + 64 KiB by default. + + .. versionadded:: 3.7 + + :param int max_line_size: Maximum allowed size of lines in responses. + + :param int max_field_size: Maximum allowed size of header fields in responses. + :param Callable[[ClientResponse,bytes],str] fallback_charset_resolver: A :term:`callable` that accepts a :class:`ClientResponse` and the :class:`bytes` contents, and returns a :class:`str` which will be used as @@ -376,12 +363,15 @@ The client session supports the context manager protocol for self closing. max_redirects=10,\ compress=None, chunked=None, expect100=False, raise_for_status=None,\ read_until_eof=True, \ - read_bufsize=None, \ proxy=None, proxy_auth=None,\ timeout=sentinel, ssl=True, \ - verify_ssl=None, fingerprint=None, \ - ssl_context=None, proxy_headers=None, \ - server_hostname=None, auto_decompress=None) + server_hostname=None, \ + proxy_headers=None, \ + trace_request_ctx=None, \ + read_bufsize=None, \ + auto_decompress=None, \ + max_line_size=None, \ + max_field_size=None) :async: :noindexentry: @@ -475,12 +465,6 @@ The client session supports the context manager protocol for self closing. does not have Content-Length header. ``True`` by default (optional). - :param int read_bufsize: Size of the read buffer (:attr:`ClientResponse.content`). - ``None`` by default, - it means that the session global value is used. - - .. versionadded:: 3.7 - :param proxy: Proxy URL, :class:`str` or :class:`~yarl.URL` (optional) :param aiohttp.BasicAuth proxy_auth: an object that represents proxy HTTP @@ -508,29 +492,6 @@ The client session supports the context manager protocol for self closing. .. versionadded:: 3.0 - :param bool verify_ssl: Perform SSL certificate validation for - *HTTPS* requests (enabled by default). May be disabled to - skip validation for sites with invalid certificates. - - .. versionadded:: 2.3 - - .. deprecated:: 3.0 - - Use ``ssl=False`` - - :param bytes fingerprint: Pass the SHA256 digest of the expected - certificate in DER format to verify that the certificate the - server presents matches. Useful for `certificate pinning - `_. - - Warning: use of MD5 or SHA1 digests is insecure and removed. - - .. versionadded:: 2.3 - - .. deprecated:: 3.0 - - Use ``ssl=aiohttp.Fingerprint(digest)`` - :param str server_hostname: Sets or overrides the host name that the target server’s certificate will be matched against. @@ -538,18 +499,6 @@ The client session supports the context manager protocol for self closing. .. versionadded:: 3.9 - :param ssl.SSLContext ssl_context: ssl context used for processing - *HTTPS* requests (optional). - - *ssl_context* may be used for configuring certification - authority channel, supported SSL options etc. - - .. versionadded:: 2.3 - - .. deprecated:: 3.0 - - Use ``ssl=ssl_context`` - :param collections.abc.Mapping proxy_headers: HTTP headers to send to the proxy if the parameter proxy has been provided. @@ -562,10 +511,20 @@ The client session supports the context manager protocol for self closing. .. versionadded:: 3.0 + :param int read_bufsize: Size of the read buffer (:attr:`ClientResponse.content`). + ``None`` by default, + it means that the session global value is used. + + .. versionadded:: 3.7 + :param bool auto_decompress: Automatically decompress response body. Overrides :attr:`ClientSession.auto_decompress`. May be used to enable/disable auto decompression on a per-request basis. + :param int max_line_size: Maximum allowed size of lines in responses. + + :param int max_field_size: Maximum allowed size of header fields in responses. + :return ClientResponse: a :class:`client response ` object. From 1adf397e8b5a626bd4bb5553edb15d6297c6f764 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 00:49:26 +0100 Subject: [PATCH 135/296] [PR #8991/1ba30112 backport][3.10] Update ClientSession reference (#8994) **This is a backport of PR #8991 as merged into master (1ba3011253e60e3bc35ea7dc93ab6a903e22cef0).** Co-authored-by: Sam Bull --- CHANGES/8991.doc.rst | 1 + docs/client_reference.rst | 145 ++++++++++++++------------------------ 2 files changed, 53 insertions(+), 93 deletions(-) create mode 100644 CHANGES/8991.doc.rst diff --git a/CHANGES/8991.doc.rst b/CHANGES/8991.doc.rst new file mode 100644 index 00000000000..c29850c4f3c --- /dev/null +++ b/CHANGES/8991.doc.rst @@ -0,0 +1 @@ +Updated ``ClientSession`` parameters to match current code -- by :user:`Dreamsorcerer`. diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 9de1ea401c7..bcd2108c1eb 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -41,17 +41,21 @@ The client session supports the context manager protocol for self closing. connector=None, cookies=None, \ headers=None, skip_auto_headers=None, \ auth=None, json_serialize=json.dumps, \ + request_class=ClientRequest, \ + response_class=ClientResponse, \ + ws_response_class=ClientWebSocketResponse, \ version=aiohttp.HttpVersion11, \ - cookie_jar=None, read_timeout=None, \ - conn_timeout=None, \ - timeout=sentinel, \ - raise_for_status=False, \ + cookie_jar=None, \ connector_owner=True, \ + raise_for_status=False, \ + timeout=sentinel, \ auto_decompress=True, \ - read_bufsize=2**16, \ - requote_redirect_url=True, \ trust_env=False, \ + requote_redirect_url=True, \ trace_configs=None, \ + read_bufsize=2**16, \ + max_line_size=8190, \ + max_field_size=8190, \ fallback_charset_resolver=lambda r, b: "utf-8") The class for creating client sessions and making requests. @@ -67,17 +71,6 @@ The client session supports the context manager protocol for self closing. :param aiohttp.BaseConnector connector: BaseConnector sub-class instance to support connection pooling. - :param loop: :ref:`event loop` used for - processing HTTP requests. - - If *loop* is ``None`` the constructor - borrows it from *connector* if specified. - - :func:`asyncio.get_event_loop` is used for getting default event - loop otherwise. - - .. deprecated:: 2.0 - :param dict cookies: Cookies to send with the request (optional) :param headers: HTTP Headers to send with every request (optional). @@ -104,6 +97,16 @@ The client session supports the context manager protocol for self closing. removed, event during redirect to a different origin. + :param collections.abc.Callable json_serialize: Json *serializer* callable. + + By default :func:`json.dumps` function. + + :param aiohttp.ClientRequest request_class: Custom class to use for client requests. + + :param ClientResponse response_class: Custom class to use for client responses. + + :param ClientWebSocketResponse ws_response_class: Custom class to use for websocket responses. + :param version: supported HTTP version, ``HTTP 1.1`` by default. :param cookie_jar: Cookie Jar, :class:`~aiohttp.abc.AbstractCookieJar` instance. @@ -119,9 +122,13 @@ The client session supports the context manager protocol for self closing. :class:`aiohttp.DummyCookieJar` instance can be provided. - :param collections.abc.Callable json_serialize: Json *serializer* callable. + :param bool connector_owner: - By default :func:`json.dumps` function. + Close connector instance on session closing. + + Setting the parameter to ``False`` allows to share + connection pool between sessions without sharing session state: + cookies etc. :param bool raise_for_status: @@ -161,39 +168,10 @@ The client session supports the context manager protocol for self closing. .. versionadded:: 3.3 - :param float read_timeout: Request operations timeout. ``read_timeout`` is - cumulative for all request operations (request, redirects, responses, - data consuming). By default, the read timeout is 5*60 seconds. - Use ``None`` or ``0`` to disable timeout checks. - - .. deprecated:: 3.3 - - Use ``timeout`` parameter instead. - - :param float conn_timeout: timeout for connection establishing - (optional). Values ``0`` or ``None`` mean no timeout. - - .. deprecated:: 3.3 - - Use ``timeout`` parameter instead. - - :param bool connector_owner: - - Close connector instance on session closing. - - Setting the parameter to ``False`` allows to share - connection pool between sessions without sharing session state: - cookies etc. - :param bool auto_decompress: Automatically decompress response body (``True`` by default). .. versionadded:: 2.3 - :param int read_bufsize: Size of the read buffer (:attr:`ClientResponse.content`). - 64 KiB by default. - - .. versionadded:: 3.7 - :param bool trust_env: Trust environment settings for proxy configuration if the parameter is ``True`` (``False`` by default). See :ref:`aiohttp-client-proxy-support` for more information. @@ -230,6 +208,15 @@ The client session supports the context manager protocol for self closing. disabling. See :ref:`aiohttp-client-tracing-reference` for more information. + :param int read_bufsize: Size of the read buffer (:attr:`ClientResponse.content`). + 64 KiB by default. + + .. versionadded:: 3.7 + + :param int max_line_size: Maximum allowed size of lines in responses. + + :param int max_field_size: Maximum allowed size of header fields in responses. + :param Callable[[ClientResponse,bytes],str] fallback_charset_resolver: A :term:`callable` that accepts a :class:`ClientResponse` and the :class:`bytes` contents, and returns a :class:`str` which will be used as @@ -374,12 +361,15 @@ The client session supports the context manager protocol for self closing. max_redirects=10,\ compress=None, chunked=None, expect100=False, raise_for_status=None,\ read_until_eof=True, \ - read_bufsize=None, \ proxy=None, proxy_auth=None,\ timeout=sentinel, ssl=True, \ - verify_ssl=None, fingerprint=None, \ - ssl_context=None, proxy_headers=None, \ - server_hostname=None, auto_decompress=None) + server_hostname=None, \ + proxy_headers=None, \ + trace_request_ctx=None, \ + read_bufsize=None, \ + auto_decompress=None, \ + max_line_size=None, \ + max_field_size=None) :async: :noindexentry: @@ -473,12 +463,6 @@ The client session supports the context manager protocol for self closing. does not have Content-Length header. ``True`` by default (optional). - :param int read_bufsize: Size of the read buffer (:attr:`ClientResponse.content`). - ``None`` by default, - it means that the session global value is used. - - .. versionadded:: 3.7 - :param proxy: Proxy URL, :class:`str` or :class:`~yarl.URL` (optional) :param aiohttp.BasicAuth proxy_auth: an object that represents proxy HTTP @@ -506,29 +490,6 @@ The client session supports the context manager protocol for self closing. .. versionadded:: 3.0 - :param bool verify_ssl: Perform SSL certificate validation for - *HTTPS* requests (enabled by default). May be disabled to - skip validation for sites with invalid certificates. - - .. versionadded:: 2.3 - - .. deprecated:: 3.0 - - Use ``ssl=False`` - - :param bytes fingerprint: Pass the SHA256 digest of the expected - certificate in DER format to verify that the certificate the - server presents matches. Useful for `certificate pinning - `_. - - Warning: use of MD5 or SHA1 digests is insecure and removed. - - .. versionadded:: 2.3 - - .. deprecated:: 3.0 - - Use ``ssl=aiohttp.Fingerprint(digest)`` - :param str server_hostname: Sets or overrides the host name that the target server’s certificate will be matched against. @@ -536,18 +497,6 @@ The client session supports the context manager protocol for self closing. .. versionadded:: 3.9 - :param ssl.SSLContext ssl_context: ssl context used for processing - *HTTPS* requests (optional). - - *ssl_context* may be used for configuring certification - authority channel, supported SSL options etc. - - .. versionadded:: 2.3 - - .. deprecated:: 3.0 - - Use ``ssl=ssl_context`` - :param collections.abc.Mapping proxy_headers: HTTP headers to send to the proxy if the parameter proxy has been provided. @@ -560,10 +509,20 @@ The client session supports the context manager protocol for self closing. .. versionadded:: 3.0 + :param int read_bufsize: Size of the read buffer (:attr:`ClientResponse.content`). + ``None`` by default, + it means that the session global value is used. + + .. versionadded:: 3.7 + :param bool auto_decompress: Automatically decompress response body. Overrides :attr:`ClientSession.auto_decompress`. May be used to enable/disable auto decompression on a per-request basis. + :param int max_line_size: Maximum allowed size of lines in responses. + + :param int max_field_size: Maximum allowed size of header fields in responses. + :return ClientResponse: a :class:`client response ` object. From fa628a2170cc03b82f32ea4e3812f41d015125e3 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 00:49:45 +0100 Subject: [PATCH 136/296] [PR #8990/731ba4dd backport][3.11] Fix changing scheme/host in Response.clone() for absolute URLs (#8997) **This is a backport of PR #8990 as merged into master (731ba4dd399c26fd470dde5ea50e011e058b798a).** Co-authored-by: Sam Bull --- CHANGES/8990.bugfix.rst | 1 + aiohttp/web_request.py | 12 ++++++++---- tests/test_web_request.py | 21 +++++++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 CHANGES/8990.bugfix.rst diff --git a/CHANGES/8990.bugfix.rst b/CHANGES/8990.bugfix.rst new file mode 100644 index 00000000000..9a9783103fd --- /dev/null +++ b/CHANGES/8990.bugfix.rst @@ -0,0 +1 @@ +Fixed changing scheme/host in ``Response.clone()`` for absolute URLs -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py index a63d3074ea5..1d94c576794 100644 --- a/aiohttp/web_request.py +++ b/aiohttp/web_request.py @@ -175,6 +175,10 @@ def __init__( self._cache: Dict[str, Any] = {} url = message.url if url.is_absolute(): + if scheme is not None: + url = url.with_scheme(scheme) + if host is not None: + url = url.with_host(host) # absolute URL is given, # override auto-calculating url, host, and scheme # all other properties should be good @@ -184,6 +188,10 @@ def __init__( self._rel_url = url.relative() else: self._rel_url = message.url + if scheme is not None: + self._cache["scheme"] = scheme + if host is not None: + self._cache["host"] = host self._post: Optional[MultiDictProxy[Union[str, bytes, FileField]]] = None self._read_bytes: Optional[bytes] = None @@ -197,10 +205,6 @@ def __init__( self._transport_sslcontext = transport.get_extra_info("sslcontext") self._transport_peername = transport.get_extra_info("peername") - if scheme is not None: - self._cache["scheme"] = scheme - if host is not None: - self._cache["host"] = host if remote is not None: self._cache["remote"] = remote diff --git a/tests/test_web_request.py b/tests/test_web_request.py index c6398ac1836..ba12d6f54e7 100644 --- a/tests/test_web_request.py +++ b/tests/test_web_request.py @@ -169,6 +169,22 @@ def test_absolute_url() -> None: assert req.rel_url == URL.build(path="/path/to", query={"a": "1"}) +def test_clone_absolute_scheme() -> None: + req = make_mocked_request("GET", "https://example.com/path/to?a=1") + assert req.scheme == "https" + req2 = req.clone(scheme="http") + assert req2.scheme == "http" + assert req2.url.scheme == "http" + + +def test_clone_absolute_host() -> None: + req = make_mocked_request("GET", "https://example.com/path/to?a=1") + assert req.host == "example.com" + req2 = req.clone(host="foo.test") + assert req2.host == "foo.test" + assert req2.url.host == "foo.test" + + def test_content_length() -> None: req = make_mocked_request("Get", "/", CIMultiDict([("CONTENT-LENGTH", "123")])) @@ -684,18 +700,23 @@ def test_save_state_on_clone() -> None: def test_clone_scheme() -> None: req = make_mocked_request("GET", "/") + assert req.scheme == "http" req2 = req.clone(scheme="https") assert req2.scheme == "https" + assert req2.url.scheme == "https" def test_clone_host() -> None: req = make_mocked_request("GET", "/") + assert req.host != "example.com" req2 = req.clone(host="example.com") assert req2.host == "example.com" + assert req2.url.host == "example.com" def test_clone_remote() -> None: req = make_mocked_request("GET", "/") + assert req.remote != "11.11.11.11" req2 = req.clone(remote="11.11.11.11") assert req2.remote == "11.11.11.11" From af8900de0e06c187a1235acd465c4b1ed2e5e6a0 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 00:57:48 +0100 Subject: [PATCH 137/296] [PR #8990/731ba4dd backport][3.10] Fix changing scheme/host in Response.clone() for absolute URLs (#8996) **This is a backport of PR #8990 as merged into master (731ba4dd399c26fd470dde5ea50e011e058b798a).** Co-authored-by: Sam Bull --- CHANGES/8990.bugfix.rst | 1 + aiohttp/web_request.py | 12 ++++++++---- tests/test_web_request.py | 21 +++++++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 CHANGES/8990.bugfix.rst diff --git a/CHANGES/8990.bugfix.rst b/CHANGES/8990.bugfix.rst new file mode 100644 index 00000000000..9a9783103fd --- /dev/null +++ b/CHANGES/8990.bugfix.rst @@ -0,0 +1 @@ +Fixed changing scheme/host in ``Response.clone()`` for absolute URLs -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py index a63d3074ea5..1d94c576794 100644 --- a/aiohttp/web_request.py +++ b/aiohttp/web_request.py @@ -175,6 +175,10 @@ def __init__( self._cache: Dict[str, Any] = {} url = message.url if url.is_absolute(): + if scheme is not None: + url = url.with_scheme(scheme) + if host is not None: + url = url.with_host(host) # absolute URL is given, # override auto-calculating url, host, and scheme # all other properties should be good @@ -184,6 +188,10 @@ def __init__( self._rel_url = url.relative() else: self._rel_url = message.url + if scheme is not None: + self._cache["scheme"] = scheme + if host is not None: + self._cache["host"] = host self._post: Optional[MultiDictProxy[Union[str, bytes, FileField]]] = None self._read_bytes: Optional[bytes] = None @@ -197,10 +205,6 @@ def __init__( self._transport_sslcontext = transport.get_extra_info("sslcontext") self._transport_peername = transport.get_extra_info("peername") - if scheme is not None: - self._cache["scheme"] = scheme - if host is not None: - self._cache["host"] = host if remote is not None: self._cache["remote"] = remote diff --git a/tests/test_web_request.py b/tests/test_web_request.py index c6398ac1836..ba12d6f54e7 100644 --- a/tests/test_web_request.py +++ b/tests/test_web_request.py @@ -169,6 +169,22 @@ def test_absolute_url() -> None: assert req.rel_url == URL.build(path="/path/to", query={"a": "1"}) +def test_clone_absolute_scheme() -> None: + req = make_mocked_request("GET", "https://example.com/path/to?a=1") + assert req.scheme == "https" + req2 = req.clone(scheme="http") + assert req2.scheme == "http" + assert req2.url.scheme == "http" + + +def test_clone_absolute_host() -> None: + req = make_mocked_request("GET", "https://example.com/path/to?a=1") + assert req.host == "example.com" + req2 = req.clone(host="foo.test") + assert req2.host == "foo.test" + assert req2.url.host == "foo.test" + + def test_content_length() -> None: req = make_mocked_request("Get", "/", CIMultiDict([("CONTENT-LENGTH", "123")])) @@ -684,18 +700,23 @@ def test_save_state_on_clone() -> None: def test_clone_scheme() -> None: req = make_mocked_request("GET", "/") + assert req.scheme == "http" req2 = req.clone(scheme="https") assert req2.scheme == "https" + assert req2.url.scheme == "https" def test_clone_host() -> None: req = make_mocked_request("GET", "/") + assert req.host != "example.com" req2 = req.clone(host="example.com") assert req2.host == "example.com" + assert req2.url.host == "example.com" def test_clone_remote() -> None: req = make_mocked_request("GET", "/") + assert req.remote != "11.11.11.11" req2 = req.clone(remote="11.11.11.11") assert req2.remote == "11.11.11.11" From ba201c4a89ed5f51bba55d255e22ffff8954b18a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 11:32:25 +0000 Subject: [PATCH 138/296] Bump setuptools from 74.0.0 to 74.1.0 (#9001) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [setuptools](https://github.com/pypa/setuptools) from 74.0.0 to 74.1.0.
Changelog

Sourced from setuptools's changelog.

v74.1.0

Features

  • Added support for defining ext-modules via pyproject.toml (EXPERIMENTAL, may change in future releases). (#4568)

Bugfixes

  • Merge with pypa/distutils@3dcdf8567, removing the duplicate vendored copy of packaging. (#4622)
  • Restored setuptools.msvc.Environmentinfo as it is used externally. (#4625)
Commits
  • 1a9d873 Bump version: 74.0.0 → 74.1.0
  • 4d9a750 Merge pull request #4626 from pypa/bugfix/msvc-EnvironmentInfo
  • a16582b Add a test for construction of EnvironmentInfo.
  • 616d873 Add news fragment.
  • 2423888 Remove only the monkeypatching, leaving EnvironmentInfo in place.
  • 7ecbcb0 Revert "Remove monkeypatching of _msvccompiler."
  • 5d4473e Implement declarative ext-modules in pyproject.toml ("experimental") (#4568)
  • 592d089 Add news fragment
  • 11731e2 Add docs about ext-modules in pyproject.toml
  • bf768e0 Add experimental warning to ext-modules in pyproject.toml
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=setuptools&package-manager=pip&previous-version=74.0.0&new-version=74.1.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 a2deb71956d..428cfff3d7f 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -296,7 +296,7 @@ zipp==3.20.1 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via pip-tools -setuptools==74.0.0 +setuptools==74.1.0 # via # blockdiag # incremental diff --git a/requirements/dev.txt b/requirements/dev.txt index 2e40f0bb153..55695f7eb64 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -288,7 +288,7 @@ zipp==3.20.1 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via pip-tools -setuptools==74.0.0 +setuptools==74.1.0 # via # blockdiag # incremental diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index b0b6bb6d469..4ef41521136 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -96,7 +96,7 @@ zipp==3.20.1 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -setuptools==74.0.0 +setuptools==74.1.0 # via # blockdiag # incremental diff --git a/requirements/doc.txt b/requirements/doc.txt index 172dedd5016..804cb6e129d 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -91,7 +91,7 @@ zipp==3.20.1 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -setuptools==74.0.0 +setuptools==74.1.0 # via # blockdiag # incremental From a3fa8d85ff227f18270d83af215b8974386989f0 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 3 Sep 2024 14:06:44 +0100 Subject: [PATCH 139/296] Fix cancelled payload send leading to hung connection (#8992) (#9002) (cherry picked from commit 5c0b8e4a7897f48063f684ac16e7dd18f6218274) --- CHANGES/8992.bugfix.rst | 1 + aiohttp/client_reqrep.py | 3 ++- tests/test_client_functional.py | 35 ++++++++++++++++++++------------- 3 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 CHANGES/8992.bugfix.rst diff --git a/CHANGES/8992.bugfix.rst b/CHANGES/8992.bugfix.rst new file mode 100644 index 00000000000..bc41d5feb81 --- /dev/null +++ b/CHANGES/8992.bugfix.rst @@ -0,0 +1 @@ +Fixed client incorrectly reusing a connection when the previous message had not been fully sent -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 933f3275e28..d2c5f16df2b 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -659,7 +659,8 @@ async def write_bytes( set_exception(protocol, reraised_exc, underlying_exc) except asyncio.CancelledError: - await writer.write_eof() + # Body hasn't been fully sent, so connection can't be reused. + conn.close() except Exception as underlying_exc: set_exception( protocol, diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index c7c31c739b1..95f73d84ec0 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -341,10 +341,11 @@ async def data_gen(): async with client.get("/") as resp: assert 200 == resp.status - # Connection should have been reused + # First connection should have been closed, otherwise server won't know if it + # received the full message. conns = next(iter(client.session.connector._conns.values())) assert len(conns) == 1 - assert conns[0][0] is conn + assert conns[0][0] is not conn async def test_stream_request_on_server_eof_nested(aiohttp_client) -> None: @@ -362,14 +363,21 @@ async def data_gen(): yield b"just data" await asyncio.sleep(0.1) + assert client.session.connector is not None async with client.put("/", data=data_gen()) as resp: + first_conn = next(iter(client.session.connector._acquired)) assert 200 == resp.status - async with client.get("/") as resp: - assert 200 == resp.status + + async with client.get("/") as resp2: + assert 200 == resp2.status # Should be 2 separate connections conns = next(iter(client.session.connector._conns.values())) - assert len(conns) == 2 + assert len(conns) == 1 + + assert first_conn is not None + assert not first_conn.is_connected() + assert first_conn is not conns[0][0] async def test_HTTP_304_WITH_BODY(aiohttp_client) -> None: @@ -3783,9 +3791,10 @@ async def handler(request): assert resp.reason == "x" * 8191 -@pytest.mark.xfail(raises=asyncio.TimeoutError, reason="#7599") -async def test_rejected_upload(aiohttp_client, tmp_path) -> None: - async def ok_handler(request): +async def test_rejected_upload( + aiohttp_client: AiohttpClient, tmp_path: pathlib.Path +) -> None: + async def ok_handler(request: web.Request) -> web.Response: return web.Response() async def not_ok_handler(request): @@ -3802,13 +3811,11 @@ async def not_ok_handler(request): with open(file_path, "rb") as file: data = {"file": file} - async with await client.post("/not_ok", data=data) as resp_not_ok: - assert 400 == resp_not_ok.status + async with client.post("/not_ok", data=data) as resp_not_ok: + assert resp_not_ok.status == 400 - async with await client.get( - "/ok", timeout=aiohttp.ClientTimeout(total=0.01) - ) as resp_ok: - assert 200 == resp_ok.status + async with client.get("/ok", timeout=aiohttp.ClientTimeout(total=1)) as resp_ok: + assert resp_ok.status == 200 @pytest.mark.parametrize( From fecb85a9291d4cae6c06999c388421cde02c9860 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 3 Sep 2024 14:06:55 +0100 Subject: [PATCH 140/296] Fix cancelled payload send leading to hung connection (#8992) (#9003) (cherry picked from commit 5c0b8e4a7897f48063f684ac16e7dd18f6218274) --- CHANGES/8992.bugfix.rst | 1 + aiohttp/client_reqrep.py | 3 ++- tests/test_client_functional.py | 35 ++++++++++++++++++++------------- 3 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 CHANGES/8992.bugfix.rst diff --git a/CHANGES/8992.bugfix.rst b/CHANGES/8992.bugfix.rst new file mode 100644 index 00000000000..bc41d5feb81 --- /dev/null +++ b/CHANGES/8992.bugfix.rst @@ -0,0 +1 @@ +Fixed client incorrectly reusing a connection when the previous message had not been fully sent -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 933f3275e28..d2c5f16df2b 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -659,7 +659,8 @@ async def write_bytes( set_exception(protocol, reraised_exc, underlying_exc) except asyncio.CancelledError: - await writer.write_eof() + # Body hasn't been fully sent, so connection can't be reused. + conn.close() except Exception as underlying_exc: set_exception( protocol, diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 1f9173bd3f7..7de195264ac 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -341,10 +341,11 @@ async def data_gen(): async with client.get("/") as resp: assert 200 == resp.status - # Connection should have been reused + # First connection should have been closed, otherwise server won't know if it + # received the full message. conns = next(iter(client.session.connector._conns.values())) assert len(conns) == 1 - assert conns[0][0] is conn + assert conns[0][0] is not conn async def test_stream_request_on_server_eof_nested(aiohttp_client) -> None: @@ -362,14 +363,21 @@ async def data_gen(): yield b"just data" await asyncio.sleep(0.1) + assert client.session.connector is not None async with client.put("/", data=data_gen()) as resp: + first_conn = next(iter(client.session.connector._acquired)) assert 200 == resp.status - async with client.get("/") as resp: - assert 200 == resp.status + + async with client.get("/") as resp2: + assert 200 == resp2.status # Should be 2 separate connections conns = next(iter(client.session.connector._conns.values())) - assert len(conns) == 2 + assert len(conns) == 1 + + assert first_conn is not None + assert not first_conn.is_connected() + assert first_conn is not conns[0][0] async def test_HTTP_304_WITH_BODY(aiohttp_client) -> None: @@ -3651,9 +3659,10 @@ async def handler(request): assert resp.reason == "x" * 8191 -@pytest.mark.xfail(raises=asyncio.TimeoutError, reason="#7599") -async def test_rejected_upload(aiohttp_client, tmp_path) -> None: - async def ok_handler(request): +async def test_rejected_upload( + aiohttp_client: AiohttpClient, tmp_path: pathlib.Path +) -> None: + async def ok_handler(request: web.Request) -> web.Response: return web.Response() async def not_ok_handler(request): @@ -3670,13 +3679,11 @@ async def not_ok_handler(request): with open(file_path, "rb") as file: data = {"file": file} - async with await client.post("/not_ok", data=data) as resp_not_ok: - assert 400 == resp_not_ok.status + async with client.post("/not_ok", data=data) as resp_not_ok: + assert resp_not_ok.status == 400 - async with await client.get( - "/ok", timeout=aiohttp.ClientTimeout(total=0.01) - ) as resp_ok: - assert 200 == resp_ok.status + async with client.get("/ok", timeout=aiohttp.ClientTimeout(total=1)) as resp_ok: + assert resp_ok.status == 200 @pytest.mark.parametrize( From 3ee1cd670c03b66a5ca68f627fb2320944712c68 Mon Sep 17 00:00:00 2001 From: "Justin \"J.R.\" Hill" Date: Tue, 3 Sep 2024 11:41:07 -0700 Subject: [PATCH 141/296] Add flake8-no-implicit-concat (#7731) (#9006) (cherry picked from commit 1d170d37f476df705a9dcc3588e192e8ccb871c0) --- .pre-commit-config.yaml | 1 + CHANGES/7731.misc.rst | 1 + aiohttp/client.py | 6 +-- aiohttp/client_reqrep.py | 12 +++-- aiohttp/connector.py | 6 +-- aiohttp/cookiejar.py | 2 +- aiohttp/formdata.py | 8 ++-- aiohttp/helpers.py | 10 ++--- aiohttp/http_websocket.py | 4 +- aiohttp/multipart.py | 4 +- aiohttp/streams.py | 2 +- aiohttp/test_utils.py | 2 +- aiohttp/web.py | 2 +- aiohttp/web_app.py | 6 +-- aiohttp/web_request.py | 4 +- aiohttp/web_response.py | 18 ++++---- aiohttp/web_routedef.py | 4 +- aiohttp/web_runner.py | 2 +- aiohttp/web_urldispatcher.py | 8 ++-- setup.cfg | 9 +++- tests/test_client_exceptions.py | 6 +-- tests/test_client_functional.py | 10 ++--- tests/test_client_proto.py | 2 +- tests/test_helpers.py | 3 +- tests/test_http_exceptions.py | 2 +- tests/test_http_parser.py | 62 +++++++++++++------------- tests/test_http_writer.py | 10 ++--- tests/test_multipart.py | 8 ++-- tests/test_multipart_helpers.py | 8 ++-- tests/test_urldispatch.py | 6 +-- tests/test_web_cli.py | 4 +- tests/test_web_functional.py | 8 +--- tests/test_web_request.py | 4 +- tests/test_web_websocket_functional.py | 2 +- tests/test_websocket_parser.py | 2 +- 35 files changed, 118 insertions(+), 130 deletions(-) create mode 100644 CHANGES/7731.misc.rst diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dc3e65cf52f..0edf03d8db7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -107,6 +107,7 @@ repos: - id: flake8 additional_dependencies: - flake8-docstrings==1.6.0 + - flake8-no-implicit-concat==0.3.4 - flake8-requirements==1.7.8 exclude: "^docs/" - repo: https://github.com/Lucas-C/pre-commit-hooks-markup diff --git a/CHANGES/7731.misc.rst b/CHANGES/7731.misc.rst new file mode 100644 index 00000000000..f46ffa5816b --- /dev/null +++ b/CHANGES/7731.misc.rst @@ -0,0 +1 @@ +Added flake8 settings to avoid some forms of implicit concatenation. -- by :user:`booniepepper`. diff --git a/aiohttp/client.py b/aiohttp/client.py index f3c60d31f08..3c4a0f97c04 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -310,7 +310,7 @@ def __init__( self._timeout = DEFAULT_TIMEOUT if read_timeout is not sentinel: warnings.warn( - "read_timeout is deprecated, " "use timeout argument instead", + "read_timeout is deprecated, use timeout argument instead", DeprecationWarning, stacklevel=2, ) @@ -318,7 +318,7 @@ def __init__( if conn_timeout is not None: self._timeout = attr.evolve(self._timeout, connect=conn_timeout) warnings.warn( - "conn_timeout is deprecated, " "use timeout argument instead", + "conn_timeout is deprecated, use timeout argument instead", DeprecationWarning, stacklevel=2, ) @@ -1255,7 +1255,7 @@ def requote_redirect_url(self) -> bool: def requote_redirect_url(self, val: bool) -> None: """Do URL requoting on redirection handling.""" warnings.warn( - "session.requote_redirect_url modification " "is deprecated #2778", + "session.requote_redirect_url modification is deprecated #2778", DeprecationWarning, stacklevel=2, ) diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index d2c5f16df2b..d7d5f63ec18 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -127,9 +127,7 @@ def __init__(self, fingerprint: bytes) -> None: if not hashfunc: raise ValueError("fingerprint has invalid length") elif hashfunc is md5 or hashfunc is sha1: - raise ValueError( - "md5 and sha1 are insecure and " "not supported. Use sha256." - ) + raise ValueError("md5 and sha1 are insecure and not supported. Use sha256.") self._hashfunc = hashfunc self._fingerprint = fingerprint @@ -190,7 +188,7 @@ def _merge_ssl_params( ssl = ssl_context if fingerprint is not None: warnings.warn( - "fingerprint is deprecated, " "use ssl=Fingerprint(fingerprint) instead", + "fingerprint is deprecated, use ssl=Fingerprint(fingerprint) instead", DeprecationWarning, stacklevel=3, ) @@ -505,7 +503,7 @@ def update_content_encoding(self, data: Any) -> None: if enc: if self.compress: raise ValueError( - "compress can not be set " "if Content-Encoding header is set" + "compress can not be set if Content-Encoding header is set" ) elif self.compress: if not isinstance(self.compress, str): @@ -527,7 +525,7 @@ def update_transfer_encoding(self) -> None: elif self.chunked: if hdrs.CONTENT_LENGTH in self.headers: raise ValueError( - "chunked can not be set " "if Content-Length header is set" + "chunked can not be set if Content-Length header is set" ) self.headers[hdrs.TRANSFER_ENCODING] = "chunked" @@ -1205,7 +1203,7 @@ async def json( self.history, status=self.status, message=( - "Attempt to decode JSON with " "unexpected mimetype: %s" % ctype + "Attempt to decode JSON with unexpected mimetype: %s" % ctype ), headers=self.headers, ) diff --git a/aiohttp/connector.py b/aiohttp/connector.py index 93c78c62b08..360eabc7bb2 100644 --- a/aiohttp/connector.py +++ b/aiohttp/connector.py @@ -243,7 +243,7 @@ def __init__( if force_close: if keepalive_timeout is not None and keepalive_timeout is not sentinel: raise ValueError( - "keepalive_timeout cannot " "be set if force_close is True" + "keepalive_timeout cannot be set if force_close is True" ) else: if keepalive_timeout is sentinel: @@ -853,7 +853,7 @@ def clear_dns_cache( if host is not None and port is not None: self._cached_hosts.remove((host, port)) elif host is not None or port is not None: - raise ValueError("either both host and port " "or none of them are allowed") + raise ValueError("either both host and port or none of them are allowed") else: self._cached_hosts.clear() @@ -1575,7 +1575,7 @@ def __init__( self._loop, asyncio.ProactorEventLoop # type: ignore[attr-defined] ): raise RuntimeError( - "Named Pipes only available in proactor " "loop under windows" + "Named Pipes only available in proactor loop under windows" ) self._path = path diff --git a/aiohttp/cookiejar.py b/aiohttp/cookiejar.py index e3eefc9c656..c57604b5e59 100644 --- a/aiohttp/cookiejar.py +++ b/aiohttp/cookiejar.py @@ -54,7 +54,7 @@ class CookieJar(AbstractCookieJar): DATE_DAY_OF_MONTH_RE = re.compile(r"(\d{1,2})") DATE_MONTH_RE = re.compile( - "(jan)|(feb)|(mar)|(apr)|(may)|(jun)|(jul)|" "(aug)|(sep)|(oct)|(nov)|(dec)", + "(jan)|(feb)|(mar)|(apr)|(may)|(jun)|(jul)|(aug)|(sep)|(oct)|(nov)|(dec)", re.I, ) diff --git a/aiohttp/formdata.py b/aiohttp/formdata.py index 2b75b3de72c..39ca8539acc 100644 --- a/aiohttp/formdata.py +++ b/aiohttp/formdata.py @@ -64,9 +64,7 @@ def add_field( type_options: MultiDict[str] = MultiDict({"name": name}) if filename is not None and not isinstance(filename, str): - raise TypeError( - "filename must be an instance of str. " "Got: %s" % filename - ) + raise TypeError("filename must be an instance of str. Got: %s" % filename) if filename is None and isinstance(value, io.IOBase): filename = guess_filename(value, name) if filename is not None: @@ -77,7 +75,7 @@ def add_field( if content_type is not None: if not isinstance(content_type, str): raise TypeError( - "content_type must be an instance of str. " "Got: %s" % content_type + "content_type must be an instance of str. Got: %s" % content_type ) headers[hdrs.CONTENT_TYPE] = content_type self._is_multipart = True @@ -131,7 +129,7 @@ def _gen_form_urlencoded(self) -> payload.BytesPayload: if charset == "utf-8": content_type = "application/x-www-form-urlencoded" else: - content_type = "application/x-www-form-urlencoded; " "charset=%s" % charset + content_type = "application/x-www-form-urlencoded; charset=%s" % charset return payload.BytesPayload( urlencode(data, doseq=True, encoding=charset).encode(), diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index 0327d31d961..bf9e135bb3c 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -393,16 +393,14 @@ def content_disposition_header( params is a dict with disposition params. """ if not disptype or not (TOKEN > set(disptype)): - raise ValueError("bad content disposition type {!r}" "".format(disptype)) + raise ValueError(f"bad content disposition type {disptype!r}") value = disptype if params: lparams = [] for key, val in params.items(): if not key or not (TOKEN > set(key)): - raise ValueError( - "bad content disposition parameter" " {!r}={!r}".format(key, val) - ) + raise ValueError(f"bad content disposition parameter {key!r}={val!r}") if quote_fields: if key.lower() == "filename": qval = quote(val, "", encoding=_charset) @@ -690,9 +688,7 @@ def __enter__(self) -> BaseTimerContext: task = asyncio.current_task(loop=self._loop) if task is None: - raise RuntimeError( - "Timeout context manager should be used " "inside a task" - ) + raise RuntimeError("Timeout context manager should be used inside a task") if self._cancelled: raise asyncio.TimeoutError from None diff --git a/aiohttp/http_websocket.py b/aiohttp/http_websocket.py index 2ea2c9191e1..9d03d2773c7 100644 --- a/aiohttp/http_websocket.py +++ b/aiohttp/http_websocket.py @@ -267,7 +267,7 @@ def ws_ext_gen( # compress wbit 8 does not support in zlib if compress < 9 or compress > 15: raise ValueError( - "Compress wbits must between 9 and 15, " "zlib does not support wbits=8" + "Compress wbits must between 9 and 15, zlib does not support wbits=8" ) enabledext = ["permessage-deflate"] if not isserver: @@ -512,7 +512,7 @@ def parse_frame( if opcode > 0x7 and length > 125: raise WebSocketError( WSCloseCode.PROTOCOL_ERROR, - "Control frame payload cannot be " "larger than 125 bytes", + "Control frame payload cannot be larger than 125 bytes", ) # Set compress status if last package is FIN diff --git a/aiohttp/multipart.py b/aiohttp/multipart.py index 965e4f279d3..e0bcce07449 100644 --- a/aiohttp/multipart.py +++ b/aiohttp/multipart.py @@ -530,9 +530,7 @@ def _decode_content_transfer(self, data: bytes) -> bytes: elif encoding in ("binary", "8bit", "7bit"): return data else: - raise RuntimeError( - "unknown content transfer encoding: {}" "".format(encoding) - ) + raise RuntimeError(f"unknown content transfer encoding: {encoding}") def get_charset(self, default: str) -> str: """Returns charset parameter from Content-Type header or default.""" diff --git a/aiohttp/streams.py b/aiohttp/streams.py index c927cfbb1b3..1ed78ce5db0 100644 --- a/aiohttp/streams.py +++ b/aiohttp/streams.py @@ -261,7 +261,7 @@ def begin_http_chunk_receiving(self) -> None: if self._http_chunk_splits is None: if self.total_bytes: raise RuntimeError( - "Called begin_http_chunk_receiving when" "some data was already fed" + "Called begin_http_chunk_receiving when some data was already fed" ) self._http_chunk_splits = [] diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py index 328561fb6a7..13b6f4d9c50 100644 --- a/aiohttp/test_utils.py +++ b/aiohttp/test_utils.py @@ -266,7 +266,7 @@ def __init__( ) -> None: if not isinstance(server, BaseTestServer): raise TypeError( - "server must be TestServer " "instance, found type: %r" % type(server) + "server must be TestServer instance, found type: %r" % type(server) ) self._server = server self._loop = loop diff --git a/aiohttp/web.py b/aiohttp/web.py index 88bf14bf828..1d18691f401 100644 --- a/aiohttp/web.py +++ b/aiohttp/web.py @@ -581,7 +581,7 @@ def main(argv: List[str]) -> None: # Compatibility logic if args.path is not None and not hasattr(socket, "AF_UNIX"): arg_parser.error( - "file system paths not supported by your operating" " environment" + "file system paths not supported by your operating environment" ) logging.basicConfig(level=logging.DEBUG) diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index 3510bffda60..8403bbbc826 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -195,7 +195,7 @@ def __getitem__(self, key: Union[str, AppKey[_T]]) -> Any: def _check_frozen(self) -> None: if self._frozen: warnings.warn( - "Changing state of started or joined " "application is deprecated", + "Changing state of started or joined application is deprecated", DeprecationWarning, stacklevel=3, ) @@ -433,7 +433,7 @@ def make_handler( ) -> Server: warnings.warn( - "Application.make_handler(...) is deprecated, " "use AppRunner API instead", + "Application.make_handler(...) is deprecated, use AppRunner API instead", DeprecationWarning, stacklevel=2, ) @@ -492,7 +492,7 @@ def _prepare_middleware(self) -> Iterator[Tuple[Middleware, bool]]: yield m, True else: warnings.warn( - 'old-style middleware "{!r}" deprecated, ' "see #2252".format(m), + f'old-style middleware "{m!r}" deprecated, see #2252', DeprecationWarning, stacklevel=2, ) diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py index 1d94c576794..2465e6655ad 100644 --- a/aiohttp/web_request.py +++ b/aiohttp/web_request.py @@ -226,7 +226,7 @@ def clone( will reuse the one from the current request object. """ if self._read_bytes: - raise RuntimeError("Cannot clone request " "after reading its content") + raise RuntimeError("Cannot clone request after reading its content") dct: Dict[str, Any] = {} if method is not sentinel: @@ -773,7 +773,7 @@ async def post(self) -> "MultiDictProxy[Union[str, bytes, FileField]]": ) else: raise ValueError( - "To decode nested multipart you need " "to use custom reader", + "To decode nested multipart you need to use custom reader", ) field = await multipart.next() diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 0020afd46c8..24ea9f5b46b 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -134,9 +134,9 @@ def set_status( status: int, reason: Optional[str] = None, ) -> None: - assert not self.prepared, ( - "Cannot change the response status code after " "the headers have been sent" - ) + assert ( + not self.prepared + ), "Cannot change the response status code after the headers have been sent" self._status = int(status) if reason is None: try: @@ -168,7 +168,7 @@ def enable_chunked_encoding(self, chunk_size: Optional[int] = None) -> None: if hdrs.CONTENT_LENGTH in self._headers: raise RuntimeError( - "You can't enable chunked encoding when " "a content length is set" + "You can't enable chunked encoding when a content length is set" ) if chunk_size is not None: warnings.warn("Chunk size is deprecated #1615", DeprecationWarning) @@ -184,9 +184,9 @@ def enable_compression( "Using boolean for force is deprecated #3318", DeprecationWarning ) elif force is not None: - assert isinstance(force, ContentCoding), ( - "force should one of " "None, bool or " "ContentEncoding" - ) + assert isinstance( + force, ContentCoding + ), "force should one of None, bool or ContentEncoding" self._compression = True self._compression_force = force @@ -289,7 +289,7 @@ def content_length(self, value: Optional[int]) -> None: value = int(value) if self._chunked: raise RuntimeError( - "You can't set content length when " "chunked encoding is enable" + "You can't set content length when chunked encoding is enable" ) self._headers[hdrs.CONTENT_LENGTH] = str(value) else: @@ -611,7 +611,7 @@ def __init__( real_headers = headers # = cast('CIMultiDict[str]', headers) if content_type is not None and "charset" in content_type: - raise ValueError("charset must not be in content_type " "argument") + raise ValueError("charset must not be in content_type argument") if text is not None: if hdrs.CONTENT_TYPE in real_headers: diff --git a/aiohttp/web_routedef.py b/aiohttp/web_routedef.py index 93802141c56..f51b6cd0081 100644 --- a/aiohttp/web_routedef.py +++ b/aiohttp/web_routedef.py @@ -66,7 +66,7 @@ def __repr__(self) -> str: info = [] for name, value in sorted(self.kwargs.items()): info.append(f", {name}={value!r}") - return " {handler.__name__!r}" "{info}>".format( + return " {handler.__name__!r}{info}>".format( method=self.method, path=self.path, handler=self.handler, info="".join(info) ) @@ -90,7 +90,7 @@ def __repr__(self) -> str: info = [] for name, value in sorted(self.kwargs.items()): info.append(f", {name}={value!r}") - return " {path}" "{info}>".format( + return " {path}{info}>".format( prefix=self.prefix, path=self.path, info="".join(info) ) diff --git a/aiohttp/web_runner.py b/aiohttp/web_runner.py index 0a237ede2c5..f8933383435 100644 --- a/aiohttp/web_runner.py +++ b/aiohttp/web_runner.py @@ -176,7 +176,7 @@ def __init__( loop, asyncio.ProactorEventLoop # type: ignore[attr-defined] ): raise RuntimeError( - "Named Pipes only available in proactor" "loop under windows" + "Named Pipes only available in proactor loop under windows" ) super().__init__(runner, shutdown_timeout=shutdown_timeout) self._path = path diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index a1df64b8e61..765f8500c0e 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -194,14 +194,14 @@ def __init__( pass elif inspect.isgeneratorfunction(handler): warnings.warn( - "Bare generators are deprecated, " "use @coroutine wrapper", + "Bare generators are deprecated, use @coroutine wrapper", DeprecationWarning, ) elif isinstance(handler, type) and issubclass(handler, AbstractView): pass else: warnings.warn( - "Bare functions are deprecated, " "use async ones", DeprecationWarning + "Bare functions are deprecated, use async ones", DeprecationWarning ) @wraps(handler) @@ -777,7 +777,7 @@ def _add_prefix_to_resources(self, prefix: str) -> None: router.index_resource(resource) def url_for(self, *args: str, **kwargs: str) -> URL: - raise RuntimeError(".url_for() is not supported " "by sub-application root") + raise RuntimeError(".url_for() is not supported by sub-application root") def get_info(self) -> _InfoDict: return {"app": self._app, "prefix": self._prefix} @@ -900,7 +900,7 @@ async def resolve(self, request: Request) -> _Resolve: return match_info, methods def __repr__(self) -> str: - return " {app!r}>" "".format(app=self._app) + return f" {self._app!r}>" class ResourceRoute(AbstractRoute): diff --git a/setup.cfg b/setup.cfg index c058fc2f05f..cd1602880e6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -86,9 +86,14 @@ max-line-length=79 zip_ok = false [flake8] -extend-select = B950 +extend-select = + B950, + # NIC001 -- "Implicitly concatenated str literals on one line" + NIC001, + # NIC101 -- "Implicitly concatenated bytes literals on one line" + NIC101, # TODO: don't disable D*, fix up issues instead -ignore = N801,N802,N803,E203,E226,E305,W504,E252,E301,E302,E501,E704,W503,W504,D1,D4 +ignore = N801,N802,N803,NIC002,NIC102,E203,E226,E305,W504,E252,E301,E302,E501,E704,W503,W504,D1,D4 max-line-length = 88 per-file-ignores = # I900: Shouldn't appear in requirements for examples. diff --git a/tests/test_client_exceptions.py b/tests/test_client_exceptions.py index 85e71a3508b..f14cfefde51 100644 --- a/tests/test_client_exceptions.py +++ b/tests/test_client_exceptions.py @@ -83,9 +83,7 @@ def test_str(self) -> None: message="Something wrong", headers=CIMultiDict(), ) - assert str(err) == ( - "400, message='Something wrong', " "url='http://example.com'" - ) + assert str(err) == ("400, message='Something wrong', url='http://example.com'") def test_response_status() -> None: @@ -254,7 +252,7 @@ def test_pickle(self) -> None: def test_repr(self) -> None: err = client.ServerDisconnectedError() - assert repr(err) == ("ServerDisconnectedError" "('Server disconnected')") + assert repr(err) == ("ServerDisconnectedError('Server disconnected')") err = client.ServerDisconnectedError(message="No connection") assert repr(err) == "ServerDisconnectedError('No connection')" diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 95f73d84ec0..74c4d99765e 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -2361,7 +2361,7 @@ async def handler(request): ret.set_cookie("c2", "cookie2") ret.headers.add( "Set-Cookie", - "c3=cookie3; " "HttpOnly; Path=/" " Expires=Tue, 1 Jan 1980 12:00:00 GMT; ", + "c3=cookie3; HttpOnly; Path=/ Expires=Tue, 1 Jan 1980 12:00:00 GMT; ", ) return ret @@ -2380,7 +2380,7 @@ async def handler(request): ret = web.Response() ret.set_cookie("c1", "cookie1") ret.set_cookie("c2", "cookie2") - ret.headers.add("Set-Cookie", "c3=cookie3; " "HttpOnly; Path=/" " Max-Age=1; ") + ret.headers.add("Set-Cookie", "c3=cookie3; HttpOnly; Path=/ Max-Age=1; ") return ret app = web.Application() @@ -2401,7 +2401,7 @@ async def handler(request): ret = web.Response() ret.headers.add( "Set-Cookie", - "overflow=overflow; " "HttpOnly; Path=/" " Max-Age=" + str(overflow) + "; ", + "overflow=overflow; HttpOnly; Path=/ Max-Age=" + str(overflow) + "; ", ) return ret @@ -3379,9 +3379,7 @@ def connection_made(self, transport): def data_received(self, data): self.data += data if data.endswith(b"\r\n\r\n"): - self.transp.write( - b"HTTP/1.1 200 OK\r\n" b"CONTENT-LENGTH: 2\r\n" b"\r\n" b"ok" - ) + self.transp.write(b"HTTP/1.1 200 OK\r\nCONTENT-LENGTH: 2\r\n\r\nok") self.transp.close() def connection_lost(self, exc): diff --git a/tests/test_client_proto.py b/tests/test_client_proto.py index d8ffac0059c..ba45d6a6839 100644 --- a/tests/test_client_proto.py +++ b/tests/test_client_proto.py @@ -50,7 +50,7 @@ async def test_uncompleted_message(loop) -> None: proto.set_response_params(read_until_eof=True) proto.data_received( - b"HTTP/1.1 301 Moved Permanently\r\n" b"Location: http://python.org/" + b"HTTP/1.1 301 Moved Permanently\r\nLocation: http://python.org/" ) proto.connection_lost(None) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 827a417c299..656364f43aa 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -325,7 +325,8 @@ def test_ipv6_addresses() -> None: def test_host_addresses() -> None: hosts = [ - "www.four.part.host" "www.python.org", + "www.four.part.host", + "www.python.org", "foo.bar", "localhost", ] diff --git a/tests/test_http_exceptions.py b/tests/test_http_exceptions.py index 24944d9fc4e..cd3b08f59db 100644 --- a/tests/test_http_exceptions.py +++ b/tests/test_http_exceptions.py @@ -81,7 +81,7 @@ def test_pickle(self) -> None: pickled = pickle.dumps(err, proto) err2 = pickle.loads(pickled) assert err2.code == 400 - assert err2.message == ("Got more than 10 bytes (12) " "when reading spam.") + assert err2.message == ("Got more than 10 bytes (12) when reading spam.") assert err2.headers is None assert err2.foo == "bar" diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py index 78abe528cb0..75276df1a07 100644 --- a/tests/test_http_parser.py +++ b/tests/test_http_parser.py @@ -443,49 +443,49 @@ def test_conn_default_1_1(parser) -> None: def test_conn_close(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"connection: close\r\n\r\n" + text = b"GET /test HTTP/1.1\r\nconnection: close\r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg = messages[0][0] assert msg.should_close def test_conn_close_1_0(parser) -> None: - text = b"GET /test HTTP/1.0\r\n" b"connection: close\r\n\r\n" + text = b"GET /test HTTP/1.0\r\nconnection: close\r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg = messages[0][0] assert msg.should_close def test_conn_keep_alive_1_0(parser) -> None: - text = b"GET /test HTTP/1.0\r\n" b"connection: keep-alive\r\n\r\n" + text = b"GET /test HTTP/1.0\r\nconnection: keep-alive\r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg = messages[0][0] assert not msg.should_close def test_conn_keep_alive_1_1(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"connection: keep-alive\r\n\r\n" + text = b"GET /test HTTP/1.1\r\nconnection: keep-alive\r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg = messages[0][0] assert not msg.should_close def test_conn_other_1_0(parser) -> None: - text = b"GET /test HTTP/1.0\r\n" b"connection: test\r\n\r\n" + text = b"GET /test HTTP/1.0\r\nconnection: test\r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg = messages[0][0] assert msg.should_close def test_conn_other_1_1(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"connection: test\r\n\r\n" + text = b"GET /test HTTP/1.1\r\nconnection: test\r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg = messages[0][0] assert not msg.should_close def test_request_chunked(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"transfer-encoding: chunked\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ntransfer-encoding: chunked\r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg, payload = messages[0] assert msg.chunked @@ -507,7 +507,7 @@ def test_request_te_chunked_with_content_length(parser: Any) -> None: def test_request_te_chunked123(parser: Any) -> None: - text = b"GET /test HTTP/1.1\r\n" b"transfer-encoding: chunked123\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ntransfer-encoding: chunked123\r\n\r\n" with pytest.raises( http_exceptions.BadHttpMessage, match="Request has invalid `Transfer-Encoding`", @@ -555,21 +555,21 @@ def test_bad_upgrade(parser) -> None: def test_compression_empty(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"content-encoding: \r\n\r\n" + text = b"GET /test HTTP/1.1\r\ncontent-encoding: \r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg = messages[0][0] assert msg.compression is None def test_compression_deflate(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"content-encoding: deflate\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ncontent-encoding: deflate\r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg = messages[0][0] assert msg.compression == "deflate" def test_compression_gzip(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"content-encoding: gzip\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ncontent-encoding: gzip\r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg = messages[0][0] assert msg.compression == "gzip" @@ -577,21 +577,21 @@ def test_compression_gzip(parser) -> None: @pytest.mark.skipif(brotli is None, reason="brotli is not installed") def test_compression_brotli(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"content-encoding: br\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ncontent-encoding: br\r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg = messages[0][0] assert msg.compression == "br" def test_compression_unknown(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"content-encoding: compress\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ncontent-encoding: compress\r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg = messages[0][0] assert msg.compression is None def test_url_connect(parser: Any) -> None: - text = b"CONNECT www.google.com HTTP/1.1\r\n" b"content-length: 0\r\n\r\n" + text = b"CONNECT www.google.com HTTP/1.1\r\ncontent-length: 0\r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg, payload = messages[0] assert upgrade @@ -599,7 +599,7 @@ def test_url_connect(parser: Any) -> None: def test_headers_connect(parser: Any) -> None: - text = b"CONNECT www.google.com HTTP/1.1\r\n" b"content-length: 0\r\n\r\n" + text = b"CONNECT www.google.com HTTP/1.1\r\ncontent-length: 0\r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg, payload = messages[0] assert upgrade @@ -619,21 +619,21 @@ def test_url_absolute(parser: Any) -> None: def test_headers_old_websocket_key1(parser: Any) -> None: - text = b"GET /test HTTP/1.1\r\n" b"SEC-WEBSOCKET-KEY1: line\r\n\r\n" + text = b"GET /test HTTP/1.1\r\nSEC-WEBSOCKET-KEY1: line\r\n\r\n" with pytest.raises(http_exceptions.BadHttpMessage): parser.feed_data(text) def test_headers_content_length_err_1(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"content-length: line\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ncontent-length: line\r\n\r\n" with pytest.raises(http_exceptions.BadHttpMessage): parser.feed_data(text) def test_headers_content_length_err_2(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"content-length: -1\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ncontent-length: -1\r\n\r\n" with pytest.raises(http_exceptions.BadHttpMessage): parser.feed_data(text) @@ -656,7 +656,7 @@ def test_headers_content_length_err_2(parser) -> None: @pytest.mark.parametrize("pad2", _pad.keys(), ids=["post-" + n for n in _pad.values()]) @pytest.mark.parametrize("pad1", _pad.keys(), ids=["pre-" + n for n in _pad.values()]) def test_invalid_header_spacing(parser, pad1: bytes, pad2: bytes, hdr: bytes) -> None: - text = b"GET /test HTTP/1.1\r\n" b"%s%s%s: value\r\n\r\n" % (pad1, hdr, pad2) + text = b"GET /test HTTP/1.1\r\n%s%s%s: value\r\n\r\n" % (pad1, hdr, pad2) expectation = pytest.raises(http_exceptions.BadHttpMessage) if pad1 == pad2 == b"" and hdr != b"": # one entry in param matrix is correct: non-empty name, not padded @@ -666,19 +666,19 @@ def test_invalid_header_spacing(parser, pad1: bytes, pad2: bytes, hdr: bytes) -> def test_empty_header_name(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b":test\r\n\r\n" + text = b"GET /test HTTP/1.1\r\n:test\r\n\r\n" with pytest.raises(http_exceptions.BadHttpMessage): parser.feed_data(text) def test_invalid_header(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"test line\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ntest line\r\n\r\n" with pytest.raises(http_exceptions.BadHttpMessage): parser.feed_data(text) def test_invalid_name(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"test[]: line\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ntest[]: line\r\n\r\n" with pytest.raises(http_exceptions.BadHttpMessage): parser.feed_data(text) @@ -715,7 +715,7 @@ def test_max_header_field_size_under_limit(parser) -> None: @pytest.mark.parametrize("size", [40960, 8191]) def test_max_header_value_size(parser, size) -> None: name = b"t" * size - text = b"GET /test HTTP/1.1\r\n" b"data:" + name + b"\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ndata:" + name + b"\r\n\r\n" match = f"400, message:\n Got more than 8190 bytes \\({size}\\) when reading" with pytest.raises(http_exceptions.LineTooLong, match=match): @@ -724,7 +724,7 @@ def test_max_header_value_size(parser, size) -> None: def test_max_header_value_size_under_limit(parser) -> None: value = b"A" * 8190 - text = b"GET /test HTTP/1.1\r\n" b"data:" + value + b"\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ndata:" + value + b"\r\n\r\n" messages, upgrade, tail = parser.feed_data(text) msg = messages[0][0] @@ -1216,7 +1216,7 @@ def test_http_response_parser_code_not_ascii(response, nonascii_digit: bytes) -> def test_http_request_chunked_payload(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"transfer-encoding: chunked\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ntransfer-encoding: chunked\r\n\r\n" msg, payload = parser.feed_data(text)[0][0] assert msg.chunked @@ -1231,7 +1231,7 @@ def test_http_request_chunked_payload(parser) -> None: def test_http_request_chunked_payload_and_next_message(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"transfer-encoding: chunked\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ntransfer-encoding: chunked\r\n\r\n" msg, payload = parser.feed_data(text)[0][0] messages, upgraded, tail = parser.feed_data( @@ -1253,7 +1253,7 @@ def test_http_request_chunked_payload_and_next_message(parser) -> None: def test_http_request_chunked_payload_chunks(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"transfer-encoding: chunked\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ntransfer-encoding: chunked\r\n\r\n" msg, payload = parser.feed_data(text)[0][0] parser.feed_data(b"4\r\ndata\r") @@ -1275,7 +1275,7 @@ def test_http_request_chunked_payload_chunks(parser) -> None: def test_parse_chunked_payload_chunk_extension(parser) -> None: - text = b"GET /test HTTP/1.1\r\n" b"transfer-encoding: chunked\r\n\r\n" + text = b"GET /test HTTP/1.1\r\ntransfer-encoding: chunked\r\n\r\n" msg, payload = parser.feed_data(text)[0][0] parser.feed_data(b"4;test\r\ndata\r\n4\r\nline\r\n0\r\ntest: test\r\n\r\n") @@ -1295,14 +1295,14 @@ def test_parse_no_length_or_te_on_post(loop: Any, protocol: Any, request_cls: An def test_parse_payload_response_without_body(loop, protocol, response_cls) -> None: parser = response_cls(protocol, loop, 2**16, response_with_body=False) - text = b"HTTP/1.1 200 Ok\r\n" b"content-length: 10\r\n\r\n" + text = b"HTTP/1.1 200 Ok\r\ncontent-length: 10\r\n\r\n" msg, payload = parser.feed_data(text)[0][0] assert payload.is_eof() def test_parse_length_payload(response) -> None: - text = b"HTTP/1.1 200 Ok\r\n" b"content-length: 4\r\n\r\n" + text = b"HTTP/1.1 200 Ok\r\ncontent-length: 4\r\n\r\n" msg, payload = response.feed_data(text)[0][0] assert not payload.is_eof() @@ -1627,7 +1627,7 @@ async def test_parse_chunked_payload_split_end_trailers3(self, protocol) -> None async def test_parse_chunked_payload_split_end_trailers4(self, protocol) -> None: out = aiohttp.StreamReader(protocol, 2**16, loop=None) p = HttpPayloadParser(out, chunked=True) - p.feed_data(b"4\r\nasdf\r\n0\r\n" b"C") + p.feed_data(b"4\r\nasdf\r\n0\r\nC") p.feed_data(b"ontent-MD5: 912ec803b2ce49e4a541068d495ab570\r\n\r\n") assert out.is_eof() diff --git a/tests/test_http_writer.py b/tests/test_http_writer.py index 5649f32f792..db50ad65f67 100644 --- a/tests/test_http_writer.py +++ b/tests/test_http_writer.py @@ -108,7 +108,7 @@ async def test_write_payload_chunked_filter_mutiple_chunks(protocol, transport, await msg.write_eof() content = b"".join([c[1][0] for c in list(write.mock_calls)]) assert content.endswith( - b"2\r\nda\r\n2\r\nta\r\n2\r\n1d\r\n2\r\nat\r\n" b"2\r\na2\r\n0\r\n\r\n" + b"2\r\nda\r\n2\r\nta\r\n2\r\n1d\r\n2\r\nat\r\n2\r\na2\r\n0\r\n\r\n" ) @@ -136,7 +136,7 @@ async def test_write_payload_deflate_and_chunked(buf, protocol, transport, loop) await msg.write(b"ta") await msg.write_eof() - thing = b"2\r\nx\x9c\r\n" b"a\r\nKI,I\x04\x00\x04\x00\x01\x9b\r\n" b"0\r\n\r\n" + thing = b"2\r\nx\x9c\r\na\r\nKI,I\x04\x00\x04\x00\x01\x9b\r\n0\r\n\r\n" assert thing == buf @@ -163,8 +163,8 @@ async def test_write_payload_short_ints_memoryview(buf, protocol, transport, loo await msg.write_eof() endians = ( - (b"6\r\n" b"\x00A\x00B\x00C\r\n" b"0\r\n\r\n"), - (b"6\r\n" b"A\x00B\x00C\x00\r\n" b"0\r\n\r\n"), + (b"6\r\n\x00A\x00B\x00C\r\n0\r\n\r\n"), + (b"6\r\nA\x00B\x00C\x00\r\n0\r\n\r\n"), ) assert buf in endians @@ -179,7 +179,7 @@ async def test_write_payload_2d_shape_memoryview(buf, protocol, transport, loop) await msg.write(payload) await msg.write_eof() - thing = b"6\r\n" b"ABCDEF\r\n" b"0\r\n\r\n" + thing = b"6\r\nABCDEF\r\n0\r\n\r\n" assert thing == buf diff --git a/tests/test_multipart.py b/tests/test_multipart.py index bbbc1c666ca..8576998962e 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -463,7 +463,7 @@ async def test_read_text_guess_encoding(self) -> None: assert data == result async def test_read_text_compressed(self) -> None: - data = b"\x0b\xc9\xccMU(\xc9W\x08J\xcdI\xacP\x04\x00" b"%s--:--" % newline + data = b"\x0b\xc9\xccMU(\xc9W\x08J\xcdI\xacP\x04\x00%s--:--" % newline with Stream(data) as stream: obj = aiohttp.BodyPartReader( BOUNDARY, @@ -515,9 +515,7 @@ async def test_read_json_guess_encoding(self) -> None: assert {"тест": "пассед"} == result async def test_read_json_compressed(self) -> None: - with Stream( - b"\xabV*I-.Q\xb2RP*H,.NMQ\xaa\x05\x00" b"%s--:--" % newline - ) as stream: + with Stream(b"\xabV*I-.Q\xb2RP*H,.NMQ\xaa\x05\x00%s--:--" % newline) as stream: obj = aiohttp.BodyPartReader( BOUNDARY, {CONTENT_ENCODING: "deflate", CONTENT_TYPE: "application/json"}, @@ -712,7 +710,7 @@ def test_dispatch_multipart(self) -> None: b"----:--", b"", b"passed", - b"----:----" b"--:--", + b"----:------:--", ] ) ) as stream: diff --git a/tests/test_multipart_helpers.py b/tests/test_multipart_helpers.py index 9516751cba9..d4fb610a22c 100644 --- a/tests/test_multipart_helpers.py +++ b/tests/test_multipart_helpers.py @@ -555,10 +555,10 @@ def test_attfncontqs(self) -> None: def test_attfncontenc(self) -> None: disptype, params = parse_content_disposition( - "attachment; filename*0*=UTF-8" 'foo-%c3%a4; filename*1=".html"' + "attachment; filename*0*=UTF-8" + 'foo-%c3%a4; filename*1=".html"' ) assert "attachment" == disptype - assert {"filename*0*": "UTF-8" "foo-%c3%a4", "filename*1": ".html"} == params + assert {"filename*0*": "UTF-8foo-%c3%a4", "filename*1": ".html"} == params def test_attfncontlz(self) -> None: disptype, params = parse_content_disposition( @@ -590,14 +590,14 @@ def test_attfncontord(self) -> None: def test_attfnboth(self) -> None: disptype, params = parse_content_disposition( - 'attachment; filename="foo-ae.html";' " filename*=UTF-8''foo-%c3%a4.html" + 'attachment; filename="foo-ae.html";' + " filename*=UTF-8''foo-%c3%a4.html" ) assert "attachment" == disptype assert {"filename": "foo-ae.html", "filename*": "foo-ä.html"} == params def test_attfnboth2(self) -> None: disptype, params = parse_content_disposition( - "attachment; filename*=UTF-8''foo-%c3%a4.html;" ' filename="foo-ae.html"' + "attachment; filename*=UTF-8''foo-%c3%a4.html;" + ' filename="foo-ae.html"' ) assert "attachment" == disptype assert {"filename": "foo-ae.html", "filename*": "foo-ä.html"} == params diff --git a/tests/test_urldispatch.py b/tests/test_urldispatch.py index f06f73edc21..d0efa91593e 100644 --- a/tests/test_urldispatch.py +++ b/tests/test_urldispatch.py @@ -368,7 +368,7 @@ def test_add_static_append_version(router) -> None: resource = router.add_static("/st", pathlib.Path(__file__).parent, name="static") url = resource.url_for(filename="/data.unknown_mime_type", append_version=True) expect_url = ( - "/st/data.unknown_mime_type?" "v=aUsn8CHEhhszc81d28QmlcBW0KQpfS2F4trgQKhOYd8%3D" + "/st/data.unknown_mime_type?v=aUsn8CHEhhszc81d28QmlcBW0KQpfS2F4trgQKhOYd8%3D" ) assert expect_url == str(url) @@ -379,7 +379,7 @@ def test_add_static_append_version_set_from_constructor(router) -> None: ) url = resource.url_for(filename="/data.unknown_mime_type") expect_url = ( - "/st/data.unknown_mime_type?" "v=aUsn8CHEhhszc81d28QmlcBW0KQpfS2F4trgQKhOYd8%3D" + "/st/data.unknown_mime_type?v=aUsn8CHEhhszc81d28QmlcBW0KQpfS2F4trgQKhOYd8%3D" ) assert expect_url == str(url) @@ -397,7 +397,7 @@ def test_add_static_append_version_filename_without_slash(router) -> None: resource = router.add_static("/st", pathlib.Path(__file__).parent, name="static") url = resource.url_for(filename="data.unknown_mime_type", append_version=True) expect_url = ( - "/st/data.unknown_mime_type?" "v=aUsn8CHEhhszc81d28QmlcBW0KQpfS2F4trgQKhOYd8%3D" + "/st/data.unknown_mime_type?v=aUsn8CHEhhszc81d28QmlcBW0KQpfS2F4trgQKhOYd8%3D" ) assert expect_url == str(url) diff --git a/tests/test_web_cli.py b/tests/test_web_cli.py index 12a01dff577..381aaf6cd82 100644 --- a/tests/test_web_cli.py +++ b/tests/test_web_cli.py @@ -90,7 +90,7 @@ def test_path_when_unsupported(mocker, monkeypatch) -> None: web.main(argv) error.assert_called_with( - "file system paths not supported by your" " operating environment" + "file system paths not supported by your operating environment" ) @@ -107,7 +107,7 @@ def test_entry_func_call(mocker) -> None: web.main(argv) module.func.assert_called_with( - ("--extra-optional-eins --extra-optional-zwei extra positional " "args").split() + ("--extra-optional-eins --extra-optional-zwei extra positional args").split() ) diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index 6f612ffc011..ad9e7c288fc 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -1651,9 +1651,7 @@ async def handler(request): resp = await client.post("/", data=data) assert 413 == resp.status resp_text = await resp.text() - assert ( - "Maximum request body size 1048576 exceeded, " "actual body size" in resp_text - ) + assert "Maximum request body size 1048576 exceeded, actual body size" in resp_text # Maximum request body size X exceeded, actual body size X body_size = int(resp_text.split()[-1]) assert body_size >= max_size @@ -1685,9 +1683,7 @@ async def handler(request): resp = await client.post("/", data=too_large_data) assert 413 == resp.status resp_text = await resp.text() - assert ( - "Maximum request body size 2097152 exceeded, " "actual body size" in resp_text - ) + assert "Maximum request body size 2097152 exceeded, actual body size" in resp_text # Maximum request body size X exceeded, actual body size X body_size = int(resp_text.split()[-1]) assert body_size >= custom_max_size diff --git a/tests/test_web_request.py b/tests/test_web_request.py index ba12d6f54e7..ff22e19d5b4 100644 --- a/tests/test_web_request.py +++ b/tests/test_web_request.py @@ -628,7 +628,7 @@ async def test_multipart_formdata(protocol) -> None: b"-----------------------------326931944431359--\r\n" ) content_type = ( - "multipart/form-data; boundary=" "---------------------------326931944431359" + "multipart/form-data; boundary=---------------------------326931944431359" ) payload.feed_eof() req = make_mocked_request( @@ -649,7 +649,7 @@ async def test_multipart_formdata_file(protocol) -> None: b"-----------------------------326931944431359--\r\n" ) content_type = ( - "multipart/form-data; boundary=" "---------------------------326931944431359" + "multipart/form-data; boundary=---------------------------326931944431359" ) payload.feed_eof() req = make_mocked_request( diff --git a/tests/test_web_websocket_functional.py b/tests/test_web_websocket_functional.py index 2be54486ee9..0ebd41db502 100644 --- a/tests/test_web_websocket_functional.py +++ b/tests/test_web_websocket_functional.py @@ -273,7 +273,7 @@ async def handler(request): await asyncio.sleep(0.08) assert await aborted - assert elapsed < 0.25, "close() should have returned before " "at most 2x timeout." + assert elapsed < 0.25, "close() should have returned before at most 2x timeout." await ws.close() diff --git a/tests/test_websocket_parser.py b/tests/test_websocket_parser.py index 3bdd8108e35..13b46803a76 100644 --- a/tests/test_websocket_parser.py +++ b/tests/test_websocket_parser.py @@ -382,7 +382,7 @@ def test_continuation_with_close_empty(out, parser) -> None: websocket_mask_data = b"some very long data for masking by websocket" websocket_mask_mask = b"1234" websocket_mask_masked = ( - b"B]^Q\x11DVFH\x12_[_U\x13PPFR\x14W]A\x14\\S@_X" b"\\T\x14SK\x13CTP@[RYV@" + b"B]^Q\x11DVFH\x12_[_U\x13PPFR\x14W]A\x14\\S@_X\\T\x14SK\x13CTP@[RYV@" ) From ff902d7977994f89138080a2e0603fcef45ca0ac Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 3 Sep 2024 19:41:23 +0100 Subject: [PATCH 142/296] Fix test_client_session_timeout_zero to not use internet connection (#9004) (#9007) (cherry picked from commit 3bbe1a5d9229f66a3f1a723d22e1c8bc0779230d) --- CHANGES/9004.packaging.rst | 1 + tests/conftest.py | 3 ++- tests/test_client_session.py | 31 +++++++++++++++++++++++++------ 3 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 CHANGES/9004.packaging.rst diff --git a/CHANGES/9004.packaging.rst b/CHANGES/9004.packaging.rst new file mode 100644 index 00000000000..f6b0f8ff2a3 --- /dev/null +++ b/CHANGES/9004.packaging.rst @@ -0,0 +1 @@ +Fixed ``test_client_session_timeout_zero`` to not require internet access -- by :user:`Dreamsorcerer`. diff --git a/tests/conftest.py b/tests/conftest.py index 1cb64b3a6f8..85fcac94138 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,6 +13,7 @@ import pytest +from aiohttp.client_proto import ResponseHandler from aiohttp.http import WS_KEY from aiohttp.test_utils import loop_context @@ -174,7 +175,7 @@ def pipe_name(): @pytest.fixture def create_mocked_conn(loop: Any): def _proto_factory(conn_closing_result=None, **kwargs): - proto = mock.Mock(**kwargs) + proto = mock.create_autospec(ResponseHandler, **kwargs) proto.closed = loop.create_future() proto.closed.set_result(conn_closing_result) return proto diff --git a/tests/test_client_session.py b/tests/test_client_session.py index 051c0aeba24..86f3a1b6c6e 100644 --- a/tests/test_client_session.py +++ b/tests/test_client_session.py @@ -20,6 +20,7 @@ from aiohttp.client_reqrep import ClientRequest from aiohttp.connector import BaseConnector, Connection, TCPConnector, UnixConnector from aiohttp.helpers import DEBUG +from aiohttp.http import RawResponseMessage from aiohttp.test_utils import make_mocked_coro from aiohttp.tracing import Trace @@ -934,13 +935,31 @@ async def test_client_session_timeout_default_args(loop) -> None: await session1.close() -async def test_client_session_timeout_zero() -> None: +async def test_client_session_timeout_zero( + create_mocked_conn: Callable[[], ResponseHandler] +) -> None: + async def create_connection( + req: object, traces: object, timeout: object + ) -> ResponseHandler: + await asyncio.sleep(0.01) + conn = create_mocked_conn() + conn.connected = True # type: ignore[misc] + assert conn.transport is not None + conn.transport.is_closing.return_value = False # type: ignore[attr-defined] + msg = mock.create_autospec(RawResponseMessage, spec_set=True, code=200) + conn.read.return_value = (msg, mock.Mock()) # type: ignore[attr-defined] + return conn + timeout = client.ClientTimeout(total=10, connect=0, sock_connect=0, sock_read=0) - try: - async with ClientSession(timeout=timeout) as session: - await session.get("http://example.com") - except asyncio.TimeoutError: - pytest.fail("0 should disable timeout.") + async with ClientSession(timeout=timeout) as session: + with mock.patch.object( + session._connector, "_create_connection", create_connection + ): + try: + resp = await session.get("http://example.com") + except asyncio.TimeoutError: # pragma: no cover + pytest.fail("0 should disable timeout.") + resp.close() async def test_client_session_timeout_bad_argument() -> None: From 9b60e5984d8ea7c92fd43824d5be37956dded9d6 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 3 Sep 2024 19:41:33 +0100 Subject: [PATCH 143/296] Fix test_client_session_timeout_zero to not use internet connection (#9004) (#9008) (cherry picked from commit 3bbe1a5d9229f66a3f1a723d22e1c8bc0779230d) --- CHANGES/9004.packaging.rst | 1 + tests/conftest.py | 3 ++- tests/test_client_session.py | 31 +++++++++++++++++++++++++------ 3 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 CHANGES/9004.packaging.rst diff --git a/CHANGES/9004.packaging.rst b/CHANGES/9004.packaging.rst new file mode 100644 index 00000000000..f6b0f8ff2a3 --- /dev/null +++ b/CHANGES/9004.packaging.rst @@ -0,0 +1 @@ +Fixed ``test_client_session_timeout_zero`` to not require internet access -- by :user:`Dreamsorcerer`. diff --git a/tests/conftest.py b/tests/conftest.py index 1cb64b3a6f8..85fcac94138 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,6 +13,7 @@ import pytest +from aiohttp.client_proto import ResponseHandler from aiohttp.http import WS_KEY from aiohttp.test_utils import loop_context @@ -174,7 +175,7 @@ def pipe_name(): @pytest.fixture def create_mocked_conn(loop: Any): def _proto_factory(conn_closing_result=None, **kwargs): - proto = mock.Mock(**kwargs) + proto = mock.create_autospec(ResponseHandler, **kwargs) proto.closed = loop.create_future() proto.closed.set_result(conn_closing_result) return proto diff --git a/tests/test_client_session.py b/tests/test_client_session.py index 051c0aeba24..86f3a1b6c6e 100644 --- a/tests/test_client_session.py +++ b/tests/test_client_session.py @@ -20,6 +20,7 @@ from aiohttp.client_reqrep import ClientRequest from aiohttp.connector import BaseConnector, Connection, TCPConnector, UnixConnector from aiohttp.helpers import DEBUG +from aiohttp.http import RawResponseMessage from aiohttp.test_utils import make_mocked_coro from aiohttp.tracing import Trace @@ -934,13 +935,31 @@ async def test_client_session_timeout_default_args(loop) -> None: await session1.close() -async def test_client_session_timeout_zero() -> None: +async def test_client_session_timeout_zero( + create_mocked_conn: Callable[[], ResponseHandler] +) -> None: + async def create_connection( + req: object, traces: object, timeout: object + ) -> ResponseHandler: + await asyncio.sleep(0.01) + conn = create_mocked_conn() + conn.connected = True # type: ignore[misc] + assert conn.transport is not None + conn.transport.is_closing.return_value = False # type: ignore[attr-defined] + msg = mock.create_autospec(RawResponseMessage, spec_set=True, code=200) + conn.read.return_value = (msg, mock.Mock()) # type: ignore[attr-defined] + return conn + timeout = client.ClientTimeout(total=10, connect=0, sock_connect=0, sock_read=0) - try: - async with ClientSession(timeout=timeout) as session: - await session.get("http://example.com") - except asyncio.TimeoutError: - pytest.fail("0 should disable timeout.") + async with ClientSession(timeout=timeout) as session: + with mock.patch.object( + session._connector, "_create_connection", create_connection + ): + try: + resp = await session.get("http://example.com") + except asyncio.TimeoutError: # pragma: no cover + pytest.fail("0 should disable timeout.") + resp.close() async def test_client_session_timeout_bad_argument() -> None: From ff9212b8c2c4eb28774ee83cf8eea8b750e5e8a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 10:46:31 +0000 Subject: [PATCH 144/296] Bump setuptools from 74.1.0 to 74.1.1 (#9011) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [setuptools](https://github.com/pypa/setuptools) from 74.1.0 to 74.1.1.
Changelog

Sourced from setuptools's changelog.

v74.1.1

Bugfixes

  • Fixed TypeError in msvc.EnvironmentInfo.return_env when no runtime redistributables are installed. (#1902)
Commits
  • 7ee29bd Bump version: 74.1.0 → 74.1.1
  • bf5d08c In msvc, use os.path namespace.
  • 60d2d56 Merge pull request #4628 from pypa/bugfix/1902-msvc-vcruntimeredist
  • 13bd961 Add news fragment.
  • 1a81003 Add type annotation for VCRuntimeRedist and update the docstring to reflect t...
  • f4adb80 In return_env, avoid checking isfile.
  • 2629280 Prefer generator expression in VCRuntimeRedist
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=setuptools&package-manager=pip&previous-version=74.1.0&new-version=74.1.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 428cfff3d7f..fe25fac4393 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -296,7 +296,7 @@ zipp==3.20.1 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via pip-tools -setuptools==74.1.0 +setuptools==74.1.1 # via # blockdiag # incremental diff --git a/requirements/dev.txt b/requirements/dev.txt index 55695f7eb64..b84fcf6ec74 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -288,7 +288,7 @@ zipp==3.20.1 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via pip-tools -setuptools==74.1.0 +setuptools==74.1.1 # via # blockdiag # incremental diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 4ef41521136..086fadafc7a 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -96,7 +96,7 @@ zipp==3.20.1 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -setuptools==74.1.0 +setuptools==74.1.1 # via # blockdiag # incremental diff --git a/requirements/doc.txt b/requirements/doc.txt index 804cb6e129d..82dd26a5e5b 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -91,7 +91,7 @@ zipp==3.20.1 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -setuptools==74.1.0 +setuptools==74.1.1 # via # blockdiag # incremental From 13f899e6ee1f14cef7a68cc8639774f1f75301fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 11:07:00 +0000 Subject: [PATCH 145/296] Bump cryptography from 43.0.0 to 43.0.1 (#9012) Bumps [cryptography](https://github.com/pyca/cryptography) from 43.0.0 to 43.0.1.
Changelog

Sourced from cryptography's changelog.

43.0.1 - 2024-09-03


* Updated Windows, macOS, and Linux wheels to be compiled with OpenSSL
3.3.2.

.. _v43-0-0:

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cryptography&package-manager=pip&previous-version=43.0.0&new-version=43.0.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index fe25fac4393..ce4a3459572 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -58,7 +58,7 @@ coverage==7.6.1 # via # -r requirements/test.in # pytest-cov -cryptography==43.0.0 +cryptography==43.0.1 # via # pyjwt # trustme diff --git a/requirements/dev.txt b/requirements/dev.txt index b84fcf6ec74..5d8116c9d2f 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -58,7 +58,7 @@ coverage==7.6.1 # via # -r requirements/test.in # pytest-cov -cryptography==43.0.0 +cryptography==43.0.1 # via # pyjwt # trustme diff --git a/requirements/lint.txt b/requirements/lint.txt index 82efbf9a4ee..00015ec3b96 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -26,7 +26,7 @@ click==8.1.7 # via # slotscheck # typer -cryptography==43.0.0 +cryptography==43.0.1 # via trustme distlib==0.3.8 # via virtualenv diff --git a/requirements/test.txt b/requirements/test.txt index a2bfc72a0a9..6f34e0b9bf3 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -34,7 +34,7 @@ coverage==7.6.1 # via # -r requirements/test.in # pytest-cov -cryptography==43.0.0 +cryptography==43.0.1 # via trustme exceptiongroup==1.2.2 # via pytest From 37ca369453a0f4badee85c5b8476f5a22db2d452 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 14:03:36 +0100 Subject: [PATCH 146/296] [PR #8998/875f23d5 backport][3.11] Fix resource reuse with regex paths (#9017) **This is a backport of PR #8998 as merged into master (875f23d516220a1cf849eb4a6d758edb8bff1f55).** Co-authored-by: Sam Bull --- CHANGES/8998.bugfix.rst | 1 + aiohttp/web_urldispatcher.py | 3 ++- tests/test_web_urldispatcher.py | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 CHANGES/8998.bugfix.rst diff --git a/CHANGES/8998.bugfix.rst b/CHANGES/8998.bugfix.rst new file mode 100644 index 00000000000..1b6b189e7ea --- /dev/null +++ b/CHANGES/8998.bugfix.rst @@ -0,0 +1 @@ +Fixed an error when trying to add a route for multiple methods with a path containing a regex pattern -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 765f8500c0e..7d74dd7ab16 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -456,6 +456,7 @@ class DynamicResource(Resource): def __init__(self, path: str, *, name: Optional[str] = None) -> None: super().__init__(name=name) + self._orig_path = path pattern = "" formatter = "" for part in ROUTE_RE.split(path): @@ -508,7 +509,7 @@ def _match(self, path: str) -> Optional[Dict[str, str]]: } def raw_match(self, path: str) -> bool: - return self._formatter == path + return self._orig_path == path def get_info(self) -> _InfoDict: return {"formatter": self._formatter, "pattern": self._pattern} diff --git a/tests/test_web_urldispatcher.py b/tests/test_web_urldispatcher.py index 8a97acf504d..7991cfe821e 100644 --- a/tests/test_web_urldispatcher.py +++ b/tests/test_web_urldispatcher.py @@ -703,10 +703,11 @@ async def handler(request: web.Request) -> web.Response: @pytest.mark.parametrize( "path", - [ + ( "/a", "/{a}", - ], + "/{a:.*}", + ), ) def test_reuse_last_added_resource(path: str) -> None: # Test that adding a route with the same name and path of the last added From f5bf86ac8429c4731f5d0783ea495e259c1eb19c Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 14:03:49 +0100 Subject: [PATCH 147/296] [PR #8998/875f23d5 backport][3.10] Fix resource reuse with regex paths (#9016) **This is a backport of PR #8998 as merged into master (875f23d516220a1cf849eb4a6d758edb8bff1f55).** Co-authored-by: Sam Bull --- CHANGES/8998.bugfix.rst | 1 + aiohttp/web_urldispatcher.py | 3 ++- tests/test_web_urldispatcher.py | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 CHANGES/8998.bugfix.rst diff --git a/CHANGES/8998.bugfix.rst b/CHANGES/8998.bugfix.rst new file mode 100644 index 00000000000..1b6b189e7ea --- /dev/null +++ b/CHANGES/8998.bugfix.rst @@ -0,0 +1 @@ +Fixed an error when trying to add a route for multiple methods with a path containing a regex pattern -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index a1df64b8e61..0b300e84da1 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -456,6 +456,7 @@ class DynamicResource(Resource): def __init__(self, path: str, *, name: Optional[str] = None) -> None: super().__init__(name=name) + self._orig_path = path pattern = "" formatter = "" for part in ROUTE_RE.split(path): @@ -508,7 +509,7 @@ def _match(self, path: str) -> Optional[Dict[str, str]]: } def raw_match(self, path: str) -> bool: - return self._formatter == path + return self._orig_path == path def get_info(self) -> _InfoDict: return {"formatter": self._formatter, "pattern": self._pattern} diff --git a/tests/test_web_urldispatcher.py b/tests/test_web_urldispatcher.py index 8a97acf504d..7991cfe821e 100644 --- a/tests/test_web_urldispatcher.py +++ b/tests/test_web_urldispatcher.py @@ -703,10 +703,11 @@ async def handler(request: web.Request) -> web.Response: @pytest.mark.parametrize( "path", - [ + ( "/a", "/{a}", - ], + "/{a:.*}", + ), ) def test_reuse_last_added_resource(path: str) -> None: # Test that adding a route with the same name and path of the last added From e62cc28161cc782065cb8434da93f647dbe13d41 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:21:16 +0000 Subject: [PATCH 148/296] Bump cffi from 1.17.0 to 1.17.1 (#9024) Bumps [cffi](https://github.com/python-cffi/cffi) from 1.17.0 to 1.17.1.
Release notes

Sourced from cffi's releases.

v1.17.1

  • Fix failing distutils.msvc9compiler imports under Windows (#118).
  • ffibuilder.emit_python_code() and ffibuiler.emit_c_code() accept file-like objects (#115).
  • ffiplatform calls are bypassed by ffibuilder.emit_python_code() and ffibuilder.emit_c_code() (#81).

Full Changelog: https://github.com/python-cffi/cffi/compare/v1.17.0...v1.17.1

Commits
  • 38bd6be release 1.17.1
  • ba10180 update whatsnew.rst for 1.17.1 (#121)
  • 61deb5f add yet another flag to recompile() to avoid calling ffiplatform (#81)
  • 1c292c1 Handle distutils without distutils.msvc9compiler.MSVCCompiler class (#118)
  • 182ffc4 Allow writing generated code to a file-like object. (#115)
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cffi&package-manager=pip&previous-version=1.17.0&new-version=1.17.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/lint.txt | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index 5876b5881a7..5c629050db9 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -16,7 +16,7 @@ attrs==24.2.0 # via -r requirements/runtime-deps.in brotli==1.1.0 ; platform_python_implementation == "CPython" # via -r requirements/runtime-deps.in -cffi==1.17.0 +cffi==1.17.1 # via pycares frozenlist==1.4.1 # via diff --git a/requirements/constraints.txt b/requirements/constraints.txt index ce4a3459572..7969d0936f0 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -36,7 +36,7 @@ build==1.2.1 # via pip-tools certifi==2024.8.30 # via requests -cffi==1.17.0 +cffi==1.17.1 # via # cryptography # pycares diff --git a/requirements/dev.txt b/requirements/dev.txt index 5d8116c9d2f..da9c650ff25 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -36,7 +36,7 @@ build==1.2.1 # via pip-tools certifi==2024.8.30 # via requests -cffi==1.17.0 +cffi==1.17.1 # via # cryptography # pycares diff --git a/requirements/lint.txt b/requirements/lint.txt index 00015ec3b96..38347013947 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -14,7 +14,7 @@ async-timeout==4.0.3 # via aioredis certifi==2024.8.30 # via requests -cffi==1.17.0 +cffi==1.17.1 # via # cryptography # pycares diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index 00927852825..be37aa14544 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -16,7 +16,7 @@ attrs==24.2.0 # via -r requirements/runtime-deps.in brotli==1.1.0 ; platform_python_implementation == "CPython" # via -r requirements/runtime-deps.in -cffi==1.17.0 +cffi==1.17.1 # via pycares frozenlist==1.4.1 # via diff --git a/requirements/test.txt b/requirements/test.txt index 6f34e0b9bf3..db0bd943d21 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -20,7 +20,7 @@ brotli==1.1.0 ; platform_python_implementation == "CPython" # via -r requirements/runtime-deps.in certifi==2024.8.30 # via requests -cffi==1.17.0 +cffi==1.17.1 # via # cryptography # pycares From c6fa90ace2032a72990b0ab227802f46be07112b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:28:07 +0000 Subject: [PATCH 149/296] Bump setuptools from 74.1.1 to 74.1.2 (#9025) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [setuptools](https://github.com/pypa/setuptools) from 74.1.1 to 74.1.2.
Changelog

Sourced from setuptools's changelog.

v74.1.2

Bugfixes

  • Fixed TypeError in sdist filelist processing by adding support for pathlib Paths for the build_base. (#4615)
  • Removed degraded and deprecated test_integration (easy_install) from the test suite. (#4632)
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=setuptools&package-manager=pip&previous-version=74.1.1&new-version=74.1.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 7969d0936f0..256c65bcb4d 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -296,7 +296,7 @@ zipp==3.20.1 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via pip-tools -setuptools==74.1.1 +setuptools==74.1.2 # via # blockdiag # incremental diff --git a/requirements/dev.txt b/requirements/dev.txt index da9c650ff25..e066e86e3a4 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -288,7 +288,7 @@ zipp==3.20.1 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via pip-tools -setuptools==74.1.1 +setuptools==74.1.2 # via # blockdiag # incremental diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 086fadafc7a..a54c0f9224e 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -96,7 +96,7 @@ zipp==3.20.1 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -setuptools==74.1.1 +setuptools==74.1.2 # via # blockdiag # incremental diff --git a/requirements/doc.txt b/requirements/doc.txt index 82dd26a5e5b..fd36d67bc1a 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -91,7 +91,7 @@ zipp==3.20.1 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -setuptools==74.1.1 +setuptools==74.1.2 # via # blockdiag # incremental From 5eaf8cae369883d17052b1705b185c74911bf5a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:15:26 +0000 Subject: [PATCH 150/296] Bump python-on-whales from 0.72.0 to 0.73.0 (#9038) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [python-on-whales](https://github.com/gabrieldemarmiesse/python-on-whales) from 0.72.0 to 0.73.0.
Release notes

Sourced from python-on-whales's releases.

v0.73.0

What's Changed

New Contributors

Full Changelog: https://github.com/gabrieldemarmiesse/python-on-whales/compare/v0.72.0...v0.73.0

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=python-on-whales&package-manager=pip&previous-version=0.72.0&new-version=0.73.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 256c65bcb4d..a4708392bd5 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -184,7 +184,7 @@ pytest-mock==3.14.0 # -r requirements/test.in python-dateutil==2.9.0.post0 # via freezegun -python-on-whales==0.72.0 +python-on-whales==0.73.0 # via # -r requirements/lint.in # -r requirements/test.in diff --git a/requirements/dev.txt b/requirements/dev.txt index e066e86e3a4..197af31a45d 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -179,7 +179,7 @@ pytest-mock==3.14.0 # -r requirements/test.in python-dateutil==2.9.0.post0 # via freezegun -python-on-whales==0.72.0 +python-on-whales==0.73.0 # via # -r requirements/lint.in # -r requirements/test.in diff --git a/requirements/lint.txt b/requirements/lint.txt index 38347013947..d3b5b35892d 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -80,7 +80,7 @@ pytest-mock==3.14.0 # via -r requirements/lint.in python-dateutil==2.9.0.post0 # via freezegun -python-on-whales==0.72.0 +python-on-whales==0.73.0 # via -r requirements/lint.in pyyaml==6.0.2 # via pre-commit diff --git a/requirements/test.txt b/requirements/test.txt index db0bd943d21..2e9a0e4abb1 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -94,7 +94,7 @@ pytest-mock==3.14.0 # via -r requirements/test.in python-dateutil==2.9.0.post0 # via freezegun -python-on-whales==0.72.0 +python-on-whales==0.73.0 # via -r requirements/test.in re-assert==1.1.0 # via -r requirements/test.in From aca99bc3c73eb6b2ae1eccd7ef76bbe1df96e3f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:46:11 +0000 Subject: [PATCH 151/296] Bump pydantic from 2.8.2 to 2.9.0 (#9041) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.8.2 to 2.9.0.
Release notes

Sourced from pydantic's releases.

v2.9.0 (2024-09-05)

The code released in v2.9.0 is practically identical to that of v2.9.0b2.

Check out our blog post to learn more about the release highlights!

What's Changed

Packaging

New Features

Changes

Performance

... (truncated)

Changelog

Sourced from pydantic's changelog.

v2.9.0 (2024-09-05)

GitHub release

The code released in v2.9.0 is practically identical to that of v2.9.0b2.

What's Changed

Packaging

New Features

Changes

Performance

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pydantic&package-manager=pip&previous-version=2.8.2&new-version=2.9.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 | 4 ++-- requirements/dev.txt | 4 ++-- requirements/lint.txt | 4 ++-- requirements/test.txt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index a4708392bd5..420d46bbf6c 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -152,9 +152,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.8.2 +pydantic==2.9.0 # via python-on-whales -pydantic-core==2.20.1 +pydantic-core==2.23.2 # via pydantic pyenchant==3.2.2 # via sphinxcontrib-spelling diff --git a/requirements/dev.txt b/requirements/dev.txt index 197af31a45d..e5ed6030ae1 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -149,9 +149,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.8.2 +pydantic==2.9.0 # via python-on-whales -pydantic-core==2.20.1 +pydantic-core==2.23.2 # via pydantic pygments==2.18.0 # via diff --git a/requirements/lint.txt b/requirements/lint.txt index d3b5b35892d..374f5762d44 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -66,9 +66,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.8.2 +pydantic==2.9.0 # via python-on-whales -pydantic-core==2.20.1 +pydantic-core==2.23.2 # via pydantic pygments==2.18.0 # via rich diff --git a/requirements/test.txt b/requirements/test.txt index 2e9a0e4abb1..82dd51bcf50 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -77,9 +77,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.8.2 +pydantic==2.9.0 # via python-on-whales -pydantic-core==2.20.1 +pydantic-core==2.23.2 # via pydantic pygments==2.18.0 # via rich From 446ed9ef7112c269edf728c01ea7dbfc0df96a50 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Sep 2024 15:11:00 -0500 Subject: [PATCH 152/296] [PR #9029/466448c backport][3.11] Fix SSLContext creation in the TCPConnector with multiple loops (#9043) --- aiohttp/connector.py | 100 +++++++++++++++--------------------- tests/test_connector.py | 109 +++++++++++++++++++++------------------- tests/test_proxy.py | 3 +- 3 files changed, 100 insertions(+), 112 deletions(-) diff --git a/aiohttp/connector.py b/aiohttp/connector.py index 360eabc7bb2..bbdb140187d 100644 --- a/aiohttp/connector.py +++ b/aiohttp/connector.py @@ -50,14 +50,7 @@ ) from .client_proto import ResponseHandler from .client_reqrep import ClientRequest, Fingerprint, _merge_ssl_params -from .helpers import ( - ceil_timeout, - is_ip_address, - noop, - sentinel, - set_exception, - set_result, -) +from .helpers import ceil_timeout, is_ip_address, noop, sentinel from .locks import EventResultOrError from .resolver import DefaultResolver @@ -748,6 +741,35 @@ def expired(self, key: Tuple[str, int]) -> bool: return self._timestamps[key] + self._ttl < monotonic() +def _make_ssl_context(verified: bool) -> SSLContext: + """Create SSL context. + + This method is not async-friendly and should be called from a thread + because it will load certificates from disk and do other blocking I/O. + """ + if ssl is None: + # No ssl support + return None + if verified: + return ssl.create_default_context() + sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + sslcontext.options |= ssl.OP_NO_SSLv2 + sslcontext.options |= ssl.OP_NO_SSLv3 + sslcontext.check_hostname = False + sslcontext.verify_mode = ssl.CERT_NONE + sslcontext.options |= ssl.OP_NO_COMPRESSION + sslcontext.set_default_verify_paths() + return sslcontext + + +# The default SSLContext objects are created at import time +# since they do blocking I/O to load certificates from disk, +# and imports should always be done before the event loop starts +# or in a thread. +_SSL_CONTEXT_VERIFIED = _make_ssl_context(True) +_SSL_CONTEXT_UNVERIFIED = _make_ssl_context(False) + + class TCPConnector(BaseConnector): """TCP connector. @@ -778,7 +800,6 @@ class TCPConnector(BaseConnector): """ allowed_protocol_schema_set = HIGH_LEVEL_SCHEMA_SET | frozenset({"tcp"}) - _made_ssl_context: Dict[bool, "asyncio.Future[SSLContext]"] = {} def __init__( self, @@ -982,25 +1003,7 @@ async def _create_connection( return proto - @staticmethod - def _make_ssl_context(verified: bool) -> SSLContext: - """Create SSL context. - - This method is not async-friendly and should be called from a thread - because it will load certificates from disk and do other blocking I/O. - """ - if verified: - return ssl.create_default_context() - sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) - sslcontext.options |= ssl.OP_NO_SSLv2 - sslcontext.options |= ssl.OP_NO_SSLv3 - sslcontext.check_hostname = False - sslcontext.verify_mode = ssl.CERT_NONE - sslcontext.options |= ssl.OP_NO_COMPRESSION - sslcontext.set_default_verify_paths() - return sslcontext - - async def _get_ssl_context(self, req: ClientRequest) -> Optional[SSLContext]: + def _get_ssl_context(self, req: ClientRequest) -> Optional[SSLContext]: """Logic to get the correct SSL context 0. if req.ssl is false, return None @@ -1024,35 +1027,14 @@ async def _get_ssl_context(self, req: ClientRequest) -> Optional[SSLContext]: return sslcontext if sslcontext is not True: # not verified or fingerprinted - return await self._make_or_get_ssl_context(False) + return _SSL_CONTEXT_UNVERIFIED sslcontext = self._ssl if isinstance(sslcontext, ssl.SSLContext): return sslcontext if sslcontext is not True: # not verified or fingerprinted - return await self._make_or_get_ssl_context(False) - return await self._make_or_get_ssl_context(True) - - async def _make_or_get_ssl_context(self, verified: bool) -> SSLContext: - """Create or get cached SSL context.""" - try: - return await self._made_ssl_context[verified] - except KeyError: - loop = self._loop - future = loop.create_future() - self._made_ssl_context[verified] = future - try: - result = await loop.run_in_executor( - None, self._make_ssl_context, verified - ) - # BaseException is used since we might get CancelledError - except BaseException as ex: - del self._made_ssl_context[verified] - set_exception(future, ex) - raise - else: - set_result(future, result) - return result + return _SSL_CONTEXT_UNVERIFIED + return _SSL_CONTEXT_VERIFIED def _get_fingerprint(self, req: ClientRequest) -> Optional["Fingerprint"]: ret = req.ssl @@ -1204,13 +1186,11 @@ async def _start_tls_connection( ) -> Tuple[asyncio.BaseTransport, ResponseHandler]: """Wrap the raw TCP transport with TLS.""" tls_proto = self._factory() # Create a brand new proto for TLS - - # Safety of the `cast()` call here is based on the fact that - # internally `_get_ssl_context()` only returns `None` when - # `req.is_ssl()` evaluates to `False` which is never gonna happen - # in this code path. Of course, it's rather fragile - # maintainability-wise but this is to be solved separately. - sslcontext = cast(ssl.SSLContext, await self._get_ssl_context(req)) + sslcontext = self._get_ssl_context(req) + if TYPE_CHECKING: + # _start_tls_connection is unreachable in the current code path + # if sslcontext is None. + assert sslcontext is not None try: async with ceil_timeout( @@ -1288,7 +1268,7 @@ async def _create_direct_connection( *, client_error: Type[Exception] = ClientConnectorError, ) -> Tuple[asyncio.Transport, ResponseHandler]: - sslcontext = await self._get_ssl_context(req) + sslcontext = self._get_ssl_context(req) fingerprint = self._get_fingerprint(req) host = req.url.raw_host diff --git a/tests/test_connector.py b/tests/test_connector.py index 0129f0cc330..bbe77f2a705 100644 --- a/tests/test_connector.py +++ b/tests/test_connector.py @@ -1,5 +1,4 @@ # Tests of http client with custom Connector - import asyncio import gc import hashlib @@ -9,8 +8,9 @@ import sys import uuid from collections import deque +from concurrent import futures from contextlib import closing, suppress -from typing import Any, List, Optional, Type +from typing import Any, List, Literal, Optional from unittest import mock import pytest @@ -18,10 +18,16 @@ from yarl import URL import aiohttp -from aiohttp import client, web +from aiohttp import client, connector as connector_module, web from aiohttp.client import ClientRequest, ClientTimeout from aiohttp.client_reqrep import ConnectionKey -from aiohttp.connector import Connection, TCPConnector, _DNSCacheTable +from aiohttp.connector import ( + _SSL_CONTEXT_UNVERIFIED, + _SSL_CONTEXT_VERIFIED, + Connection, + TCPConnector, + _DNSCacheTable, +) from aiohttp.locks import EventResultOrError from aiohttp.test_utils import make_mocked_coro, unused_port from aiohttp.tracing import Trace @@ -1540,23 +1546,11 @@ async def test_tcp_connector_clear_dns_cache_bad_args(loop) -> None: conn.clear_dns_cache("localhost") -async def test_dont_recreate_ssl_context() -> None: - conn = aiohttp.TCPConnector() - ctx = await conn._make_or_get_ssl_context(True) - assert ctx is await conn._make_or_get_ssl_context(True) - - -async def test_dont_recreate_ssl_context2() -> None: - conn = aiohttp.TCPConnector() - ctx = await conn._make_or_get_ssl_context(False) - assert ctx is await conn._make_or_get_ssl_context(False) - - async def test___get_ssl_context1() -> None: conn = aiohttp.TCPConnector() req = mock.Mock() req.is_ssl.return_value = False - assert await conn._get_ssl_context(req) is None + assert conn._get_ssl_context(req) is None async def test___get_ssl_context2(loop) -> None: @@ -1565,7 +1559,7 @@ async def test___get_ssl_context2(loop) -> None: req = mock.Mock() req.is_ssl.return_value = True req.ssl = ctx - assert await conn._get_ssl_context(req) is ctx + assert conn._get_ssl_context(req) is ctx async def test___get_ssl_context3(loop) -> None: @@ -1574,7 +1568,7 @@ async def test___get_ssl_context3(loop) -> None: req = mock.Mock() req.is_ssl.return_value = True req.ssl = True - assert await conn._get_ssl_context(req) is ctx + assert conn._get_ssl_context(req) is ctx async def test___get_ssl_context4(loop) -> None: @@ -1583,9 +1577,7 @@ async def test___get_ssl_context4(loop) -> None: req = mock.Mock() req.is_ssl.return_value = True req.ssl = False - assert await conn._get_ssl_context(req) is await conn._make_or_get_ssl_context( - False - ) + assert conn._get_ssl_context(req) is _SSL_CONTEXT_UNVERIFIED async def test___get_ssl_context5(loop) -> None: @@ -1594,9 +1586,7 @@ async def test___get_ssl_context5(loop) -> None: req = mock.Mock() req.is_ssl.return_value = True req.ssl = aiohttp.Fingerprint(hashlib.sha256(b"1").digest()) - assert await conn._get_ssl_context(req) is await conn._make_or_get_ssl_context( - False - ) + assert conn._get_ssl_context(req) is _SSL_CONTEXT_UNVERIFIED async def test___get_ssl_context6() -> None: @@ -1604,7 +1594,7 @@ async def test___get_ssl_context6() -> None: req = mock.Mock() req.is_ssl.return_value = True req.ssl = True - assert await conn._get_ssl_context(req) is await conn._make_or_get_ssl_context(True) + assert conn._get_ssl_context(req) is _SSL_CONTEXT_VERIFIED async def test_ssl_context_once() -> None: @@ -1616,31 +1606,9 @@ async def test_ssl_context_once() -> None: req = mock.Mock() req.is_ssl.return_value = True req.ssl = True - assert await conn1._get_ssl_context(req) is await conn1._make_or_get_ssl_context( - True - ) - assert await conn2._get_ssl_context(req) is await conn1._make_or_get_ssl_context( - True - ) - assert await conn3._get_ssl_context(req) is await conn1._make_or_get_ssl_context( - True - ) - assert conn1._made_ssl_context is conn2._made_ssl_context is conn3._made_ssl_context - assert True in conn1._made_ssl_context - - -@pytest.mark.parametrize("exception", [OSError, ssl.SSLError, asyncio.CancelledError]) -async def test_ssl_context_creation_raises(exception: Type[BaseException]) -> None: - """Test that we try again if SSLContext creation fails the first time.""" - conn = aiohttp.TCPConnector() - conn._made_ssl_context.clear() - - with mock.patch.object( - conn, "_make_ssl_context", side_effect=exception - ), pytest.raises(exception): - await conn._make_or_get_ssl_context(True) - - assert isinstance(await conn._make_or_get_ssl_context(True), ssl.SSLContext) + assert conn1._get_ssl_context(req) is _SSL_CONTEXT_VERIFIED + assert conn2._get_ssl_context(req) is _SSL_CONTEXT_VERIFIED + assert conn3._get_ssl_context(req) is _SSL_CONTEXT_VERIFIED async def test_close_twice(loop) -> None: @@ -2717,3 +2685,42 @@ async def allow_connection_and_add_dummy_waiter(): ) await connector.close() + + +def test_connector_multiple_event_loop() -> None: + """Test the connector with multiple event loops.""" + + async def async_connect() -> Literal[True]: + conn = aiohttp.TCPConnector() + loop = asyncio.get_running_loop() + req = ClientRequest("GET", URL("https://127.0.0.1"), loop=loop) + with suppress(aiohttp.ClientConnectorError): + with mock.patch.object( + conn._loop, + "create_connection", + autospec=True, + spec_set=True, + side_effect=ssl.CertificateError, + ): + await conn.connect(req, [], ClientTimeout()) + return True + + def test_connect() -> Literal[True]: + loop = asyncio.new_event_loop() + try: + return loop.run_until_complete(async_connect()) + finally: + loop.close() + + with futures.ThreadPoolExecutor() as executor: + res_list = [executor.submit(test_connect) for _ in range(2)] + raw_response_list = [res.result() for res in futures.as_completed(res_list)] + + assert raw_response_list == [True, True] + + +def test_default_ssl_context_creation_without_ssl() -> None: + """Verify _make_ssl_context does not raise when ssl is not available.""" + with mock.patch.object(connector_module, "ssl", None): + assert connector_module._make_ssl_context(False) is None + assert connector_module._make_ssl_context(True) is None diff --git a/tests/test_proxy.py b/tests/test_proxy.py index c5e98deb8a5..4fa5e932098 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -12,6 +12,7 @@ import aiohttp from aiohttp.client_reqrep import ClientRequest, ClientResponse +from aiohttp.connector import _SSL_CONTEXT_VERIFIED from aiohttp.helpers import TimerNoop from aiohttp.test_utils import make_mocked_coro @@ -817,7 +818,7 @@ async def make_conn(): self.loop.start_tls.assert_called_with( mock.ANY, mock.ANY, - self.loop.run_until_complete(connector._make_or_get_ssl_context(True)), + _SSL_CONTEXT_VERIFIED, server_hostname="www.python.org", ssl_handshake_timeout=mock.ANY, ) From 9c34a8cbf3908634fd6da2eab23adaf240879588 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 20:36:12 +0000 Subject: [PATCH 153/296] [PR #9031/9f0ae074 backport][3.10] Avoid tracing overhead in http_writer when there are no traces (#9045) Co-authored-by: J. Nick Koston --- CHANGES/9031.misc.rst | 1 + aiohttp/client_reqrep.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 CHANGES/9031.misc.rst diff --git a/CHANGES/9031.misc.rst b/CHANGES/9031.misc.rst new file mode 100644 index 00000000000..1deab5230f7 --- /dev/null +++ b/CHANGES/9031.misc.rst @@ -0,0 +1 @@ +Tracing overhead is avoided in the http writer when there are no active traces -- by user:`bdraco`. diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index d2c5f16df2b..c261af0421e 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -696,11 +696,15 @@ async def send(self, conn: "Connection") -> "ClientResponse": writer = StreamWriter( protocol, self.loop, - on_chunk_sent=functools.partial( - self._on_chunk_request_sent, self.method, self.url + on_chunk_sent=( + functools.partial(self._on_chunk_request_sent, self.method, self.url) + if self._traces + else None ), - on_headers_sent=functools.partial( - self._on_headers_request_sent, self.method, self.url + on_headers_sent=( + functools.partial(self._on_headers_request_sent, self.method, self.url) + if self._traces + else None ), ) From 4e1070e1523fd55409534c617d691aec4fdbe7c6 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 21:02:33 +0000 Subject: [PATCH 154/296] [PR #9032/c693a816 backport][3.11] Fix Link-Local IPv6 Flags in the Resolver (#9047) Co-authored-by: GitNMLee <89409038+GitNMLee@users.noreply.github.com> Fixes #9028 Fixes #123'). --> --- CHANGES/9032.bugfix.rst | 3 +++ aiohttp/resolver.py | 5 +++-- tests/test_resolver.py | 27 ++++++++++++++++++++++----- 3 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 CHANGES/9032.bugfix.rst diff --git a/CHANGES/9032.bugfix.rst b/CHANGES/9032.bugfix.rst new file mode 100644 index 00000000000..8c8d81f6319 --- /dev/null +++ b/CHANGES/9032.bugfix.rst @@ -0,0 +1,3 @@ +Fixed the incorrect use of flags for ``getnameinfo()`` in the Resolver --by :user:`GitNMLee` + +Link-Local IPv6 addresses can now be handled by the Resolver correctly. diff --git a/aiohttp/resolver.py b/aiohttp/resolver.py index c8fce5b5706..4f15e84071d 100644 --- a/aiohttp/resolver.py +++ b/aiohttp/resolver.py @@ -17,6 +17,7 @@ _NUMERIC_SOCKET_FLAGS = socket.AI_NUMERICHOST | socket.AI_NUMERICSERV +_NAME_SOCKET_FLAGS = socket.NI_NUMERICHOST | socket.NI_NUMERICSERV class ThreadedResolver(AbstractResolver): @@ -52,7 +53,7 @@ async def resolve( # LL IPv6 is a VERY rare case. Strictly speaking, we should use # getnameinfo() unconditionally, but performance makes sense. resolved_host, _port = await self._loop.getnameinfo( - address, _NUMERIC_SOCKET_FLAGS + address, _NAME_SOCKET_FLAGS ) port = int(_port) else: @@ -120,7 +121,7 @@ async def resolve( # getnameinfo() unconditionally, but performance makes sense. result = await self._resolver.getnameinfo( (address[0].decode("ascii"), *address[1:]), - _NUMERIC_SOCKET_FLAGS, + _NAME_SOCKET_FLAGS, ) resolved_host = result.node else: diff --git a/tests/test_resolver.py b/tests/test_resolver.py index 825db81e41b..e0e843f4782 100644 --- a/tests/test_resolver.py +++ b/tests/test_resolver.py @@ -3,12 +3,12 @@ import socket from ipaddress import ip_address from typing import Any, Awaitable, Callable, Collection, List, NamedTuple, Tuple, Union -from unittest.mock import Mock, patch +from unittest.mock import Mock, create_autospec, patch import pytest from aiohttp.resolver import ( - _NUMERIC_SOCKET_FLAGS, + _NAME_SOCKET_FLAGS, AsyncResolver, DefaultResolver, ThreadedResolver, @@ -153,9 +153,7 @@ async def test_async_resolver_positive_link_local_ipv6_lookup(loop: Any) -> None port=0, type=socket.SOCK_STREAM, ) - mock().getnameinfo.assert_called_with( - ("fe80::1", 0, 0, 3), _NUMERIC_SOCKET_FLAGS - ) + mock().getnameinfo.assert_called_with(("fe80::1", 0, 0, 3), _NAME_SOCKET_FLAGS) @pytest.mark.skipif(not getaddrinfo, reason="aiodns >=3.2.0 required") @@ -211,12 +209,31 @@ async def test_threaded_resolver_positive_ipv6_link_local_lookup() -> None: loop = Mock() loop.getaddrinfo = fake_ipv6_addrinfo(["fe80::1"]) loop.getnameinfo = fake_ipv6_nameinfo("fe80::1%eth0") + + # Mock the fake function that was returned by helper functions + loop.getaddrinfo = create_autospec(loop.getaddrinfo) + loop.getnameinfo = create_autospec(loop.getnameinfo) + + # Set the correct return values for mock functions + loop.getaddrinfo.return_value = await fake_ipv6_addrinfo(["fe80::1"])() + loop.getnameinfo.return_value = await fake_ipv6_nameinfo("fe80::1%eth0")() + resolver = ThreadedResolver() resolver._loop = loop real = await resolver.resolve("www.python.org") assert real[0]["hostname"] == "www.python.org" ipaddress.ip_address(real[0]["host"]) + loop.getaddrinfo.assert_called_with( + "www.python.org", + 0, + type=socket.SOCK_STREAM, + family=socket.AF_INET, + flags=socket.AI_ADDRCONFIG, + ) + + loop.getnameinfo.assert_called_with(("fe80::1", 0, 0, 3), _NAME_SOCKET_FLAGS) + async def test_threaded_resolver_multiple_replies() -> None: loop = Mock() From 53d3f595dac05565e4ca8657fa0c4be8c68de4f8 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 21:10:13 +0000 Subject: [PATCH 155/296] [PR #9044/684b7922 backport][3.10] Add missing changelog entry for #9029 (#9049) Co-authored-by: J. Nick Koston --- CHANGES/9029.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 CHANGES/9029.bugfix.rst diff --git a/CHANGES/9029.bugfix.rst b/CHANGES/9029.bugfix.rst new file mode 100644 index 00000000000..7ca956e3832 --- /dev/null +++ b/CHANGES/9029.bugfix.rst @@ -0,0 +1 @@ +Fixed creation of ``SSLContext`` inside of :py:class:`aiohttp.TCPConnector` with multiple event loops in different threads -- by :user:`bdraco`. From a35a73b453948f1f3b6d5b2a3ad6ffbfd83ccb78 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 21:16:43 +0000 Subject: [PATCH 156/296] [PR #9044/684b7922 backport][3.11] Add missing changelog entry for #9029 (#9050) Co-authored-by: J. Nick Koston --- CHANGES/9029.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 CHANGES/9029.bugfix.rst diff --git a/CHANGES/9029.bugfix.rst b/CHANGES/9029.bugfix.rst new file mode 100644 index 00000000000..7ca956e3832 --- /dev/null +++ b/CHANGES/9029.bugfix.rst @@ -0,0 +1 @@ +Fixed creation of ``SSLContext`` inside of :py:class:`aiohttp.TCPConnector` with multiple event loops in different threads -- by :user:`bdraco`. From b48ebc1e9f9c03ed39b52ab95486d1d957995d30 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Sep 2024 18:23:48 -0500 Subject: [PATCH 157/296] [PR #9032/c693a816 backport][3.10] Fix Link-Local IPv6 Flags in the Resolver (#9048) Co-authored-by: Nathan Lee Co-authored-by: pre-commit-ci[bot] Co-authored-by: Sam Bull Co-authored-by: J. Nick Koston Co-authored-by: GitNMLee <89409038+GitNMLee@users.noreply.github.com> --- CHANGES/9032.bugfix.rst | 3 +++ aiohttp/resolver.py | 5 +++-- tests/test_resolver.py | 27 ++++++++++++++++++++++----- 3 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 CHANGES/9032.bugfix.rst diff --git a/CHANGES/9032.bugfix.rst b/CHANGES/9032.bugfix.rst new file mode 100644 index 00000000000..8c8d81f6319 --- /dev/null +++ b/CHANGES/9032.bugfix.rst @@ -0,0 +1,3 @@ +Fixed the incorrect use of flags for ``getnameinfo()`` in the Resolver --by :user:`GitNMLee` + +Link-Local IPv6 addresses can now be handled by the Resolver correctly. diff --git a/aiohttp/resolver.py b/aiohttp/resolver.py index 10e36266abe..6283ec2b8d5 100644 --- a/aiohttp/resolver.py +++ b/aiohttp/resolver.py @@ -18,6 +18,7 @@ _NUMERIC_SOCKET_FLAGS = socket.AI_NUMERICHOST | socket.AI_NUMERICSERV +_NAME_SOCKET_FLAGS = socket.NI_NUMERICHOST | socket.NI_NUMERICSERV _SUPPORTS_SCOPE_ID = sys.version_info >= (3, 9, 0) @@ -54,7 +55,7 @@ async def resolve( # LL IPv6 is a VERY rare case. Strictly speaking, we should use # getnameinfo() unconditionally, but performance makes sense. resolved_host, _port = await self._loop.getnameinfo( - address, _NUMERIC_SOCKET_FLAGS + address, _NAME_SOCKET_FLAGS ) port = int(_port) else: @@ -122,7 +123,7 @@ async def resolve( # getnameinfo() unconditionally, but performance makes sense. result = await self._resolver.getnameinfo( (address[0].decode("ascii"), *address[1:]), - _NUMERIC_SOCKET_FLAGS, + _NAME_SOCKET_FLAGS, ) resolved_host = result.node else: diff --git a/tests/test_resolver.py b/tests/test_resolver.py index f51506a6999..8b2ea620037 100644 --- a/tests/test_resolver.py +++ b/tests/test_resolver.py @@ -3,12 +3,12 @@ import socket from ipaddress import ip_address from typing import Any, Awaitable, Callable, Collection, List, NamedTuple, Tuple, Union -from unittest.mock import Mock, patch +from unittest.mock import Mock, create_autospec, patch import pytest from aiohttp.resolver import ( - _NUMERIC_SOCKET_FLAGS, + _NAME_SOCKET_FLAGS, _SUPPORTS_SCOPE_ID, AsyncResolver, DefaultResolver, @@ -157,9 +157,7 @@ async def test_async_resolver_positive_link_local_ipv6_lookup(loop: Any) -> None port=0, type=socket.SOCK_STREAM, ) - mock().getnameinfo.assert_called_with( - ("fe80::1", 0, 0, 3), _NUMERIC_SOCKET_FLAGS - ) + mock().getnameinfo.assert_called_with(("fe80::1", 0, 0, 3), _NAME_SOCKET_FLAGS) @pytest.mark.skipif(not getaddrinfo, reason="aiodns >=3.2.0 required") @@ -218,12 +216,31 @@ async def test_threaded_resolver_positive_ipv6_link_local_lookup() -> None: loop = Mock() loop.getaddrinfo = fake_ipv6_addrinfo(["fe80::1"]) loop.getnameinfo = fake_ipv6_nameinfo("fe80::1%eth0") + + # Mock the fake function that was returned by helper functions + loop.getaddrinfo = create_autospec(loop.getaddrinfo) + loop.getnameinfo = create_autospec(loop.getnameinfo) + + # Set the correct return values for mock functions + loop.getaddrinfo.return_value = await fake_ipv6_addrinfo(["fe80::1"])() + loop.getnameinfo.return_value = await fake_ipv6_nameinfo("fe80::1%eth0")() + resolver = ThreadedResolver() resolver._loop = loop real = await resolver.resolve("www.python.org") assert real[0]["hostname"] == "www.python.org" ipaddress.ip_address(real[0]["host"]) + loop.getaddrinfo.assert_called_with( + "www.python.org", + 0, + type=socket.SOCK_STREAM, + family=socket.AF_INET, + flags=socket.AI_ADDRCONFIG, + ) + + loop.getnameinfo.assert_called_with(("fe80::1", 0, 0, 3), _NAME_SOCKET_FLAGS) + async def test_threaded_resolver_multiple_replies() -> None: loop = Mock() From 4d022e4e68eb93c01ac53fad88efab70df0a193d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 6 Sep 2024 18:23:58 -0500 Subject: [PATCH 158/296] [PR #9029/466448c backport][3.10] Fix SSLContext creation in the TCPConnector with multiple loops (#9042) --- aiohttp/connector.py | 100 ++++++++++++++-------------------- tests/test_connector.py | 116 ++++++++++++++++++++++------------------ tests/test_proxy.py | 3 +- 3 files changed, 106 insertions(+), 113 deletions(-) diff --git a/aiohttp/connector.py b/aiohttp/connector.py index 7c6e747695e..c25f184bbbe 100644 --- a/aiohttp/connector.py +++ b/aiohttp/connector.py @@ -50,14 +50,7 @@ ) from .client_proto import ResponseHandler from .client_reqrep import ClientRequest, Fingerprint, _merge_ssl_params -from .helpers import ( - ceil_timeout, - is_ip_address, - noop, - sentinel, - set_exception, - set_result, -) +from .helpers import ceil_timeout, is_ip_address, noop, sentinel from .locks import EventResultOrError from .resolver import DefaultResolver @@ -748,6 +741,35 @@ def expired(self, key: Tuple[str, int]) -> bool: return self._timestamps[key] + self._ttl < monotonic() +def _make_ssl_context(verified: bool) -> SSLContext: + """Create SSL context. + + This method is not async-friendly and should be called from a thread + because it will load certificates from disk and do other blocking I/O. + """ + if ssl is None: + # No ssl support + return None + if verified: + return ssl.create_default_context() + sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + sslcontext.options |= ssl.OP_NO_SSLv2 + sslcontext.options |= ssl.OP_NO_SSLv3 + sslcontext.check_hostname = False + sslcontext.verify_mode = ssl.CERT_NONE + sslcontext.options |= ssl.OP_NO_COMPRESSION + sslcontext.set_default_verify_paths() + return sslcontext + + +# The default SSLContext objects are created at import time +# since they do blocking I/O to load certificates from disk, +# and imports should always be done before the event loop starts +# or in a thread. +_SSL_CONTEXT_VERIFIED = _make_ssl_context(True) +_SSL_CONTEXT_UNVERIFIED = _make_ssl_context(False) + + class TCPConnector(BaseConnector): """TCP connector. @@ -778,7 +800,6 @@ class TCPConnector(BaseConnector): """ allowed_protocol_schema_set = HIGH_LEVEL_SCHEMA_SET | frozenset({"tcp"}) - _made_ssl_context: Dict[bool, "asyncio.Future[SSLContext]"] = {} def __init__( self, @@ -982,25 +1003,7 @@ async def _create_connection( return proto - @staticmethod - def _make_ssl_context(verified: bool) -> SSLContext: - """Create SSL context. - - This method is not async-friendly and should be called from a thread - because it will load certificates from disk and do other blocking I/O. - """ - if verified: - return ssl.create_default_context() - sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) - sslcontext.options |= ssl.OP_NO_SSLv2 - sslcontext.options |= ssl.OP_NO_SSLv3 - sslcontext.check_hostname = False - sslcontext.verify_mode = ssl.CERT_NONE - sslcontext.options |= ssl.OP_NO_COMPRESSION - sslcontext.set_default_verify_paths() - return sslcontext - - async def _get_ssl_context(self, req: ClientRequest) -> Optional[SSLContext]: + def _get_ssl_context(self, req: ClientRequest) -> Optional[SSLContext]: """Logic to get the correct SSL context 0. if req.ssl is false, return None @@ -1024,35 +1027,14 @@ async def _get_ssl_context(self, req: ClientRequest) -> Optional[SSLContext]: return sslcontext if sslcontext is not True: # not verified or fingerprinted - return await self._make_or_get_ssl_context(False) + return _SSL_CONTEXT_UNVERIFIED sslcontext = self._ssl if isinstance(sslcontext, ssl.SSLContext): return sslcontext if sslcontext is not True: # not verified or fingerprinted - return await self._make_or_get_ssl_context(False) - return await self._make_or_get_ssl_context(True) - - async def _make_or_get_ssl_context(self, verified: bool) -> SSLContext: - """Create or get cached SSL context.""" - try: - return await self._made_ssl_context[verified] - except KeyError: - loop = self._loop - future = loop.create_future() - self._made_ssl_context[verified] = future - try: - result = await loop.run_in_executor( - None, self._make_ssl_context, verified - ) - # BaseException is used since we might get CancelledError - except BaseException as ex: - del self._made_ssl_context[verified] - set_exception(future, ex) - raise - else: - set_result(future, result) - return result + return _SSL_CONTEXT_UNVERIFIED + return _SSL_CONTEXT_VERIFIED def _get_fingerprint(self, req: ClientRequest) -> Optional["Fingerprint"]: ret = req.ssl @@ -1204,13 +1186,11 @@ async def _start_tls_connection( ) -> Tuple[asyncio.BaseTransport, ResponseHandler]: """Wrap the raw TCP transport with TLS.""" tls_proto = self._factory() # Create a brand new proto for TLS - - # Safety of the `cast()` call here is based on the fact that - # internally `_get_ssl_context()` only returns `None` when - # `req.is_ssl()` evaluates to `False` which is never gonna happen - # in this code path. Of course, it's rather fragile - # maintainability-wise but this is to be solved separately. - sslcontext = cast(ssl.SSLContext, await self._get_ssl_context(req)) + sslcontext = self._get_ssl_context(req) + if TYPE_CHECKING: + # _start_tls_connection is unreachable in the current code path + # if sslcontext is None. + assert sslcontext is not None try: async with ceil_timeout( @@ -1288,7 +1268,7 @@ async def _create_direct_connection( *, client_error: Type[Exception] = ClientConnectorError, ) -> Tuple[asyncio.Transport, ResponseHandler]: - sslcontext = await self._get_ssl_context(req) + sslcontext = self._get_ssl_context(req) fingerprint = self._get_fingerprint(req) host = req.url.raw_host diff --git a/tests/test_connector.py b/tests/test_connector.py index 0129f0cc330..9f9dbe66c28 100644 --- a/tests/test_connector.py +++ b/tests/test_connector.py @@ -1,5 +1,4 @@ # Tests of http client with custom Connector - import asyncio import gc import hashlib @@ -9,8 +8,9 @@ import sys import uuid from collections import deque +from concurrent import futures from contextlib import closing, suppress -from typing import Any, List, Optional, Type +from typing import Any, List, Literal, Optional from unittest import mock import pytest @@ -18,10 +18,21 @@ from yarl import URL import aiohttp -from aiohttp import client, web -from aiohttp.client import ClientRequest, ClientTimeout +from aiohttp import ( + ClientRequest, + ClientTimeout, + client, + connector as connector_module, + web, +) from aiohttp.client_reqrep import ConnectionKey -from aiohttp.connector import Connection, TCPConnector, _DNSCacheTable +from aiohttp.connector import ( + _SSL_CONTEXT_UNVERIFIED, + _SSL_CONTEXT_VERIFIED, + Connection, + TCPConnector, + _DNSCacheTable, +) from aiohttp.locks import EventResultOrError from aiohttp.test_utils import make_mocked_coro, unused_port from aiohttp.tracing import Trace @@ -1540,23 +1551,11 @@ async def test_tcp_connector_clear_dns_cache_bad_args(loop) -> None: conn.clear_dns_cache("localhost") -async def test_dont_recreate_ssl_context() -> None: - conn = aiohttp.TCPConnector() - ctx = await conn._make_or_get_ssl_context(True) - assert ctx is await conn._make_or_get_ssl_context(True) - - -async def test_dont_recreate_ssl_context2() -> None: - conn = aiohttp.TCPConnector() - ctx = await conn._make_or_get_ssl_context(False) - assert ctx is await conn._make_or_get_ssl_context(False) - - async def test___get_ssl_context1() -> None: conn = aiohttp.TCPConnector() req = mock.Mock() req.is_ssl.return_value = False - assert await conn._get_ssl_context(req) is None + assert conn._get_ssl_context(req) is None async def test___get_ssl_context2(loop) -> None: @@ -1565,7 +1564,7 @@ async def test___get_ssl_context2(loop) -> None: req = mock.Mock() req.is_ssl.return_value = True req.ssl = ctx - assert await conn._get_ssl_context(req) is ctx + assert conn._get_ssl_context(req) is ctx async def test___get_ssl_context3(loop) -> None: @@ -1574,7 +1573,7 @@ async def test___get_ssl_context3(loop) -> None: req = mock.Mock() req.is_ssl.return_value = True req.ssl = True - assert await conn._get_ssl_context(req) is ctx + assert conn._get_ssl_context(req) is ctx async def test___get_ssl_context4(loop) -> None: @@ -1583,9 +1582,7 @@ async def test___get_ssl_context4(loop) -> None: req = mock.Mock() req.is_ssl.return_value = True req.ssl = False - assert await conn._get_ssl_context(req) is await conn._make_or_get_ssl_context( - False - ) + assert conn._get_ssl_context(req) is _SSL_CONTEXT_UNVERIFIED async def test___get_ssl_context5(loop) -> None: @@ -1594,9 +1591,7 @@ async def test___get_ssl_context5(loop) -> None: req = mock.Mock() req.is_ssl.return_value = True req.ssl = aiohttp.Fingerprint(hashlib.sha256(b"1").digest()) - assert await conn._get_ssl_context(req) is await conn._make_or_get_ssl_context( - False - ) + assert conn._get_ssl_context(req) is _SSL_CONTEXT_UNVERIFIED async def test___get_ssl_context6() -> None: @@ -1604,7 +1599,7 @@ async def test___get_ssl_context6() -> None: req = mock.Mock() req.is_ssl.return_value = True req.ssl = True - assert await conn._get_ssl_context(req) is await conn._make_or_get_ssl_context(True) + assert conn._get_ssl_context(req) is _SSL_CONTEXT_VERIFIED async def test_ssl_context_once() -> None: @@ -1616,31 +1611,9 @@ async def test_ssl_context_once() -> None: req = mock.Mock() req.is_ssl.return_value = True req.ssl = True - assert await conn1._get_ssl_context(req) is await conn1._make_or_get_ssl_context( - True - ) - assert await conn2._get_ssl_context(req) is await conn1._make_or_get_ssl_context( - True - ) - assert await conn3._get_ssl_context(req) is await conn1._make_or_get_ssl_context( - True - ) - assert conn1._made_ssl_context is conn2._made_ssl_context is conn3._made_ssl_context - assert True in conn1._made_ssl_context - - -@pytest.mark.parametrize("exception", [OSError, ssl.SSLError, asyncio.CancelledError]) -async def test_ssl_context_creation_raises(exception: Type[BaseException]) -> None: - """Test that we try again if SSLContext creation fails the first time.""" - conn = aiohttp.TCPConnector() - conn._made_ssl_context.clear() - - with mock.patch.object( - conn, "_make_ssl_context", side_effect=exception - ), pytest.raises(exception): - await conn._make_or_get_ssl_context(True) - - assert isinstance(await conn._make_or_get_ssl_context(True), ssl.SSLContext) + assert conn1._get_ssl_context(req) is _SSL_CONTEXT_VERIFIED + assert conn2._get_ssl_context(req) is _SSL_CONTEXT_VERIFIED + assert conn3._get_ssl_context(req) is _SSL_CONTEXT_VERIFIED async def test_close_twice(loop) -> None: @@ -2717,3 +2690,42 @@ async def allow_connection_and_add_dummy_waiter(): ) await connector.close() + + +def test_connector_multiple_event_loop() -> None: + """Test the connector with multiple event loops.""" + + async def async_connect() -> Literal[True]: + conn = aiohttp.TCPConnector() + loop = asyncio.get_running_loop() + req = ClientRequest("GET", URL("https://127.0.0.1"), loop=loop) + with suppress(aiohttp.ClientConnectorError): + with mock.patch.object( + conn._loop, + "create_connection", + autospec=True, + spec_set=True, + side_effect=ssl.CertificateError, + ): + await conn.connect(req, [], ClientTimeout()) + return True + + def test_connect() -> Literal[True]: + loop = asyncio.new_event_loop() + try: + return loop.run_until_complete(async_connect()) + finally: + loop.close() + + with futures.ThreadPoolExecutor() as executor: + res_list = [executor.submit(test_connect) for _ in range(2)] + raw_response_list = [res.result() for res in futures.as_completed(res_list)] + + assert raw_response_list == [True, True] + + +def test_default_ssl_context_creation_without_ssl() -> None: + """Verify _make_ssl_context does not raise when ssl is not available.""" + with mock.patch.object(connector_module, "ssl", None): + assert connector_module._make_ssl_context(False) is None + assert connector_module._make_ssl_context(True) is None diff --git a/tests/test_proxy.py b/tests/test_proxy.py index c5e98deb8a5..4fa5e932098 100644 --- a/tests/test_proxy.py +++ b/tests/test_proxy.py @@ -12,6 +12,7 @@ import aiohttp from aiohttp.client_reqrep import ClientRequest, ClientResponse +from aiohttp.connector import _SSL_CONTEXT_VERIFIED from aiohttp.helpers import TimerNoop from aiohttp.test_utils import make_mocked_coro @@ -817,7 +818,7 @@ async def make_conn(): self.loop.start_tls.assert_called_with( mock.ANY, mock.ANY, - self.loop.run_until_complete(connector._make_or_get_ssl_context(True)), + _SSL_CONTEXT_VERIFIED, server_hostname="www.python.org", ssl_handshake_timeout=mock.ANY, ) From 8f30c91788196c774b31c1c23311d27a7ca04fc9 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 23:51:00 +0000 Subject: [PATCH 159/296] [PR #9031/9f0ae074 backport][3.11] Avoid tracing overhead in http_writer when there are no traces (#9046) Co-authored-by: J. Nick Koston --- CHANGES/9031.misc.rst | 1 + aiohttp/client_reqrep.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 CHANGES/9031.misc.rst diff --git a/CHANGES/9031.misc.rst b/CHANGES/9031.misc.rst new file mode 100644 index 00000000000..1deab5230f7 --- /dev/null +++ b/CHANGES/9031.misc.rst @@ -0,0 +1 @@ +Tracing overhead is avoided in the http writer when there are no active traces -- by user:`bdraco`. diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index d7d5f63ec18..4ea1070a0fd 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -694,11 +694,15 @@ async def send(self, conn: "Connection") -> "ClientResponse": writer = StreamWriter( protocol, self.loop, - on_chunk_sent=functools.partial( - self._on_chunk_request_sent, self.method, self.url + on_chunk_sent=( + functools.partial(self._on_chunk_request_sent, self.method, self.url) + if self._traces + else None ), - on_headers_sent=functools.partial( - self._on_headers_request_sent, self.method, self.url + on_headers_sent=( + functools.partial(self._on_headers_request_sent, self.method, self.url) + if self._traces + else None ), ) From 5e99c4f1c74fa07426541f0b4ab8a0ea3a36518c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 7 Sep 2024 21:18:43 +0000 Subject: [PATCH 160/296] Bump yarl from 1.9.7 to 1.10.0 (#9056) Bumps [yarl](https://github.com/aio-libs/yarl) from 1.9.7 to 1.10.0.
Release notes

Sourced from yarl's releases.

1.10.0

Bug fixes

  • Fixed joining a path when the existing path was empty -- by :user:bdraco.

    A regression in :meth:URL.join() <yarl.URL.join> was introduced in #1082.

    Related issues and pull requests on GitHub: #1118.

Features

  • Added :meth:URL.without_query_params() <yarl.URL.without_query_params> method, to drop some parameters from query string -- by :user:hongquan.

    Related issues and pull requests on GitHub: #774, #898, #1010.

  • The previously protected types _SimpleQuery, _QueryVariable, and _Query are now available for use externally as SimpleQuery, QueryVariable, and Query -- by :user:bdraco.

    Related issues and pull requests on GitHub: #1050, #1113.

Contributor-facing changes

  • Replaced all :class:~typing.Optional with :class:~typing.Union -- by :user:bdraco.

    Related issues and pull requests on GitHub: #1095.

Miscellaneous internal changes

  • Significantly improved performance of parsing the network location -- by :user:bdraco.

    Related issues and pull requests on GitHub: #1112.

  • Added internal types to the cache to prevent future refactoring errors -- by :user:bdraco.

    Related issues and pull requests on GitHub: #1117.


... (truncated)

Changelog

Sourced from yarl's changelog.

1.10.0

(2024-09-06)

Bug fixes

  • Fixed joining a path when the existing path was empty -- by :user:bdraco.

    A regression in :meth:URL.join() <yarl.URL.join> was introduced in :issue:1082.

    Related issues and pull requests on GitHub: :issue:1118.

Features

  • Added :meth:URL.without_query_params() <yarl.URL.without_query_params> method, to drop some parameters from query string -- by :user:hongquan.

    Related issues and pull requests on GitHub: :issue:774, :issue:898, :issue:1010.

  • The previously protected types _SimpleQuery, _QueryVariable, and _Query are now available for use externally as SimpleQuery, QueryVariable, and Query -- by :user:bdraco.

    Related issues and pull requests on GitHub: :issue:1050, :issue:1113.

Contributor-facing changes

  • Replaced all :class:~typing.Optional with :class:~typing.Union -- by :user:bdraco.

    Related issues and pull requests on GitHub: :issue:1095.

Miscellaneous internal changes

  • Significantly improved performance of parsing the network location -- by :user:bdraco.

    Related issues and pull requests on GitHub: :issue:1112.

  • Added internal types to the cache to prevent future refactoring errors -- by :user:bdraco.

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=yarl&package-manager=pip&previous-version=1.9.7&new-version=1.10.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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> Co-authored-by: J. Nick Koston --- aiohttp/web_urldispatcher.py | 2 +- requirements/base.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 7d74dd7ab16..07c8f6e6ff3 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -39,7 +39,7 @@ cast, ) -from yarl import URL, __version__ as yarl_version # type: ignore[attr-defined] +from yarl import URL, __version__ as yarl_version from . import hdrs from .abc import AbstractMatchInfo, AbstractRouter, AbstractView diff --git a/requirements/base.txt b/requirements/base.txt index 5c629050db9..e828e202e26 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -38,5 +38,5 @@ pycparser==2.22 # via cffi uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in -yarl==1.9.7 +yarl==1.10.0 # via -r requirements/runtime-deps.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 420d46bbf6c..b7368d5d3a0 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -286,7 +286,7 @@ webcolors==24.8.0 # via blockdiag wheel==0.44.0 # via pip-tools -yarl==1.9.7 +yarl==1.10.0 # via -r requirements/runtime-deps.in zipp==3.20.1 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index e5ed6030ae1..475eb077b8c 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -278,7 +278,7 @@ webcolors==24.8.0 # via blockdiag wheel==0.44.0 # via pip-tools -yarl==1.9.7 +yarl==1.10.0 # via -r requirements/runtime-deps.in zipp==3.20.1 # via diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index be37aa14544..29d2f84db32 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -32,5 +32,5 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -yarl==1.9.7 +yarl==1.10.0 # via -r requirements/runtime-deps.in diff --git a/requirements/test.txt b/requirements/test.txt index 82dd51bcf50..b5b839c2a48 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -136,5 +136,5 @@ uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpytho # via -r requirements/base.in wait-for-it==2.2.2 # via -r requirements/test.in -yarl==1.9.7 +yarl==1.10.0 # via -r requirements/runtime-deps.in From f631015d44e1e452f405c7b6057ee8794abaed56 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sat, 7 Sep 2024 21:18:50 +0000 Subject: [PATCH 161/296] [PR #9054/c3da10cd backport][3.10] Sync reify Cython implementation with yarl (#9057) Co-authored-by: J. Nick Koston --- CHANGES/9054.misc.rst | 1 + aiohttp/_helpers.pyx | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 CHANGES/9054.misc.rst diff --git a/CHANGES/9054.misc.rst b/CHANGES/9054.misc.rst new file mode 100644 index 00000000000..ddc71f453e5 --- /dev/null +++ b/CHANGES/9054.misc.rst @@ -0,0 +1 @@ +Improved performance of reify Cython implementation -- by :user:`bdraco`. diff --git a/aiohttp/_helpers.pyx b/aiohttp/_helpers.pyx index 665f367c5de..5f089225dc8 100644 --- a/aiohttp/_helpers.pyx +++ b/aiohttp/_helpers.pyx @@ -1,3 +1,6 @@ + +cdef _sentinel = object() + cdef class reify: """Use as a class method decorator. It operates almost exactly like the Python `@property` decorator, but it puts the result of the @@ -19,17 +22,14 @@ cdef class reify: return self.wrapped.__doc__ def __get__(self, inst, owner): - try: - try: - return inst._cache[self.name] - except KeyError: - val = self.wrapped(inst) - inst._cache[self.name] = val - return val - except AttributeError: - if inst is None: - return self - raise + if inst is None: + return self + cdef dict cache = inst._cache + val = cache.get(self.name, _sentinel) + if val is _sentinel: + val = self.wrapped(inst) + cache[self.name] = val + return val def __set__(self, inst, value): raise AttributeError("reified property is read-only") From 2f30ac47c48d3973383673ef6d3a85aca87a9e69 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sat, 7 Sep 2024 21:18:58 +0000 Subject: [PATCH 162/296] [PR #9054/c3da10cd backport][3.11] Sync reify Cython implementation with yarl (#9058) Co-authored-by: J. Nick Koston --- CHANGES/9054.misc.rst | 1 + aiohttp/_helpers.pyx | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 CHANGES/9054.misc.rst diff --git a/CHANGES/9054.misc.rst b/CHANGES/9054.misc.rst new file mode 100644 index 00000000000..ddc71f453e5 --- /dev/null +++ b/CHANGES/9054.misc.rst @@ -0,0 +1 @@ +Improved performance of reify Cython implementation -- by :user:`bdraco`. diff --git a/aiohttp/_helpers.pyx b/aiohttp/_helpers.pyx index 665f367c5de..5f089225dc8 100644 --- a/aiohttp/_helpers.pyx +++ b/aiohttp/_helpers.pyx @@ -1,3 +1,6 @@ + +cdef _sentinel = object() + cdef class reify: """Use as a class method decorator. It operates almost exactly like the Python `@property` decorator, but it puts the result of the @@ -19,17 +22,14 @@ cdef class reify: return self.wrapped.__doc__ def __get__(self, inst, owner): - try: - try: - return inst._cache[self.name] - except KeyError: - val = self.wrapped(inst) - inst._cache[self.name] = val - return val - except AttributeError: - if inst is None: - return self - raise + if inst is None: + return self + cdef dict cache = inst._cache + val = cache.get(self.name, _sentinel) + if val is _sentinel: + val = self.wrapped(inst) + cache[self.name] = val + return val def __set__(self, inst, value): raise AttributeError("reified property is read-only") From d049f4d1321ad1e1f5ba18063e47b723f3175f06 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sat, 7 Sep 2024 21:56:11 +0000 Subject: [PATCH 163/296] [PR #9055/11a96fcc backport][3.10] Add xfail test for issue #5180 (#9059) Co-authored-by: J. Nick Koston --- tests/test_web_websocket_functional.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_web_websocket_functional.py b/tests/test_web_websocket_functional.py index 2be54486ee9..5ddcdbce7d0 100644 --- a/tests/test_web_websocket_functional.py +++ b/tests/test_web_websocket_functional.py @@ -1119,3 +1119,23 @@ async def on_shutdown(app: web.Application) -> None: assert reply.extra == "Server shutdown" assert websocket.closed is True + + +@pytest.mark.xfail(reason="close never reaches client per issue #5180") +async def test_ws_close_return_code(aiohttp_client: AiohttpClient) -> None: + """Test that the close code is returned when the server closes the connection.""" + + async def handler(request: web.Request) -> web.WebSocketResponse: + ws = web.WebSocketResponse() + await ws.prepare(request) + await ws.close() + return ws + + app = web.Application() + app.router.add_route("GET", "/", handler) + client = await aiohttp_client(app) + resp = await client.ws_connect("/") + await resp.send_str("some data") + await asyncio.sleep(0.1) + await resp.receive() + assert resp.close_code is WSCloseCode.OK From 2d6898a0d368b48c9039244a819fcd1a246288bc Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sat, 7 Sep 2024 22:05:43 +0000 Subject: [PATCH 164/296] [PR #9055/11a96fcc backport][3.11] Add xfail test for issue #5180 (#9060) Co-authored-by: J. Nick Koston --- tests/test_web_websocket_functional.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_web_websocket_functional.py b/tests/test_web_websocket_functional.py index 0ebd41db502..dfe3f59c5fd 100644 --- a/tests/test_web_websocket_functional.py +++ b/tests/test_web_websocket_functional.py @@ -1119,3 +1119,23 @@ async def on_shutdown(app: web.Application) -> None: assert reply.extra == "Server shutdown" assert websocket.closed is True + + +@pytest.mark.xfail(reason="close never reaches client per issue #5180") +async def test_ws_close_return_code(aiohttp_client: AiohttpClient) -> None: + """Test that the close code is returned when the server closes the connection.""" + + async def handler(request: web.Request) -> web.WebSocketResponse: + ws = web.WebSocketResponse() + await ws.prepare(request) + await ws.close() + return ws + + app = web.Application() + app.router.add_route("GET", "/", handler) + client = await aiohttp_client(app) + resp = await client.ws_connect("/") + await resp.send_str("some data") + await asyncio.sleep(0.1) + await resp.receive() + assert resp.close_code is WSCloseCode.OK From 69ed00f069df6a753b0e8792d3b68dc209af9813 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 Sep 2024 12:59:10 -0500 Subject: [PATCH 165/296] [PR #9065/b6196e7 backport][3.10] Add coverage for combining an existing query string with params (#9066) --- tests/test_client_functional.py | 37 +++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 7de195264ac..58add05f577 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -672,8 +672,41 @@ async def handler(request): assert 200 == resp.status -async def test_drop_params_on_redirect(aiohttp_client) -> None: - async def handler_redirect(request): +async def test_params_and_query_string(aiohttp_client: AiohttpClient) -> None: + """Test combining params with an existing query_string.""" + + async def handler(request: web.Request) -> web.Response: + assert request.rel_url.query_string == "q=abc&q=test&d=dog" + return web.Response() + + app = web.Application() + app.router.add_route("GET", "/", handler) + client = await aiohttp_client(app) + + async with client.get("/?q=abc", params="q=test&d=dog") as resp: + assert resp.status == 200 + + +@pytest.mark.parametrize("params", [None, "", {}, MultiDict()]) +async def test_empty_params_and_query_string( + aiohttp_client: AiohttpClient, params: Any +) -> None: + """Test combining empty params with an existing query_string.""" + + async def handler(request: web.Request) -> web.Response: + assert request.rel_url.query_string == "q=abc" + return web.Response() + + app = web.Application() + app.router.add_route("GET", "/", handler) + client = await aiohttp_client(app) + + async with client.get("/?q=abc", params=params) as resp: + assert resp.status == 200 + + +async def test_drop_params_on_redirect(aiohttp_client: AiohttpClient) -> None: + async def handler_redirect(request: web.Request) -> web.Response: return web.Response(status=301, headers={"Location": "/ok?a=redirect"}) async def handler_ok(request): From e34f91bdee226f81467101eae895d3a01b1e1ab9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 Sep 2024 13:04:49 -0500 Subject: [PATCH 166/296] [PR #9065/b6196e7 backport][3.11] Add coverage for combining an existing query string with params (#9067) --- tests/test_client_functional.py | 37 +++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 74c4d99765e..a350171dacf 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -672,8 +672,41 @@ async def handler(request): assert 200 == resp.status -async def test_drop_params_on_redirect(aiohttp_client) -> None: - async def handler_redirect(request): +async def test_params_and_query_string(aiohttp_client: AiohttpClient) -> None: + """Test combining params with an existing query_string.""" + + async def handler(request: web.Request) -> web.Response: + assert request.rel_url.query_string == "q=abc&q=test&d=dog" + return web.Response() + + app = web.Application() + app.router.add_route("GET", "/", handler) + client = await aiohttp_client(app) + + async with client.get("/?q=abc", params="q=test&d=dog") as resp: + assert resp.status == 200 + + +@pytest.mark.parametrize("params", [None, "", {}, MultiDict()]) +async def test_empty_params_and_query_string( + aiohttp_client: AiohttpClient, params: Any +) -> None: + """Test combining empty params with an existing query_string.""" + + async def handler(request: web.Request) -> web.Response: + assert request.rel_url.query_string == "q=abc" + return web.Response() + + app = web.Application() + app.router.add_route("GET", "/", handler) + client = await aiohttp_client(app) + + async with client.get("/?q=abc", params=params) as resp: + assert resp.status == 200 + + +async def test_drop_params_on_redirect(aiohttp_client: AiohttpClient) -> None: + async def handler_redirect(request: web.Request) -> web.Response: return web.Response(status=301, headers={"Location": "/ok?a=redirect"}) async def handler_ok(request): From 2bc41815ac71bb1c5a759b1e0452ddd76ee3f083 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 9 Sep 2024 00:11:07 +0100 Subject: [PATCH 167/296] [3.11] Drop async-timeout from dependencies (#9069) --- README.rst | 1 - docs/index.rst | 1 - 2 files changed, 2 deletions(-) diff --git a/README.rst b/README.rst index 45b647437e3..470ced9b29c 100644 --- a/README.rst +++ b/README.rst @@ -157,7 +157,6 @@ Please add *aiohttp* tag to your question there. Requirements ============ -- async-timeout_ - attrs_ - multidict_ - yarl_ diff --git a/docs/index.rst b/docs/index.rst index 9692152cb99..4ce20aca643 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -147,7 +147,6 @@ or have some suggestion in order to improve the library. Dependencies ============ -- *async_timeout* - *attrs* - *multidict* - *yarl* From d36317b472dfb72d57815448a6356235dc3cca0c Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 8 Sep 2024 23:36:31 +0000 Subject: [PATCH 168/296] [PR #9069/2bc41815 backport][3.10] [3.11] Drop async-timeout from dependencies (#9070) **This is a backport of PR #9069 as merged into 3.11 (2bc41815ac71bb1c5a759b1e0452ddd76ee3f083).** Co-authored-by: Sam Bull --- README.rst | 1 - docs/index.rst | 1 - 2 files changed, 2 deletions(-) diff --git a/README.rst b/README.rst index 45b647437e3..470ced9b29c 100644 --- a/README.rst +++ b/README.rst @@ -157,7 +157,6 @@ Please add *aiohttp* tag to your question there. Requirements ============ -- async-timeout_ - attrs_ - multidict_ - yarl_ diff --git a/docs/index.rst b/docs/index.rst index 9692152cb99..4ce20aca643 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -147,7 +147,6 @@ or have some suggestion in order to improve the library. Dependencies ============ -- *async_timeout* - *attrs* - *multidict* - *yarl* From 3794391a39f5314615e2f687caa9ff7361a45a06 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 Sep 2024 19:29:07 -0500 Subject: [PATCH 169/296] [PR #9072/7fb1631 backport][3.11] Bump yarl to 1.11.0 (#9074) --- requirements/base.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index e828e202e26..87300dd8515 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -38,5 +38,5 @@ pycparser==2.22 # via cffi uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in -yarl==1.10.0 +yarl==1.11.0 # via -r requirements/runtime-deps.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index b7368d5d3a0..2b04b656eb6 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -286,7 +286,7 @@ webcolors==24.8.0 # via blockdiag wheel==0.44.0 # via pip-tools -yarl==1.10.0 +yarl==1.11.0 # via -r requirements/runtime-deps.in zipp==3.20.1 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 475eb077b8c..a8e08879fd3 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -278,7 +278,7 @@ webcolors==24.8.0 # via blockdiag wheel==0.44.0 # via pip-tools -yarl==1.10.0 +yarl==1.11.0 # via -r requirements/runtime-deps.in zipp==3.20.1 # via diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index 29d2f84db32..89e30717677 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -32,5 +32,5 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -yarl==1.10.0 +yarl==1.11.0 # via -r requirements/runtime-deps.in diff --git a/requirements/test.txt b/requirements/test.txt index b5b839c2a48..a065d607643 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -136,5 +136,5 @@ uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpytho # via -r requirements/base.in wait-for-it==2.2.2 # via -r requirements/test.in -yarl==1.10.0 +yarl==1.11.0 # via -r requirements/runtime-deps.in From 965aac1ebbd90bbec92a1ba11094949c698d4165 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 Sep 2024 19:47:15 -0500 Subject: [PATCH 170/296] [PR #9072/7fb1631 backport][3.10] Bump yarl to 1.11.0 (#9073) --- aiohttp/web_urldispatcher.py | 2 +- requirements/base.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 0b300e84da1..c302351500b 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -39,7 +39,7 @@ cast, ) -from yarl import URL, __version__ as yarl_version # type: ignore[attr-defined] +from yarl import URL, __version__ as yarl_version from . import hdrs from .abc import AbstractMatchInfo, AbstractRouter, AbstractView diff --git a/requirements/base.txt b/requirements/base.txt index 0ab66407cde..6604aa0f6c5 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -38,5 +38,5 @@ pycparser==2.21 # via cffi uvloop==0.19.0 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in -yarl==1.9.6 +yarl==1.11.0 # via -r requirements/runtime-deps.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 45a2ce5dea1..f3109af5fb7 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -274,7 +274,7 @@ webcolors==1.11.1 # via blockdiag wheel==0.37.0 # via pip-tools -yarl==1.9.6 +yarl==1.11.0 # via -r requirements/runtime-deps.in zipp==3.17.0 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index d7b0fbc81c7..54c0157b01a 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -261,7 +261,7 @@ webcolors==1.13 # via blockdiag wheel==0.41.0 # via pip-tools -yarl==1.9.6 +yarl==1.11.0 # via -r requirements/runtime-deps.in zipp==3.17.0 # via diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index 3a0b956d3de..279a9525fc5 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -32,5 +32,5 @@ pycares==4.3.0 # via aiodns pycparser==2.21 # via cffi -yarl==1.9.6 +yarl==1.11.0 # via -r requirements/runtime-deps.in diff --git a/requirements/test.txt b/requirements/test.txt index daae1ddb6ef..ad9ec0ace39 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -125,5 +125,5 @@ uvloop==0.19.0 ; platform_system != "Windows" and implementation_name == "cpytho # via -r requirements/base.in wait-for-it==2.2.2 # via -r requirements/test.in -yarl==1.9.6 +yarl==1.11.0 # via -r requirements/runtime-deps.in From 5b2b77dda5d52468e826252df3d736f2de8c4965 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 01:18:13 +0000 Subject: [PATCH 171/296] [PR #9071/a33270bc backport][3.10] Add tests for websocket close (#9075) Co-authored-by: J. Nick Koston closes #5180 --- tests/test_web_websocket_functional.py | 28 +++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/tests/test_web_websocket_functional.py b/tests/test_web_websocket_functional.py index 5ddcdbce7d0..42faff8e517 100644 --- a/tests/test_web_websocket_functional.py +++ b/tests/test_web_websocket_functional.py @@ -1121,10 +1121,31 @@ async def on_shutdown(app: web.Application) -> None: assert websocket.closed is True -@pytest.mark.xfail(reason="close never reaches client per issue #5180") async def test_ws_close_return_code(aiohttp_client: AiohttpClient) -> None: """Test that the close code is returned when the server closes the connection.""" + async def handler(request: web.Request) -> web.WebSocketResponse: + ws = web.WebSocketResponse() + await ws.prepare(request) + await ws.receive() + await ws.close() + return ws + + app = web.Application() + app.router.add_route("GET", "/", handler) + client = await aiohttp_client(app) + resp = await client.ws_connect("/") + await resp.send_str("some data") + msg = await resp.receive() + assert msg.type is aiohttp.WSMsgType.CLOSE + assert resp.close_code == WSCloseCode.OK + + +async def test_abnormal_closure_when_server_does_not_receive( + aiohttp_client: AiohttpClient, +) -> None: + """Test abnormal closure when the server closes and a message is pending.""" + async def handler(request: web.Request) -> web.WebSocketResponse: ws = web.WebSocketResponse() await ws.prepare(request) @@ -1137,5 +1158,6 @@ async def handler(request: web.Request) -> web.WebSocketResponse: resp = await client.ws_connect("/") await resp.send_str("some data") await asyncio.sleep(0.1) - await resp.receive() - assert resp.close_code is WSCloseCode.OK + msg = await resp.receive() + assert msg.type is aiohttp.WSMsgType.CLOSE + assert resp.close_code == WSCloseCode.ABNORMAL_CLOSURE From 9587c45452b0ec2eb656aa502d176152a2eb4fe2 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 01:36:56 +0000 Subject: [PATCH 172/296] [PR #9071/a33270bc backport][3.11] Add tests for websocket close (#9076) Co-authored-by: J. Nick Koston closes #5180 --- tests/test_web_websocket_functional.py | 28 +++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/tests/test_web_websocket_functional.py b/tests/test_web_websocket_functional.py index dfe3f59c5fd..5770eee87dc 100644 --- a/tests/test_web_websocket_functional.py +++ b/tests/test_web_websocket_functional.py @@ -1121,10 +1121,31 @@ async def on_shutdown(app: web.Application) -> None: assert websocket.closed is True -@pytest.mark.xfail(reason="close never reaches client per issue #5180") async def test_ws_close_return_code(aiohttp_client: AiohttpClient) -> None: """Test that the close code is returned when the server closes the connection.""" + async def handler(request: web.Request) -> web.WebSocketResponse: + ws = web.WebSocketResponse() + await ws.prepare(request) + await ws.receive() + await ws.close() + return ws + + app = web.Application() + app.router.add_route("GET", "/", handler) + client = await aiohttp_client(app) + resp = await client.ws_connect("/") + await resp.send_str("some data") + msg = await resp.receive() + assert msg.type is aiohttp.WSMsgType.CLOSE + assert resp.close_code == WSCloseCode.OK + + +async def test_abnormal_closure_when_server_does_not_receive( + aiohttp_client: AiohttpClient, +) -> None: + """Test abnormal closure when the server closes and a message is pending.""" + async def handler(request: web.Request) -> web.WebSocketResponse: ws = web.WebSocketResponse() await ws.prepare(request) @@ -1137,5 +1158,6 @@ async def handler(request: web.Request) -> web.WebSocketResponse: resp = await client.ws_connect("/") await resp.send_str("some data") await asyncio.sleep(0.1) - await resp.receive() - assert resp.close_code is WSCloseCode.OK + msg = await resp.receive() + assert msg.type is aiohttp.WSMsgType.CLOSE + assert resp.close_code == WSCloseCode.ABNORMAL_CLOSURE From 7fd5e73eb44d3f6a5df807d2393d46481a8c1c56 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 Sep 2024 20:57:40 -0500 Subject: [PATCH 173/296] [PR #9068/841d00e backport][3.11] Use URL.extend_query to add params in `ClientRequest` (#9078) --- CHANGES/9068.misc.rst | 3 +++ aiohttp/client_reqrep.py | 14 +++++++++----- tests/test_client_functional.py | 26 +++++++++++++++++++------- 3 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 CHANGES/9068.misc.rst diff --git a/CHANGES/9068.misc.rst b/CHANGES/9068.misc.rst new file mode 100644 index 00000000000..7ce5ec5c839 --- /dev/null +++ b/CHANGES/9068.misc.rst @@ -0,0 +1,3 @@ +Use :meth:`URL.extend_query() ` to extend query params (requires yarl 1.11.0+) -- by :user:`bdraco`. + +If yarl is older than 1.11.0, the previous slower hand rolled version will be used. diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 4ea1070a0fd..e0232a40c4c 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -27,7 +27,7 @@ import attr from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy -from yarl import URL +from yarl import URL, __version__ as yarl_version from . import hdrs, helpers, http, multipart, payload from .abc import AbstractStreamWriter @@ -88,6 +88,7 @@ _CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") +_YARL_SUPPORTS_EXTEND_QUERY = tuple(map(int, yarl_version.split(".")[:2])) >= (1, 11) json_re = re.compile(r"^application/(?:[\w.+-]+?\+)?json") @@ -299,10 +300,13 @@ def __init__( # assert session is not None self._session = cast("ClientSession", session) if params: - q = MultiDict(url.query) - url2 = url.with_query(params) - q.extend(url2.query) - url = url.with_query(q) + if _YARL_SUPPORTS_EXTEND_QUERY: + url = url.extend_query(params) + else: + q = MultiDict(url.query) + url2 = url.with_query(params) + q.extend(url2.query) + url = url.with_query(q) self.original_url = url self.url = url.with_fragment(None) self.method = method.upper() diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index a350171dacf..4edbfa2cfeb 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -20,7 +20,7 @@ from yarl import URL import aiohttp -from aiohttp import Fingerprint, ServerFingerprintMismatch, hdrs, web +from aiohttp import Fingerprint, ServerFingerprintMismatch, client_reqrep, hdrs, web from aiohttp.abc import AbstractResolver from aiohttp.client_exceptions import ( InvalidURL, @@ -672,7 +672,10 @@ async def handler(request): assert 200 == resp.status -async def test_params_and_query_string(aiohttp_client: AiohttpClient) -> None: +@pytest.mark.parametrize("yarl_supports_extend_query", [True, False]) +async def test_params_and_query_string( + aiohttp_client: AiohttpClient, yarl_supports_extend_query: bool +) -> None: """Test combining params with an existing query_string.""" async def handler(request: web.Request) -> web.Response: @@ -683,13 +686,18 @@ async def handler(request: web.Request) -> web.Response: app.router.add_route("GET", "/", handler) client = await aiohttp_client(app) - async with client.get("/?q=abc", params="q=test&d=dog") as resp: - assert resp.status == 200 + # Ensure the old path is tested for old yarl versions + with mock.patch.object( + client_reqrep, "_YARL_SUPPORTS_EXTEND_QUERY", yarl_supports_extend_query + ): + async with client.get("/?q=abc", params="q=test&d=dog") as resp: + assert resp.status == 200 @pytest.mark.parametrize("params", [None, "", {}, MultiDict()]) +@pytest.mark.parametrize("yarl_supports_extend_query", [True, False]) async def test_empty_params_and_query_string( - aiohttp_client: AiohttpClient, params: Any + aiohttp_client: AiohttpClient, params: Any, yarl_supports_extend_query: bool ) -> None: """Test combining empty params with an existing query_string.""" @@ -701,8 +709,12 @@ async def handler(request: web.Request) -> web.Response: app.router.add_route("GET", "/", handler) client = await aiohttp_client(app) - async with client.get("/?q=abc", params=params) as resp: - assert resp.status == 200 + # Ensure the old path is tested for old yarl versions + with mock.patch.object( + client_reqrep, "_YARL_SUPPORTS_EXTEND_QUERY", yarl_supports_extend_query + ): + async with client.get("/?q=abc", params=params) as resp: + assert resp.status == 200 async def test_drop_params_on_redirect(aiohttp_client: AiohttpClient) -> None: From 524bd16663338a535f1002b44e91bfa9206337fb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 Sep 2024 20:57:48 -0500 Subject: [PATCH 174/296] [PR #9068/841d00e backport][3.10] Use URL.extend_query to add params in `ClientRequest` (#9077) --- CHANGES/9068.misc.rst | 3 +++ aiohttp/client_reqrep.py | 14 +++++++++----- tests/test_client_functional.py | 26 +++++++++++++++++++------- 3 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 CHANGES/9068.misc.rst diff --git a/CHANGES/9068.misc.rst b/CHANGES/9068.misc.rst new file mode 100644 index 00000000000..7ce5ec5c839 --- /dev/null +++ b/CHANGES/9068.misc.rst @@ -0,0 +1,3 @@ +Use :meth:`URL.extend_query() ` to extend query params (requires yarl 1.11.0+) -- by :user:`bdraco`. + +If yarl is older than 1.11.0, the previous slower hand rolled version will be used. diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index c261af0421e..60d70724b3d 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -27,7 +27,7 @@ import attr from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy -from yarl import URL +from yarl import URL, __version__ as yarl_version from . import hdrs, helpers, http, multipart, payload from .abc import AbstractStreamWriter @@ -88,6 +88,7 @@ _CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") +_YARL_SUPPORTS_EXTEND_QUERY = tuple(map(int, yarl_version.split(".")[:2])) >= (1, 11) json_re = re.compile(r"^application/(?:[\w.+-]+?\+)?json") @@ -301,10 +302,13 @@ def __init__( # assert session is not None self._session = cast("ClientSession", session) if params: - q = MultiDict(url.query) - url2 = url.with_query(params) - q.extend(url2.query) - url = url.with_query(q) + if _YARL_SUPPORTS_EXTEND_QUERY: + url = url.extend_query(params) + else: + q = MultiDict(url.query) + url2 = url.with_query(params) + q.extend(url2.query) + url = url.with_query(q) self.original_url = url self.url = url.with_fragment(None) self.method = method.upper() diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 58add05f577..9325cc17e48 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -20,7 +20,7 @@ from yarl import URL import aiohttp -from aiohttp import Fingerprint, ServerFingerprintMismatch, hdrs, web +from aiohttp import Fingerprint, ServerFingerprintMismatch, client_reqrep, hdrs, web from aiohttp.abc import AbstractResolver from aiohttp.client_exceptions import ( InvalidURL, @@ -672,7 +672,10 @@ async def handler(request): assert 200 == resp.status -async def test_params_and_query_string(aiohttp_client: AiohttpClient) -> None: +@pytest.mark.parametrize("yarl_supports_extend_query", [True, False]) +async def test_params_and_query_string( + aiohttp_client: AiohttpClient, yarl_supports_extend_query: bool +) -> None: """Test combining params with an existing query_string.""" async def handler(request: web.Request) -> web.Response: @@ -683,13 +686,18 @@ async def handler(request: web.Request) -> web.Response: app.router.add_route("GET", "/", handler) client = await aiohttp_client(app) - async with client.get("/?q=abc", params="q=test&d=dog") as resp: - assert resp.status == 200 + # Ensure the old path is tested for old yarl versions + with mock.patch.object( + client_reqrep, "_YARL_SUPPORTS_EXTEND_QUERY", yarl_supports_extend_query + ): + async with client.get("/?q=abc", params="q=test&d=dog") as resp: + assert resp.status == 200 @pytest.mark.parametrize("params", [None, "", {}, MultiDict()]) +@pytest.mark.parametrize("yarl_supports_extend_query", [True, False]) async def test_empty_params_and_query_string( - aiohttp_client: AiohttpClient, params: Any + aiohttp_client: AiohttpClient, params: Any, yarl_supports_extend_query: bool ) -> None: """Test combining empty params with an existing query_string.""" @@ -701,8 +709,12 @@ async def handler(request: web.Request) -> web.Response: app.router.add_route("GET", "/", handler) client = await aiohttp_client(app) - async with client.get("/?q=abc", params=params) as resp: - assert resp.status == 200 + # Ensure the old path is tested for old yarl versions + with mock.patch.object( + client_reqrep, "_YARL_SUPPORTS_EXTEND_QUERY", yarl_supports_extend_query + ): + async with client.get("/?q=abc", params=params) as resp: + assert resp.status == 200 async def test_drop_params_on_redirect(aiohttp_client: AiohttpClient) -> None: From 19049fd97e4d4a5ad326d1d066c2259559868cc3 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 02:52:07 +0000 Subject: [PATCH 175/296] [PR #8564/b543677e backport][3.10] Use Query typedef from yarl for params (#9080) --- CHANGES/8564.feature.rst | 1 + aiohttp/client.py | 12 ++++++------ aiohttp/client_reqrep.py | 3 ++- aiohttp/typedefs.py | 13 +++++++++++++ 4 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 CHANGES/8564.feature.rst diff --git a/CHANGES/8564.feature.rst b/CHANGES/8564.feature.rst new file mode 100644 index 00000000000..1eac9d12217 --- /dev/null +++ b/CHANGES/8564.feature.rst @@ -0,0 +1 @@ +Improved type on ``params`` to match the underlying type allowed by ``yarl`` -- by :user:`lpetre`. diff --git a/aiohttp/client.py b/aiohttp/client.py index 8edd14d01ff..2814edc31ee 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -97,7 +97,7 @@ from .http_websocket import WSHandshakeError, WSMessage, ws_ext_gen, ws_ext_parse from .streams import FlowControlDataQueue from .tracing import Trace, TraceConfig -from .typedefs import JSONEncoder, LooseCookies, LooseHeaders, StrOrURL +from .typedefs import JSONEncoder, LooseCookies, LooseHeaders, Query, StrOrURL __all__ = ( # client_exceptions @@ -156,7 +156,7 @@ class _RequestOptions(TypedDict, total=False): - params: Union[Mapping[str, Union[str, int]], str, None] + params: Query data: Any json: Any cookies: Union[LooseCookies, None] @@ -450,7 +450,7 @@ async def _request( method: str, str_or_url: StrOrURL, *, - params: Optional[Mapping[str, str]] = None, + params: Query = None, data: Any = None, json: Any = None, cookies: Optional[LooseCookies] = None, @@ -827,7 +827,7 @@ def ws_connect( heartbeat: Optional[float] = None, auth: Optional[BasicAuth] = None, origin: Optional[str] = None, - params: Optional[Mapping[str, str]] = None, + params: Query = None, headers: Optional[LooseHeaders] = None, proxy: Optional[StrOrURL] = None, proxy_auth: Optional[BasicAuth] = None, @@ -879,7 +879,7 @@ async def _ws_connect( heartbeat: Optional[float] = None, auth: Optional[BasicAuth] = None, origin: Optional[str] = None, - params: Optional[Mapping[str, str]] = None, + params: Query = None, headers: Optional[LooseHeaders] = None, proxy: Optional[StrOrURL] = None, proxy_auth: Optional[BasicAuth] = None, @@ -1421,7 +1421,7 @@ def request( method: str, url: StrOrURL, *, - params: Optional[Mapping[str, str]] = None, + params: Query = None, data: Any = None, json: Any = None, headers: Optional[LooseHeaders] = None, diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 60d70724b3d..2df43d112cd 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -67,6 +67,7 @@ JSONDecoder, LooseCookies, LooseHeaders, + Query, RawHeaders, ) @@ -264,7 +265,7 @@ def __init__( method: str, url: URL, *, - params: Optional[Mapping[str, str]] = None, + params: Query = None, headers: Optional[LooseHeaders] = None, skip_auto_headers: Optional[Iterable[str]] = None, data: Any = None, diff --git a/aiohttp/typedefs.py b/aiohttp/typedefs.py index 9fb21c15f83..2e285fa2561 100644 --- a/aiohttp/typedefs.py +++ b/aiohttp/typedefs.py @@ -8,6 +8,7 @@ Iterable, Mapping, Protocol, + Sequence, Tuple, Union, ) @@ -15,6 +16,18 @@ from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy, istr from yarl import URL +try: + # Available in yarl>=1.10.0 + from yarl import Query as _Query +except ImportError: # pragma: no cover + SimpleQuery = Union[str, int, float] # pragma: no cover + QueryVariable = Union[SimpleQuery, "Sequence[SimpleQuery]"] # pragma: no cover + _Query = Union[ # type: ignore[misc] # pragma: no cover + None, str, "Mapping[str, QueryVariable]", "Sequence[Tuple[str, QueryVariable]]" + ] + +Query = _Query + DEFAULT_JSON_ENCODER = json.dumps DEFAULT_JSON_DECODER = json.loads From 3bd4baa53a8b171823f13873d3b2b9207164c40c Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 03:00:36 +0000 Subject: [PATCH 176/296] [PR #8564/b543677e backport][3.11] Use Query typedef from yarl for params (#9081) --- CHANGES/8564.feature.rst | 1 + aiohttp/client.py | 12 ++++++------ aiohttp/client_reqrep.py | 3 ++- aiohttp/typedefs.py | 13 +++++++++++++ 4 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 CHANGES/8564.feature.rst diff --git a/CHANGES/8564.feature.rst b/CHANGES/8564.feature.rst new file mode 100644 index 00000000000..1eac9d12217 --- /dev/null +++ b/CHANGES/8564.feature.rst @@ -0,0 +1 @@ +Improved type on ``params`` to match the underlying type allowed by ``yarl`` -- by :user:`lpetre`. diff --git a/aiohttp/client.py b/aiohttp/client.py index 3c4a0f97c04..1e5c1448ce5 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -101,7 +101,7 @@ from .http_websocket import WSHandshakeError, WSMessage, ws_ext_gen, ws_ext_parse from .streams import FlowControlDataQueue from .tracing import Trace, TraceConfig -from .typedefs import JSONEncoder, LooseCookies, LooseHeaders, StrOrURL +from .typedefs import JSONEncoder, LooseCookies, LooseHeaders, Query, StrOrURL __all__ = ( # client_exceptions @@ -161,7 +161,7 @@ class _RequestOptions(TypedDict, total=False): - params: Union[Mapping[str, Union[str, int]], str, None] + params: Query data: Any json: Any cookies: Union[LooseCookies, None] @@ -455,7 +455,7 @@ async def _request( method: str, str_or_url: StrOrURL, *, - params: Optional[Mapping[str, str]] = None, + params: Query = None, data: Any = None, json: Any = None, cookies: Optional[LooseCookies] = None, @@ -835,7 +835,7 @@ def ws_connect( heartbeat: Optional[float] = None, auth: Optional[BasicAuth] = None, origin: Optional[str] = None, - params: Optional[Mapping[str, str]] = None, + params: Query = None, headers: Optional[LooseHeaders] = None, proxy: Optional[StrOrURL] = None, proxy_auth: Optional[BasicAuth] = None, @@ -887,7 +887,7 @@ async def _ws_connect( heartbeat: Optional[float] = None, auth: Optional[BasicAuth] = None, origin: Optional[str] = None, - params: Optional[Mapping[str, str]] = None, + params: Query = None, headers: Optional[LooseHeaders] = None, proxy: Optional[StrOrURL] = None, proxy_auth: Optional[BasicAuth] = None, @@ -1452,7 +1452,7 @@ def request( method: str, url: StrOrURL, *, - params: Optional[Mapping[str, str]] = None, + params: Query = None, data: Any = None, json: Any = None, headers: Optional[LooseHeaders] = None, diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index e0232a40c4c..79073cb895b 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -67,6 +67,7 @@ JSONDecoder, LooseCookies, LooseHeaders, + Query, RawHeaders, ) @@ -262,7 +263,7 @@ def __init__( method: str, url: URL, *, - params: Optional[Mapping[str, str]] = None, + params: Query = None, headers: Optional[LooseHeaders] = None, skip_auto_headers: Optional[Iterable[str]] = None, data: Any = None, diff --git a/aiohttp/typedefs.py b/aiohttp/typedefs.py index 9fb21c15f83..2e285fa2561 100644 --- a/aiohttp/typedefs.py +++ b/aiohttp/typedefs.py @@ -8,6 +8,7 @@ Iterable, Mapping, Protocol, + Sequence, Tuple, Union, ) @@ -15,6 +16,18 @@ from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy, istr from yarl import URL +try: + # Available in yarl>=1.10.0 + from yarl import Query as _Query +except ImportError: # pragma: no cover + SimpleQuery = Union[str, int, float] # pragma: no cover + QueryVariable = Union[SimpleQuery, "Sequence[SimpleQuery]"] # pragma: no cover + _Query = Union[ # type: ignore[misc] # pragma: no cover + None, str, "Mapping[str, QueryVariable]", "Sequence[Tuple[str, QueryVariable]]" + ] + +Query = _Query + DEFAULT_JSON_ENCODER = json.dumps DEFAULT_JSON_DECODER = json.loads From 424af54dd3aecb0f41a262fdd968e397b5167172 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 Sep 2024 22:20:48 -0500 Subject: [PATCH 177/296] [PR #9079/7404afc backport][3.11] Bump yarl requirement to >=1.11.0 (#9082) --- CHANGES/9079.misc.rst | 1 + requirements/runtime-deps.in | 2 +- setup.cfg | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 CHANGES/9079.misc.rst diff --git a/CHANGES/9079.misc.rst b/CHANGES/9079.misc.rst new file mode 100644 index 00000000000..db20492c9f8 --- /dev/null +++ b/CHANGES/9079.misc.rst @@ -0,0 +1 @@ +Increase minimum yarl version to 1.11.0 -- by :user:`bdraco`. diff --git a/requirements/runtime-deps.in b/requirements/runtime-deps.in index 2299584a463..1b440bc7c68 100644 --- a/requirements/runtime-deps.in +++ b/requirements/runtime-deps.in @@ -9,4 +9,4 @@ Brotli; platform_python_implementation == 'CPython' brotlicffi; platform_python_implementation != 'CPython' frozenlist >= 1.1.1 multidict >=4.5, < 7.0 -yarl >= 1.0, < 2.0 +yarl >= 1.11.0, < 2.0 diff --git a/setup.cfg b/setup.cfg index cd1602880e6..c5258115f11 100644 --- a/setup.cfg +++ b/setup.cfg @@ -54,7 +54,7 @@ install_requires = attrs >= 17.3.0 frozenlist >= 1.1.1 multidict >=4.5, < 7.0 - yarl >= 1.0, < 2.0 + yarl >= 1.11.0, < 2.0 [options.exclude_package_data] * = From 02b89114b26bf2f2665d5135884ea1bc01f64968 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 8 Sep 2024 23:08:27 -0500 Subject: [PATCH 178/296] Remove unused backwards compatibility code for old yarl versions (#9083) (#9085) --- aiohttp/client_reqrep.py | 11 ++--------- aiohttp/typedefs.py | 13 +------------ tests/test_client_functional.py | 26 +++++++------------------- 3 files changed, 10 insertions(+), 40 deletions(-) diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 79073cb895b..75bcd3ecf5e 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -27,7 +27,7 @@ import attr from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy -from yarl import URL, __version__ as yarl_version +from yarl import URL from . import hdrs, helpers, http, multipart, payload from .abc import AbstractStreamWriter @@ -89,7 +89,6 @@ _CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") -_YARL_SUPPORTS_EXTEND_QUERY = tuple(map(int, yarl_version.split(".")[:2])) >= (1, 11) json_re = re.compile(r"^application/(?:[\w.+-]+?\+)?json") @@ -301,13 +300,7 @@ def __init__( # assert session is not None self._session = cast("ClientSession", session) if params: - if _YARL_SUPPORTS_EXTEND_QUERY: - url = url.extend_query(params) - else: - q = MultiDict(url.query) - url2 = url.with_query(params) - q.extend(url2.query) - url = url.with_query(q) + url = url.extend_query(params) self.original_url = url self.url = url.with_fragment(None) self.method = method.upper() diff --git a/aiohttp/typedefs.py b/aiohttp/typedefs.py index 2e285fa2561..cc8c0825b4e 100644 --- a/aiohttp/typedefs.py +++ b/aiohttp/typedefs.py @@ -8,23 +8,12 @@ Iterable, Mapping, Protocol, - Sequence, Tuple, Union, ) from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy, istr -from yarl import URL - -try: - # Available in yarl>=1.10.0 - from yarl import Query as _Query -except ImportError: # pragma: no cover - SimpleQuery = Union[str, int, float] # pragma: no cover - QueryVariable = Union[SimpleQuery, "Sequence[SimpleQuery]"] # pragma: no cover - _Query = Union[ # type: ignore[misc] # pragma: no cover - None, str, "Mapping[str, QueryVariable]", "Sequence[Tuple[str, QueryVariable]]" - ] +from yarl import URL, Query as _Query Query = _Query diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 4edbfa2cfeb..a350171dacf 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -20,7 +20,7 @@ from yarl import URL import aiohttp -from aiohttp import Fingerprint, ServerFingerprintMismatch, client_reqrep, hdrs, web +from aiohttp import Fingerprint, ServerFingerprintMismatch, hdrs, web from aiohttp.abc import AbstractResolver from aiohttp.client_exceptions import ( InvalidURL, @@ -672,10 +672,7 @@ async def handler(request): assert 200 == resp.status -@pytest.mark.parametrize("yarl_supports_extend_query", [True, False]) -async def test_params_and_query_string( - aiohttp_client: AiohttpClient, yarl_supports_extend_query: bool -) -> None: +async def test_params_and_query_string(aiohttp_client: AiohttpClient) -> None: """Test combining params with an existing query_string.""" async def handler(request: web.Request) -> web.Response: @@ -686,18 +683,13 @@ async def handler(request: web.Request) -> web.Response: app.router.add_route("GET", "/", handler) client = await aiohttp_client(app) - # Ensure the old path is tested for old yarl versions - with mock.patch.object( - client_reqrep, "_YARL_SUPPORTS_EXTEND_QUERY", yarl_supports_extend_query - ): - async with client.get("/?q=abc", params="q=test&d=dog") as resp: - assert resp.status == 200 + async with client.get("/?q=abc", params="q=test&d=dog") as resp: + assert resp.status == 200 @pytest.mark.parametrize("params", [None, "", {}, MultiDict()]) -@pytest.mark.parametrize("yarl_supports_extend_query", [True, False]) async def test_empty_params_and_query_string( - aiohttp_client: AiohttpClient, params: Any, yarl_supports_extend_query: bool + aiohttp_client: AiohttpClient, params: Any ) -> None: """Test combining empty params with an existing query_string.""" @@ -709,12 +701,8 @@ async def handler(request: web.Request) -> web.Response: app.router.add_route("GET", "/", handler) client = await aiohttp_client(app) - # Ensure the old path is tested for old yarl versions - with mock.patch.object( - client_reqrep, "_YARL_SUPPORTS_EXTEND_QUERY", yarl_supports_extend_query - ): - async with client.get("/?q=abc", params=params) as resp: - assert resp.status == 200 + async with client.get("/?q=abc", params=params) as resp: + assert resp.status == 200 async def test_drop_params_on_redirect(aiohttp_client: AiohttpClient) -> None: From 20f6858b10e27d7e049c993239c0487819833dbe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:23:57 +0000 Subject: [PATCH 179/296] Bump build from 1.2.1 to 1.2.2 (#9091) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [build](https://github.com/pypa/build) from 1.2.1 to 1.2.2.
Release notes

Sourced from build's releases.

Version 1.2.2

What's Changed

  • Add editable to builder.get_requries_for_build's static types (PR #764, fixes issue #763)
  • Include artifact attestations in our release (PR #782)
  • Fix typing compatibility with typed pyproject-hooks (PR #788)
  • Mark more tests with network (PR #808)
  • Add more intersphinx links to docs (PR #804)
  • Make uv optional for tests (PR #807 and #813)

New Contributors

Full Changelog: https://github.com/pypa/build/compare/1.2.1...1.2.2

Changelog

Sourced from build's changelog.

1.2.2 (2024-09-06)

  • Add editable to builder.get_requries_for_build's static types (PR :pr:764, fixes issue :issue:763)
  • Include artifact attestations in our release (PR :pr:782)
  • Fix typing compatibility with typed pyproject-hooks (PR :pr:788)
  • Mark more tests with network (PR :pr:808)
  • Add more intersphinx links to docs (PR :pr:804)
  • Make uv optional for tests (PR :pr:807 and :pr:813)
Commits
  • 3b0b5d0 docs: changelog for 1.2.2 (#812)
  • b44a886 docs: more info in README
  • 8e19948 build(deps): bump actions/attest-build-provenance in the actions group (#814)
  • b90956c tests: add module case to uv detection (#813)
  • e79f1b3 ci: remove bot comments from generated release notes (#810)
  • f6da25a pre-commit: bump repositories (#801)
  • 9a52c50 tests: optional uv (#807)
  • 553b700 docs: Add a few intersphinx links to the Python Packaging User Guide (#804)
  • 336efcb build(deps): bump actions/attest-build-provenance in the actions group (#802)
  • 73b7213 tests: mark more network tests (#808)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=build&package-manager=pip&previous-version=1.2.1&new-version=1.2.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 2b04b656eb6..d3e54ce459f 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -32,7 +32,7 @@ blockdiag==3.0.0 # via sphinxcontrib-blockdiag brotli==1.1.0 ; platform_python_implementation == "CPython" # via -r requirements/runtime-deps.in -build==1.2.1 +build==1.2.2 # via pip-tools certifi==2024.8.30 # via requests diff --git a/requirements/dev.txt b/requirements/dev.txt index a8e08879fd3..cb037d59243 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -32,7 +32,7 @@ blockdiag==3.0.0 # via sphinxcontrib-blockdiag brotli==1.1.0 ; platform_python_implementation == "CPython" # via -r requirements/runtime-deps.in -build==1.2.1 +build==1.2.2 # via pip-tools certifi==2024.8.30 # via requests From 9498f6a597c0a94f21825fb8dac6c051381dc42f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:30:22 +0000 Subject: [PATCH 180/296] Bump virtualenv from 20.26.3 to 20.26.4 (#9092) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.26.3 to 20.26.4.
Release notes

Sourced from virtualenv's releases.

20.26.4

What's Changed

New Contributors

Full Changelog: https://github.com/pypa/virtualenv/compare/20.26.3...20.26.4

Changelog

Sourced from virtualenv's changelog.

v20.26.4 (2024-09-07)

Bugfixes - 20.26.4

- no longer create `()` output in console during activation
of a virtualenv by .bat file. (:issue:`2728`)
- Upgrade embedded wheels:
  • wheel to 0.44.0 from 0.43.0
  • pip to 24.2 from 24.1
  • setuptools to 74.1.2 from 70.1.0 (:issue:2760)
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=virtualenv&package-manager=pip&previous-version=20.26.3&new-version=20.26.4)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 d3e54ce459f..97effef1d9a 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -278,7 +278,7 @@ uvloop==0.20.0 ; platform_system != "Windows" # via # -r requirements/base.in # -r requirements/lint.in -virtualenv==20.26.3 +virtualenv==20.26.4 # via pre-commit wait-for-it==2.2.2 # via -r requirements/test.in diff --git a/requirements/dev.txt b/requirements/dev.txt index cb037d59243..b55d4e7da02 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -270,7 +270,7 @@ uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpytho # via # -r requirements/base.in # -r requirements/lint.in -virtualenv==20.26.3 +virtualenv==20.26.4 # via pre-commit wait-for-it==2.2.2 # via -r requirements/test.in diff --git a/requirements/lint.txt b/requirements/lint.txt index 374f5762d44..2b85f545b72 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -119,5 +119,5 @@ urllib3==2.2.2 # via requests uvloop==0.20.0 ; platform_system != "Windows" # via -r requirements/lint.in -virtualenv==20.26.3 +virtualenv==20.26.4 # via pre-commit From 5d81fa8dc28c01d5f074761e4df04a77b8b08b1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:46:59 +0000 Subject: [PATCH 181/296] Bump platformdirs from 4.2.2 to 4.3.2 (#9094) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [platformdirs](https://github.com/platformdirs/platformdirs) from 4.2.2 to 4.3.2.
Release notes

Sourced from platformdirs's releases.

4.3.2

What's Changed

New Contributors

Full Changelog: https://github.com/tox-dev/platformdirs/compare/4.3.1...4.3.2

4.3.1

Full Changelog: https://github.com/tox-dev/platformdirs/compare/4.3.0...4.3.1

4.3.0

What's Changed

New Contributors

Full Changelog: https://github.com/tox-dev/platformdirs/compare/4.2.2...4.3.0

Commits
  • c596271 Fix multi-path returned from _path methods on MacOS (#299)
  • a420284 Use uv as installer (#300)
  • 49a89ef Update README.rst
  • 4851532 Update README.rst
  • 330b272 Ensure PlatformDirs is valid superclass type for mypy AND not an abstract cla...
  • 1ca8592 Bump pypa/gh-action-pypi-publish from 1.9.0 to 1.10.1 (#297)
  • 6ac03f5 [pre-commit.ci] pre-commit autoupdate (#293)
  • 9e539d7 Use include-hidden-files: true to upload coverage artifacts (#298)
  • 6a0ff60 [pre-commit.ci] pre-commit autoupdate (#288)
  • 8f59e91 Test with latest PyPy (#290)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=platformdirs&package-manager=pip&previous-version=4.2.2&new-version=4.3.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 97effef1d9a..261195122e2 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -140,7 +140,7 @@ pillow==9.5.0 # blockdiag pip-tools==7.4.1 # via -r requirements/dev.in -platformdirs==4.2.2 +platformdirs==4.3.2 # via virtualenv pluggy==1.5.0 # via pytest diff --git a/requirements/dev.txt b/requirements/dev.txt index b55d4e7da02..0ce3efdbe82 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -137,7 +137,7 @@ pillow==9.5.0 # blockdiag pip-tools==7.4.1 # via -r requirements/dev.in -platformdirs==4.2.2 +platformdirs==4.3.2 # via virtualenv pluggy==1.5.0 # via pytest diff --git a/requirements/lint.txt b/requirements/lint.txt index 2b85f545b72..6cf64101a64 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -56,7 +56,7 @@ nodeenv==1.9.1 # via pre-commit packaging==24.1 # via pytest -platformdirs==4.2.2 +platformdirs==4.3.2 # via virtualenv pluggy==1.5.0 # via pytest From 4b2ac73e01cdec1f07d290a0a716d3de6c0f8eca Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 20:02:07 +0000 Subject: [PATCH 182/296] [PR #9095/ffcf9dc4 backport][3.10] Reduce overhead to check if a host is an IP Address (#9096) Co-authored-by: J. Nick Koston --- CHANGES/9095.misc.rst | 1 + aiohttp/helpers.py | 66 +++++++++++++++++++++++-------------------- tests/test_helpers.py | 28 ++++++++---------- 3 files changed, 49 insertions(+), 46 deletions(-) create mode 100644 CHANGES/9095.misc.rst diff --git a/CHANGES/9095.misc.rst b/CHANGES/9095.misc.rst new file mode 100644 index 00000000000..f4a06cb09d6 --- /dev/null +++ b/CHANGES/9095.misc.rst @@ -0,0 +1 @@ +Improved performance of checking if a host is an IP Address -- by :user:`bdraco`. diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index 0327d31d961..6abbe74d8cf 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -34,7 +34,6 @@ List, Mapping, Optional, - Pattern, Protocol, Tuple, Type, @@ -471,44 +470,51 @@ def __set__(self, inst: _TSelf[_T], value: _T) -> None: except ImportError: pass -_ipv4_pattern = ( - r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}" - r"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" -) -_ipv6_pattern = ( - r"^(?:(?:(?:[A-F0-9]{1,4}:){6}|(?=(?:[A-F0-9]{0,4}:){0,6}" - r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}$)(([0-9A-F]{1,4}:){0,5}|:)" - r"((:[0-9A-F]{1,4}){1,5}:|:)|::(?:[A-F0-9]{1,4}:){5})" - r"(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}" - r"(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])|(?:[A-F0-9]{1,4}:){7}" - r"[A-F0-9]{1,4}|(?=(?:[A-F0-9]{0,4}:){0,7}[A-F0-9]{0,4}$)" - r"(([0-9A-F]{1,4}:){1,7}|:)((:[0-9A-F]{1,4}){1,7}|:)|(?:[A-F0-9]{1,4}:){7}" - r":|:(:[A-F0-9]{1,4}){7})$" -) -_ipv4_regex = re.compile(_ipv4_pattern) -_ipv6_regex = re.compile(_ipv6_pattern, flags=re.IGNORECASE) -_ipv4_regexb = re.compile(_ipv4_pattern.encode("ascii")) -_ipv6_regexb = re.compile(_ipv6_pattern.encode("ascii"), flags=re.IGNORECASE) +def is_ipv4_address(host: Optional[Union[str, bytes]]) -> bool: + """Check if host looks like an IPv4 address. + + This function does not validate that the format is correct, only that + the host is a str or bytes, and its all numeric. -def _is_ip_address( - regex: Pattern[str], regexb: Pattern[bytes], host: Optional[Union[str, bytes]] -) -> bool: - if host is None: + This check is only meant as a heuristic to ensure that + a host is not a domain name. + """ + if not host: return False + # For a host to be an ipv4 address, it must be all numeric. if isinstance(host, str): - return bool(regex.match(host)) - elif isinstance(host, (bytes, bytearray, memoryview)): - return bool(regexb.match(host)) - else: - raise TypeError(f"{host} [{type(host)}] is not a str or bytes") + return host.replace(".", "").isdigit() + if isinstance(host, (bytes, bytearray, memoryview)): + return host.decode("ascii").replace(".", "").isdigit() + raise TypeError(f"{host} [{type(host)}] is not a str or bytes") + +def is_ipv6_address(host: Optional[Union[str, bytes]]) -> bool: + """Check if host looks like an IPv6 address. -is_ipv4_address = functools.partial(_is_ip_address, _ipv4_regex, _ipv4_regexb) -is_ipv6_address = functools.partial(_is_ip_address, _ipv6_regex, _ipv6_regexb) + This function does not validate that the format is correct, only that + the host contains a colon and that it is a str or bytes. + + This check is only meant as a heuristic to ensure that + a host is not a domain name. + """ + if not host: + return False + # The host must contain a colon to be an IPv6 address. + if isinstance(host, str): + return ":" in host + if isinstance(host, (bytes, bytearray, memoryview)): + return b":" in host + raise TypeError(f"{host} [{type(host)}] is not a str or bytes") def is_ip_address(host: Optional[Union[str, bytes, bytearray, memoryview]]) -> bool: + """Check if host looks like an IP Address. + + This check is only meant as a heuristic to ensure that + a host is not a domain name. + """ return is_ipv4_address(host) or is_ipv6_address(host) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 827a417c299..2d6e098aae5 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -267,14 +267,6 @@ def test_is_ip_address() -> None: assert not helpers.is_ip_address("localhost") assert not helpers.is_ip_address("www.example.com") - # Out of range - assert not helpers.is_ip_address("999.999.999.999") - # Contain a port - assert not helpers.is_ip_address("127.0.0.1:80") - assert not helpers.is_ip_address("[2001:db8:0:1]:80") - # Too many "::" - assert not helpers.is_ip_address("1200::AB00:1234::2552:7777:1313") - def test_is_ip_address_bytes() -> None: assert helpers.is_ip_address(b"127.0.0.1") @@ -285,14 +277,6 @@ def test_is_ip_address_bytes() -> None: assert not helpers.is_ip_address(b"localhost") assert not helpers.is_ip_address(b"www.example.com") - # Out of range - assert not helpers.is_ip_address(b"999.999.999.999") - # Contain a port - assert not helpers.is_ip_address(b"127.0.0.1:80") - assert not helpers.is_ip_address(b"[2001:db8:0:1]:80") - # Too many "::" - assert not helpers.is_ip_address(b"1200::AB00:1234::2552:7777:1313") - def test_ipv4_addresses() -> None: ip_addresses = [ @@ -340,6 +324,18 @@ def test_is_ip_address_invalid_type() -> None: with pytest.raises(TypeError): helpers.is_ip_address(object()) + with pytest.raises(TypeError): + helpers.is_ipv4_address(123) # type: ignore[arg-type] + + with pytest.raises(TypeError): + helpers.is_ipv4_address(object()) # type: ignore[arg-type] + + with pytest.raises(TypeError): + helpers.is_ipv6_address(123) # type: ignore[arg-type] + + with pytest.raises(TypeError): + helpers.is_ipv6_address(object()) # type: ignore[arg-type] + # ----------------------------------- TimeoutHandle ------------------- From 7af8416233d57c40efbc0d8132435e348167cd04 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 20:10:24 +0000 Subject: [PATCH 183/296] [PR #9095/ffcf9dc4 backport][3.11] Reduce overhead to check if a host is an IP Address (#9097) Co-authored-by: J. Nick Koston --- CHANGES/9095.misc.rst | 1 + aiohttp/helpers.py | 66 +++++++++++++++++++++++-------------------- tests/test_helpers.py | 28 ++++++++---------- 3 files changed, 49 insertions(+), 46 deletions(-) create mode 100644 CHANGES/9095.misc.rst diff --git a/CHANGES/9095.misc.rst b/CHANGES/9095.misc.rst new file mode 100644 index 00000000000..f4a06cb09d6 --- /dev/null +++ b/CHANGES/9095.misc.rst @@ -0,0 +1 @@ +Improved performance of checking if a host is an IP Address -- by :user:`bdraco`. diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index bf9e135bb3c..88fc7412ea8 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -34,7 +34,6 @@ List, Mapping, Optional, - Pattern, Protocol, Tuple, Type, @@ -469,44 +468,51 @@ def __set__(self, inst: _TSelf[_T], value: _T) -> None: except ImportError: pass -_ipv4_pattern = ( - r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}" - r"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" -) -_ipv6_pattern = ( - r"^(?:(?:(?:[A-F0-9]{1,4}:){6}|(?=(?:[A-F0-9]{0,4}:){0,6}" - r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}$)(([0-9A-F]{1,4}:){0,5}|:)" - r"((:[0-9A-F]{1,4}){1,5}:|:)|::(?:[A-F0-9]{1,4}:){5})" - r"(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}" - r"(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])|(?:[A-F0-9]{1,4}:){7}" - r"[A-F0-9]{1,4}|(?=(?:[A-F0-9]{0,4}:){0,7}[A-F0-9]{0,4}$)" - r"(([0-9A-F]{1,4}:){1,7}|:)((:[0-9A-F]{1,4}){1,7}|:)|(?:[A-F0-9]{1,4}:){7}" - r":|:(:[A-F0-9]{1,4}){7})$" -) -_ipv4_regex = re.compile(_ipv4_pattern) -_ipv6_regex = re.compile(_ipv6_pattern, flags=re.IGNORECASE) -_ipv4_regexb = re.compile(_ipv4_pattern.encode("ascii")) -_ipv6_regexb = re.compile(_ipv6_pattern.encode("ascii"), flags=re.IGNORECASE) +def is_ipv4_address(host: Optional[Union[str, bytes]]) -> bool: + """Check if host looks like an IPv4 address. + + This function does not validate that the format is correct, only that + the host is a str or bytes, and its all numeric. -def _is_ip_address( - regex: Pattern[str], regexb: Pattern[bytes], host: Optional[Union[str, bytes]] -) -> bool: - if host is None: + This check is only meant as a heuristic to ensure that + a host is not a domain name. + """ + if not host: return False + # For a host to be an ipv4 address, it must be all numeric. if isinstance(host, str): - return bool(regex.match(host)) - elif isinstance(host, (bytes, bytearray, memoryview)): - return bool(regexb.match(host)) - else: - raise TypeError(f"{host} [{type(host)}] is not a str or bytes") + return host.replace(".", "").isdigit() + if isinstance(host, (bytes, bytearray, memoryview)): + return host.decode("ascii").replace(".", "").isdigit() + raise TypeError(f"{host} [{type(host)}] is not a str or bytes") + +def is_ipv6_address(host: Optional[Union[str, bytes]]) -> bool: + """Check if host looks like an IPv6 address. -is_ipv4_address = functools.partial(_is_ip_address, _ipv4_regex, _ipv4_regexb) -is_ipv6_address = functools.partial(_is_ip_address, _ipv6_regex, _ipv6_regexb) + This function does not validate that the format is correct, only that + the host contains a colon and that it is a str or bytes. + + This check is only meant as a heuristic to ensure that + a host is not a domain name. + """ + if not host: + return False + # The host must contain a colon to be an IPv6 address. + if isinstance(host, str): + return ":" in host + if isinstance(host, (bytes, bytearray, memoryview)): + return b":" in host + raise TypeError(f"{host} [{type(host)}] is not a str or bytes") def is_ip_address(host: Optional[Union[str, bytes, bytearray, memoryview]]) -> bool: + """Check if host looks like an IP Address. + + This check is only meant as a heuristic to ensure that + a host is not a domain name. + """ return is_ipv4_address(host) or is_ipv6_address(host) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 656364f43aa..13d73a312fc 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -267,14 +267,6 @@ def test_is_ip_address() -> None: assert not helpers.is_ip_address("localhost") assert not helpers.is_ip_address("www.example.com") - # Out of range - assert not helpers.is_ip_address("999.999.999.999") - # Contain a port - assert not helpers.is_ip_address("127.0.0.1:80") - assert not helpers.is_ip_address("[2001:db8:0:1]:80") - # Too many "::" - assert not helpers.is_ip_address("1200::AB00:1234::2552:7777:1313") - def test_is_ip_address_bytes() -> None: assert helpers.is_ip_address(b"127.0.0.1") @@ -285,14 +277,6 @@ def test_is_ip_address_bytes() -> None: assert not helpers.is_ip_address(b"localhost") assert not helpers.is_ip_address(b"www.example.com") - # Out of range - assert not helpers.is_ip_address(b"999.999.999.999") - # Contain a port - assert not helpers.is_ip_address(b"127.0.0.1:80") - assert not helpers.is_ip_address(b"[2001:db8:0:1]:80") - # Too many "::" - assert not helpers.is_ip_address(b"1200::AB00:1234::2552:7777:1313") - def test_ipv4_addresses() -> None: ip_addresses = [ @@ -341,6 +325,18 @@ def test_is_ip_address_invalid_type() -> None: with pytest.raises(TypeError): helpers.is_ip_address(object()) + with pytest.raises(TypeError): + helpers.is_ipv4_address(123) # type: ignore[arg-type] + + with pytest.raises(TypeError): + helpers.is_ipv4_address(object()) # type: ignore[arg-type] + + with pytest.raises(TypeError): + helpers.is_ipv6_address(123) # type: ignore[arg-type] + + with pytest.raises(TypeError): + helpers.is_ipv6_address(object()) # type: ignore[arg-type] + # ----------------------------------- TimeoutHandle ------------------- From c835d747b5f762d3af00335f9fa09ba716f97122 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:23:25 +0000 Subject: [PATCH 184/296] Bump filelock from 3.15.4 to 3.16.0 (#9093) 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.15.4 to 3.16.0.
Release notes

Sourced from filelock's releases.

3.16.0

What's Changed

Full Changelog: https://github.com/tox-dev/filelock/compare/3.15.4...3.16.0

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=filelock&package-manager=pip&previous-version=3.15.4&new-version=3.16.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 261195122e2..d40abf6df52 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -70,7 +70,7 @@ docutils==0.20.1 # via sphinx exceptiongroup==1.2.2 # via pytest -filelock==3.15.4 +filelock==3.16.0 # via virtualenv freezegun==1.5.1 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 0ce3efdbe82..700e3dc1208 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -68,7 +68,7 @@ docutils==0.20.1 # via sphinx exceptiongroup==1.2.2 # via pytest -filelock==3.15.4 +filelock==3.16.0 # via virtualenv freezegun==1.5.1 # via diff --git a/requirements/lint.txt b/requirements/lint.txt index 6cf64101a64..d0a5781d1b8 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -32,7 +32,7 @@ distlib==0.3.8 # via virtualenv exceptiongroup==1.2.2 # via pytest -filelock==3.15.4 +filelock==3.16.0 # via virtualenv freezegun==1.5.1 # via -r requirements/lint.in From 3d5db00174ac54733b67e93c163c14d11033eded Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 12:39:18 +0000 Subject: [PATCH 185/296] Bump pytest from 8.3.2 to 8.3.3 (#9104) Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.3.2 to 8.3.3.
Release notes

Sourced from pytest's releases.

8.3.3

pytest 8.3.3 (2024-09-09)

Bug fixes

  • #12446: Avoid calling @property (and other instance descriptors) during fixture discovery -- by asottile{.interpreted-text role="user"}

  • #12659: Fixed the issue of not displaying assertion failure differences when using the parameter --import-mode=importlib in pytest>=8.1.

  • #12667: Fixed a regression where type change in [ExceptionInfo.errisinstance]{.title-ref} caused [mypy]{.title-ref} to fail.

  • #12744: Fixed typing compatibility with Python 3.9 or less -- replaced [typing.Self]{.title-ref} with [typing_extensions.Self]{.title-ref} -- by Avasam{.interpreted-text role="user"}

  • #12745: Fixed an issue with backslashes being incorrectly converted in nodeid paths on Windows, ensuring consistent path handling across environments.

  • #6682: Fixed bug where the verbosity levels where not being respected when printing the "msg" part of failed assertion (as in assert condition, msg).

  • #9422: Fix bug where disabling the terminal plugin via -p no:terminal would cause crashes related to missing the verbose option.

    -- by GTowers1{.interpreted-text role="user"}

Improved documentation

  • #12663: Clarify that the [pytest_deselected]{.title-ref} hook should be called from [pytest_collection_modifyitems]{.title-ref} hook implementations when items are deselected.
  • #12678: Remove erroneous quotes from [tmp_path_retention_policy]{.title-ref} example in docs.

Miscellaneous internal changes

  • #12769: Fix typos discovered by codespell and add codespell to pre-commit hooks.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pytest&package-manager=pip&previous-version=8.3.2&new-version=8.3.3)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index d40abf6df52..ecb06752a2e 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -170,7 +170,7 @@ pyproject-hooks==1.1.0 # via # build # pip-tools -pytest==8.3.2 +pytest==8.3.3 # via # -r requirements/lint.in # -r requirements/test.in diff --git a/requirements/dev.txt b/requirements/dev.txt index 700e3dc1208..e67753f3597 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -165,7 +165,7 @@ pyproject-hooks==1.1.0 # via # build # pip-tools -pytest==8.3.2 +pytest==8.3.3 # via # -r requirements/lint.in # -r requirements/test.in diff --git a/requirements/lint.txt b/requirements/lint.txt index d0a5781d1b8..c75880dc0d8 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -72,7 +72,7 @@ pydantic-core==2.23.2 # via pydantic pygments==2.18.0 # via rich -pytest==8.3.2 +pytest==8.3.3 # via # -r requirements/lint.in # pytest-mock diff --git a/requirements/test.txt b/requirements/test.txt index a065d607643..8db0bb18584 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -83,7 +83,7 @@ pydantic-core==2.23.2 # via pydantic pygments==2.18.0 # via rich -pytest==8.3.2 +pytest==8.3.3 # via # -r requirements/test.in # pytest-cov From 277813ca5a7d370ecc1240e46e606d7bea400aa3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 12:46:19 +0000 Subject: [PATCH 186/296] Bump yarl from 1.11.0 to 1.11.1 (#9105) Bumps [yarl](https://github.com/aio-libs/yarl) from 1.11.0 to 1.11.1.
Release notes

Sourced from yarl's releases.

1.11.1

Bug fixes

  • Allowed scheme replacement for relative URLs if the scheme does not require a host -- by :user:bdraco.

    Related issues and pull requests on GitHub: #280, #1138.

  • Allowed empty host for URL schemes other than the special schemes listed in the WHATWG URL spec -- by :user:bdraco.

    Related issues and pull requests on GitHub: #1136.

Features

  • Loosened restriction on integers as query string values to allow classes that implement __int__ -- by :user:bdraco.

    Related issues and pull requests on GitHub: #1139.

Miscellaneous internal changes

  • Improved performance of normalizing paths -- by :user:bdraco.

    Related issues and pull requests on GitHub: #1137.


Changelog

Sourced from yarl's changelog.

1.11.1

(2024-09-09)

Bug fixes

  • Allowed scheme replacement for relative URLs if the scheme does not require a host -- by :user:bdraco.

    Related issues and pull requests on GitHub: :issue:280, :issue:1138.

  • Allowed empty host for URL schemes other than the special schemes listed in the WHATWG URL spec -- by :user:bdraco.

    Related issues and pull requests on GitHub: :issue:1136.

Features

  • Loosened restriction on integers as query string values to allow classes that implement __int__ -- by :user:bdraco.

    Related issues and pull requests on GitHub: :issue:1139.

Miscellaneous internal changes

  • Improved performance of normalizing paths -- by :user:bdraco.

    Related issues and pull requests on GitHub: :issue:1137.


Commits
  • 134d4cd Release 1.11.1
  • fa321e5 Cleanup tense of changelog messages (#1140)
  • 8048180 Allow scheme replacement for relative URLs if the target scheme does not requ...
  • 2340c72 Accept objects that support int for query vars (#1139)
  • 0ee0104 Allow empty hosts for schemes that do not require them (#1136)
  • 1e969ab Small speed up to normalizing the path (#1137)
  • 07c4e03 Increment version to 1.11.1.dev0 (#1135)
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=yarl&package-manager=pip&previous-version=1.11.0&new-version=1.11.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index 87300dd8515..b8f70307c91 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -38,5 +38,5 @@ pycparser==2.22 # via cffi uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in -yarl==1.11.0 +yarl==1.11.1 # via -r requirements/runtime-deps.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index ecb06752a2e..14e784e5eb6 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -286,7 +286,7 @@ webcolors==24.8.0 # via blockdiag wheel==0.44.0 # via pip-tools -yarl==1.11.0 +yarl==1.11.1 # via -r requirements/runtime-deps.in zipp==3.20.1 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index e67753f3597..bf8d7d71098 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -278,7 +278,7 @@ webcolors==24.8.0 # via blockdiag wheel==0.44.0 # via pip-tools -yarl==1.11.0 +yarl==1.11.1 # via -r requirements/runtime-deps.in zipp==3.20.1 # via diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index 89e30717677..988d7b275c9 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -32,5 +32,5 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -yarl==1.11.0 +yarl==1.11.1 # via -r requirements/runtime-deps.in diff --git a/requirements/test.txt b/requirements/test.txt index 8db0bb18584..96f3f7c3ee5 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -136,5 +136,5 @@ uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpytho # via -r requirements/base.in wait-for-it==2.2.2 # via -r requirements/test.in -yarl==1.11.0 +yarl==1.11.1 # via -r requirements/runtime-deps.in From 47c08987cd54f5bc5b67eccff3b45c64ad7fe8b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 13:01:44 +0000 Subject: [PATCH 187/296] Bump pydantic from 2.9.0 to 2.9.1 (#9107) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.9.0 to 2.9.1.
Release notes

Sourced from pydantic's releases.

v2.9.1 (2024-09-09)

What's Changed

Fixes

Full Changelog: https://github.com/pydantic/pydantic/compare/v2.9.0...v2.9.1

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pydantic&package-manager=pip&previous-version=2.9.0&new-version=2.9.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 | 4 ++-- requirements/dev.txt | 4 ++-- requirements/lint.txt | 4 ++-- requirements/test.txt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 14e784e5eb6..24511763e32 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -152,9 +152,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.9.0 +pydantic==2.9.1 # via python-on-whales -pydantic-core==2.23.2 +pydantic-core==2.23.3 # via pydantic pyenchant==3.2.2 # via sphinxcontrib-spelling diff --git a/requirements/dev.txt b/requirements/dev.txt index bf8d7d71098..7ff9de0c37a 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -149,9 +149,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.9.0 +pydantic==2.9.1 # via python-on-whales -pydantic-core==2.23.2 +pydantic-core==2.23.3 # via pydantic pygments==2.18.0 # via diff --git a/requirements/lint.txt b/requirements/lint.txt index c75880dc0d8..159b481e30e 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -66,9 +66,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.9.0 +pydantic==2.9.1 # via python-on-whales -pydantic-core==2.23.2 +pydantic-core==2.23.3 # via pydantic pygments==2.18.0 # via rich diff --git a/requirements/test.txt b/requirements/test.txt index 96f3f7c3ee5..03e9f77a961 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -77,9 +77,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.9.0 +pydantic==2.9.1 # via python-on-whales -pydantic-core==2.23.2 +pydantic-core==2.23.3 # via pydantic pygments==2.18.0 # via rich From bea443a85d407df08fe6ff9798def5f57f7f3968 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 13:08:46 +0000 Subject: [PATCH 188/296] Bump multidict from 6.0.5 to 6.1.0 (#9106) Bumps [multidict](https://github.com/aio-libs/multidict) from 6.0.5 to 6.1.0.
Release notes

Sourced from multidict's releases.

6.1.0

Bug fixes

  • Covered the unreachable code path in multidict._multidict_base._abc_itemsview_register() with typing -- by :user:skinnyBat.

    Related issues and pull requests on GitHub: #928.

Features

  • Added support for Python 3.13 -- by :user:bdraco.

    Related issues and pull requests on GitHub: #1002.

Removals and backward incompatible breaking changes

  • Removed Python 3.7 support -- by :user:bdraco.

    Related issues and pull requests on GitHub: #997.

Contributor-facing changes

  • Added tests to have full code coverage of the multidict._multidict_base._viewbaseset_richcmp() function -- by :user:skinnyBat.

    Related issues and pull requests on GitHub: #928.

... (truncated)

Changelog

Sourced from multidict's changelog.

6.1.0 (2024-09-09)

Bug fixes

  • Covered the unreachable code path in multidict._multidict_base._abc_itemsview_register() with typing -- by :user:skinnyBat.

    Related issues and pull requests on GitHub: :issue:928.

Features

  • Added support for Python 3.13 -- by :user:bdraco.

    Related issues and pull requests on GitHub: :issue:1002.

Removals and backward incompatible breaking changes

  • Removed Python 3.7 support -- by :user:bdraco.

    Related issues and pull requests on GitHub: :issue:997.

Contributor-facing changes

  • Added tests to have full code coverage of the multidict._multidict_base._viewbaseset_richcmp() function -- by :user:skinnyBat.

    Related issues and pull requests on GitHub:

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=multidict&package-manager=pip&previous-version=6.0.5&new-version=6.1.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 4 +++- requirements/constraints.txt | 3 ++- requirements/cython.txt | 4 +++- requirements/dev.txt | 3 ++- requirements/multidict.txt | 4 +++- requirements/runtime-deps.txt | 4 +++- requirements/test.txt | 3 ++- 7 files changed, 18 insertions(+), 7 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index b8f70307c91..d947f437f98 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -26,7 +26,7 @@ gunicorn==23.0.0 # via -r requirements/base.in idna==3.4 # via yarl -multidict==6.0.5 +multidict==6.1.0 # via # -r requirements/runtime-deps.in # yarl @@ -36,6 +36,8 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi +typing-extensions==4.12.2 + # via multidict uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in yarl==1.11.1 diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 24511763e32..e54f64ccf31 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -115,7 +115,7 @@ markupsafe==2.1.5 # via jinja2 mdurl==0.1.2 # via markdown-it-py -multidict==6.0.5 +multidict==6.1.0 # via # -r requirements/multidict.in # -r requirements/runtime-deps.in @@ -264,6 +264,7 @@ typing-extensions==4.12.2 # via # aioredis # annotated-types + # multidict # mypy # pydantic # pydantic-core diff --git a/requirements/cython.txt b/requirements/cython.txt index ae232fdd2ee..f67cc903a0b 100644 --- a/requirements/cython.txt +++ b/requirements/cython.txt @@ -6,5 +6,7 @@ # cython==3.0.11 # via -r requirements/cython.in -multidict==6.0.5 +multidict==6.1.0 # via -r requirements/multidict.in +typing-extensions==4.12.2 + # via multidict diff --git a/requirements/dev.txt b/requirements/dev.txt index 7ff9de0c37a..90fffae5826 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -113,7 +113,7 @@ markupsafe==2.1.5 # via jinja2 mdurl==0.1.2 # via markdown-it-py -multidict==6.0.5 +multidict==6.1.0 # via # -r requirements/runtime-deps.in # yarl @@ -256,6 +256,7 @@ typing-extensions==4.12.2 # via # aioredis # annotated-types + # multidict # mypy # pydantic # pydantic-core diff --git a/requirements/multidict.txt b/requirements/multidict.txt index 915f9c24dcc..b8b44428920 100644 --- a/requirements/multidict.txt +++ b/requirements/multidict.txt @@ -4,5 +4,7 @@ # # pip-compile --allow-unsafe --output-file=requirements/multidict.txt --resolver=backtracking --strip-extras requirements/multidict.in # -multidict==6.0.5 +multidict==6.1.0 # via -r requirements/multidict.in +typing-extensions==4.12.2 + # via multidict diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index 988d7b275c9..eea3d44a539 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -24,7 +24,7 @@ frozenlist==1.4.1 # aiosignal idna==3.4 # via yarl -multidict==6.0.5 +multidict==6.1.0 # via # -r requirements/runtime-deps.in # yarl @@ -32,5 +32,7 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi +typing-extensions==4.12.2 + # via multidict yarl==1.11.1 # via -r requirements/runtime-deps.in diff --git a/requirements/test.txt b/requirements/test.txt index 03e9f77a961..56d9a6c9ceb 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -57,7 +57,7 @@ markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py -multidict==6.0.5 +multidict==6.1.0 # via # -r requirements/runtime-deps.in # yarl @@ -124,6 +124,7 @@ typer==0.12.5 typing-extensions==4.12.2 # via # annotated-types + # multidict # mypy # pydantic # pydantic-core From b90dd1e41f77e3296edf8932cc0ec59ab1343463 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 10 Sep 2024 16:08:14 +0100 Subject: [PATCH 189/296] Avoid compressing empty body (#9108) (#9110) (cherry picked from commit 1d112418a05dcdcabd38590351e78bec8f4a45bc) --- CHANGES/9108.bugfix.rst | 1 + aiohttp/client.py | 4 +-- aiohttp/client_reqrep.py | 8 +++--- tests/test_client_functional.py | 45 ++++++++++++++++++++++++++++++--- 4 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 CHANGES/9108.bugfix.rst diff --git a/CHANGES/9108.bugfix.rst b/CHANGES/9108.bugfix.rst new file mode 100644 index 00000000000..8be000575e8 --- /dev/null +++ b/CHANGES/9108.bugfix.rst @@ -0,0 +1 @@ +Fixed compressed requests failing when no body was provided -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/client.py b/aiohttp/client.py index 1e5c1448ce5..edf4090832f 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -170,7 +170,7 @@ class _RequestOptions(TypedDict, total=False): auth: Union[BasicAuth, None] allow_redirects: bool max_redirects: int - compress: Union[str, None] + compress: Union[str, bool, None] chunked: Union[bool, None] expect100: bool raise_for_status: Union[None, bool, Callable[[ClientResponse], Awaitable[None]]] @@ -464,7 +464,7 @@ async def _request( auth: Optional[BasicAuth] = None, allow_redirects: bool = True, max_redirects: int = 10, - compress: Optional[str] = None, + compress: Union[str, bool, None] = None, chunked: Optional[bool] = None, expect100: bool = False, raise_for_status: Union[ diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 75bcd3ecf5e..7d4467dbdbb 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -269,7 +269,7 @@ def __init__( cookies: Optional[LooseCookies] = None, auth: Optional[BasicAuth] = None, version: http.HttpVersion = http.HttpVersion11, - compress: Optional[str] = None, + compress: Union[str, bool, None] = None, chunked: Optional[bool] = None, expect100: bool = False, loop: Optional[asyncio.AbstractEventLoop] = None, @@ -494,7 +494,9 @@ def update_cookies(self, cookies: Optional[LooseCookies]) -> None: def update_content_encoding(self, data: Any) -> None: """Set request content encoding.""" - if data is None: + if not data: + # Don't compress an empty body. + self.compress = None return enc = self.headers.get(hdrs.CONTENT_ENCODING, "").lower() @@ -705,7 +707,7 @@ async def send(self, conn: "Connection") -> "ClientResponse": ) if self.compress: - writer.enable_compression(self.compress) + writer.enable_compression(self.compress) # type: ignore[arg-type] if self.chunked is not None: writer.enable_chunking() diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index a350171dacf..70c5bf16096 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -12,7 +12,7 @@ import tarfile import time import zipfile -from typing import Any, AsyncIterator, Type +from typing import Any, AsyncIterator, Optional, Type from unittest import mock import pytest @@ -31,6 +31,9 @@ SocketTimeoutError, TooManyRedirects, ) +from aiohttp.client_reqrep import ClientRequest +from aiohttp.connector import Connection +from aiohttp.http_writer import StreamWriter from aiohttp.pytest_plugin import AiohttpClient, AiohttpServer, TestClient from aiohttp.test_utils import unused_port @@ -1498,8 +1501,44 @@ async def handler(request): assert 200 == resp.status -async def test_POST_DATA_DEFLATE(aiohttp_client) -> None: - async def handler(request): +@pytest.mark.parametrize("data", (None, b"")) +async def test_GET_DEFLATE( + aiohttp_client: AiohttpClient, data: Optional[bytes] +) -> None: + async def handler(request: web.Request) -> web.Response: + return web.json_response({"ok": True}) + + write_mock = None + original_write_bytes = ClientRequest.write_bytes + + async def write_bytes( + self: ClientRequest, writer: StreamWriter, conn: Connection + ) -> None: + nonlocal write_mock + original_write = writer._write + + with mock.patch.object( + writer, "_write", autospec=True, spec_set=True, side_effect=original_write + ) as write_mock: + await original_write_bytes(self, writer, conn) + + with mock.patch.object(ClientRequest, "write_bytes", write_bytes): + app = web.Application() + app.router.add_get("/", handler) + client = await aiohttp_client(app) + + async with client.get("/", data=data, compress=True) as resp: + assert resp.status == 200 + content = await resp.json() + assert content == {"ok": True} + + assert write_mock is not None + # No chunks should have been sent for an empty body. + write_mock.assert_not_called() + + +async def test_POST_DATA_DEFLATE(aiohttp_client: AiohttpClient) -> None: + async def handler(request: web.Request) -> web.Response: data = await request.post() return web.json_response(dict(data)) From 89951ecf658a49cfe477d1a389336f90a043ff2d Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 19:04:02 +0100 Subject: [PATCH 190/296] [PR #9110/b90dd1e4 backport][3.10] Avoid compressing empty body (#9108) (#9111) **This is a backport of PR #9110 as merged into 3.11 (b90dd1e41f77e3296edf8932cc0ec59ab1343463).** (cherry picked from commit 1d112418a05dcdcabd38590351e78bec8f4a45bc) Co-authored-by: Sam Bull --- CHANGES/9108.bugfix.rst | 1 + aiohttp/client.py | 4 +-- aiohttp/client_reqrep.py | 8 +++--- tests/test_client_functional.py | 45 ++++++++++++++++++++++++++++++--- 4 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 CHANGES/9108.bugfix.rst diff --git a/CHANGES/9108.bugfix.rst b/CHANGES/9108.bugfix.rst new file mode 100644 index 00000000000..8be000575e8 --- /dev/null +++ b/CHANGES/9108.bugfix.rst @@ -0,0 +1 @@ +Fixed compressed requests failing when no body was provided -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/client.py b/aiohttp/client.py index 2814edc31ee..5f9e95f4706 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -165,7 +165,7 @@ class _RequestOptions(TypedDict, total=False): auth: Union[BasicAuth, None] allow_redirects: bool max_redirects: int - compress: Union[str, None] + compress: Union[str, bool, None] chunked: Union[bool, None] expect100: bool raise_for_status: Union[None, bool, Callable[[ClientResponse], Awaitable[None]]] @@ -459,7 +459,7 @@ async def _request( auth: Optional[BasicAuth] = None, allow_redirects: bool = True, max_redirects: int = 10, - compress: Optional[str] = None, + compress: Union[str, bool, None] = None, chunked: Optional[bool] = None, expect100: bool = False, raise_for_status: Union[ diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 2df43d112cd..93e7b59a8a1 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -272,7 +272,7 @@ def __init__( cookies: Optional[LooseCookies] = None, auth: Optional[BasicAuth] = None, version: http.HttpVersion = http.HttpVersion11, - compress: Optional[str] = None, + compress: Union[str, bool, None] = None, chunked: Optional[bool] = None, expect100: bool = False, loop: Optional[asyncio.AbstractEventLoop] = None, @@ -503,7 +503,9 @@ def update_cookies(self, cookies: Optional[LooseCookies]) -> None: def update_content_encoding(self, data: Any) -> None: """Set request content encoding.""" - if data is None: + if not data: + # Don't compress an empty body. + self.compress = None return enc = self.headers.get(hdrs.CONTENT_ENCODING, "").lower() @@ -714,7 +716,7 @@ async def send(self, conn: "Connection") -> "ClientResponse": ) if self.compress: - writer.enable_compression(self.compress) + writer.enable_compression(self.compress) # type: ignore[arg-type] if self.chunked is not None: writer.enable_chunking() diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 9325cc17e48..082db6f3e9a 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -12,7 +12,7 @@ import tarfile import time import zipfile -from typing import Any, AsyncIterator, Type +from typing import Any, AsyncIterator, Optional, Type from unittest import mock import pytest @@ -31,6 +31,9 @@ SocketTimeoutError, TooManyRedirects, ) +from aiohttp.client_reqrep import ClientRequest +from aiohttp.connector import Connection +from aiohttp.http_writer import StreamWriter from aiohttp.pytest_plugin import AiohttpClient, AiohttpServer, TestClient from aiohttp.test_utils import unused_port @@ -1510,8 +1513,44 @@ async def handler(request): assert 200 == resp.status -async def test_POST_DATA_DEFLATE(aiohttp_client) -> None: - async def handler(request): +@pytest.mark.parametrize("data", (None, b"")) +async def test_GET_DEFLATE( + aiohttp_client: AiohttpClient, data: Optional[bytes] +) -> None: + async def handler(request: web.Request) -> web.Response: + return web.json_response({"ok": True}) + + write_mock = None + original_write_bytes = ClientRequest.write_bytes + + async def write_bytes( + self: ClientRequest, writer: StreamWriter, conn: Connection + ) -> None: + nonlocal write_mock + original_write = writer._write + + with mock.patch.object( + writer, "_write", autospec=True, spec_set=True, side_effect=original_write + ) as write_mock: + await original_write_bytes(self, writer, conn) + + with mock.patch.object(ClientRequest, "write_bytes", write_bytes): + app = web.Application() + app.router.add_get("/", handler) + client = await aiohttp_client(app) + + async with client.get("/", data=data, compress=True) as resp: + assert resp.status == 200 + content = await resp.json() + assert content == {"ok": True} + + assert write_mock is not None + # No chunks should have been sent for an empty body. + write_mock.assert_not_called() + + +async def test_POST_DATA_DEFLATE(aiohttp_client: AiohttpClient) -> None: + async def handler(request: web.Request) -> web.Response: data = await request.post() return web.json_response(dict(data)) From 398eef29d571b01faffc1c85c6a9918daff83574 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 10 Sep 2024 23:41:27 +0100 Subject: [PATCH 191/296] =?UTF-8?q?Add=20`strategy`=20argument=20to=20`Str?= =?UTF-8?q?eamResponse.enable=5Fcompression()`=20meth=E2=80=A6=20(#9114)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …od (#6257) (cherry picked from commit b8eca2754de8b30148c7bc2233f019d8045bce36) Co-authored-by: Konstantin Shootkin <1991konstantin@gmail.com> --- CHANGES/6257.feature | 4 ++++ CONTRIBUTORS.txt | 1 + aiohttp/abc.py | 5 ++++- aiohttp/web_response.py | 11 +++++++++-- docs/web_reference.rst | 5 ++++- tests/test_web_response.py | 11 ++++++----- 6 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 CHANGES/6257.feature diff --git a/CHANGES/6257.feature b/CHANGES/6257.feature new file mode 100644 index 00000000000..51fc6bf9bb7 --- /dev/null +++ b/CHANGES/6257.feature @@ -0,0 +1,4 @@ +Added ``strategy`` parameter to :meth:`aiohttp.web.StreamResponse.enable_compression` +The value of this parameter is passed to the :func:`zlib.compressobj` function, allowing people +to use a more sufficient compression algorithm for their data served by :mod:`aiohttp.web` +-- by :user:`shootkin` diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index e7214dfedd4..cf22583989f 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -201,6 +201,7 @@ Kevin Samuel Kimmo Parviainen-Jalanko Kirill Klenov Kirill Malovitsa +Konstantin Shutkin Konstantin Valetov Krzysztof Blazewicz Kyrylo Perevozchikov diff --git a/aiohttp/abc.py b/aiohttp/abc.py index 3fb024048a4..59a7976ec06 100644 --- a/aiohttp/abc.py +++ b/aiohttp/abc.py @@ -1,6 +1,7 @@ import asyncio import logging import socket +import zlib from abc import ABC, abstractmethod from collections.abc import Sized from http.cookies import BaseCookie, Morsel @@ -208,7 +209,9 @@ async def drain(self) -> None: """Flush the write buffer.""" @abstractmethod - def enable_compression(self, encoding: str = "deflate") -> None: + def enable_compression( + self, encoding: str = "deflate", strategy: int = zlib.Z_DEFAULT_STRATEGY + ) -> None: """Enable HTTP body compression""" @abstractmethod diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 24ea9f5b46b..d4f18271a83 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -6,6 +6,7 @@ import math import time import warnings +import zlib from concurrent.futures import Executor from http import HTTPStatus from http.cookies import SimpleCookie @@ -85,6 +86,7 @@ def __init__( self._keep_alive: Optional[bool] = None self._chunked = False self._compression = False + self._compression_strategy: int = zlib.Z_DEFAULT_STRATEGY self._compression_force: Optional[ContentCoding] = None self._cookies = SimpleCookie() @@ -174,7 +176,9 @@ def enable_chunked_encoding(self, chunk_size: Optional[int] = None) -> None: warnings.warn("Chunk size is deprecated #1615", DeprecationWarning) def enable_compression( - self, force: Optional[Union[bool, ContentCoding]] = None + self, + force: Optional[Union[bool, ContentCoding]] = None, + strategy: int = zlib.Z_DEFAULT_STRATEGY, ) -> None: """Enables response compression encoding.""" # Backwards compatibility for when force was a bool <0.17. @@ -190,6 +194,7 @@ def enable_compression( self._compression = True self._compression_force = force + self._compression_strategy = strategy @property def headers(self) -> "CIMultiDict[str]": @@ -404,7 +409,9 @@ async def _do_start_compression(self, coding: ContentCoding) -> None: if coding != ContentCoding.identity: assert self._payload_writer is not None self._headers[hdrs.CONTENT_ENCODING] = coding.value - self._payload_writer.enable_compression(coding.value) + self._payload_writer.enable_compression( + coding.value, self._compression_strategy + ) # Compressed payload may have different content length, # remove the header self._headers.popall(hdrs.CONTENT_LENGTH, None) diff --git a/docs/web_reference.rst b/docs/web_reference.rst index bb22cfd6369..39b503de248 100644 --- a/docs/web_reference.rst +++ b/docs/web_reference.rst @@ -669,7 +669,7 @@ and :ref:`aiohttp-web-signals` handlers:: .. seealso:: :meth:`enable_compression` - .. method:: enable_compression(force=None) + .. method:: enable_compression(force=None, strategy=zlib.Z_DEFAULT_STRATEGY) Enable compression. @@ -679,6 +679,9 @@ and :ref:`aiohttp-web-signals` handlers:: *Accept-Encoding* is not checked if *force* is set to a :class:`ContentCoding`. + *strategy* accepts a :mod:`zlib` compression strategy. + See :func:`zlib.compressobj` for possible values. + .. seealso:: :attr:`compression` .. attribute:: chunked diff --git a/tests/test_web_response.py b/tests/test_web_response.py index 2e1e332e0a5..b71730868e4 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -3,6 +3,7 @@ import gzip import io import json +import zlib from concurrent.futures import ThreadPoolExecutor from typing import AsyncIterator, Optional from unittest import mock @@ -461,7 +462,7 @@ async def test_compression_default_coding() -> None: msg = await resp.prepare(req) - msg.enable_compression.assert_called_with("deflate") + msg.enable_compression.assert_called_with("deflate", zlib.Z_DEFAULT_STRATEGY) assert "deflate" == resp.headers.get(hdrs.CONTENT_ENCODING) assert msg.filter is not None @@ -476,7 +477,7 @@ async def test_force_compression_deflate() -> None: assert resp.compression msg = await resp.prepare(req) - msg.enable_compression.assert_called_with("deflate") + msg.enable_compression.assert_called_with("deflate", zlib.Z_DEFAULT_STRATEGY) assert "deflate" == resp.headers.get(hdrs.CONTENT_ENCODING) @@ -488,7 +489,7 @@ async def test_force_compression_no_accept_deflate() -> None: assert resp.compression msg = await resp.prepare(req) - msg.enable_compression.assert_called_with("deflate") + msg.enable_compression.assert_called_with("deflate", zlib.Z_DEFAULT_STRATEGY) assert "deflate" == resp.headers.get(hdrs.CONTENT_ENCODING) @@ -502,7 +503,7 @@ async def test_force_compression_gzip() -> None: assert resp.compression msg = await resp.prepare(req) - msg.enable_compression.assert_called_with("gzip") + msg.enable_compression.assert_called_with("gzip", zlib.Z_DEFAULT_STRATEGY) assert "gzip" == resp.headers.get(hdrs.CONTENT_ENCODING) @@ -514,7 +515,7 @@ async def test_force_compression_no_accept_gzip() -> None: assert resp.compression msg = await resp.prepare(req) - msg.enable_compression.assert_called_with("gzip") + msg.enable_compression.assert_called_with("gzip", zlib.Z_DEFAULT_STRATEGY) assert "gzip" == resp.headers.get(hdrs.CONTENT_ENCODING) From 1607be98825c6cebeafb3e19c9473184c85f4ca6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2024 10:33:50 +0000 Subject: [PATCH 192/296] Bump pytz from 2024.1 to 2024.2 (#9115) Bumps [pytz](https://github.com/stub42/pytz) from 2024.1 to 2024.2.
Commits
  • 3944f75 Bump version numbers to 2024.2 / 2024b
  • 640c9bd IANA 2024b
  • 382ca0c Squashed 'tz/' changes from 380c07cef..923e54bae
  • 96a1e88 Stop testing unavailable and EOL Python 3.5
  • 68186b6 Add support for Python 3.13
  • e994058 Run other jobs if one fails
  • 2326f9f Bump GitHub Actions
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pytz&package-manager=pip&previous-version=2024.1&new-version=2024.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 e54f64ccf31..fe76d5b3d07 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -188,7 +188,7 @@ python-on-whales==0.73.0 # via # -r requirements/lint.in # -r requirements/test.in -pytz==2024.1 +pytz==2024.2 # via babel pyyaml==6.0.2 # via pre-commit diff --git a/requirements/dev.txt b/requirements/dev.txt index 90fffae5826..9e6ed748fa6 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -183,7 +183,7 @@ python-on-whales==0.73.0 # via # -r requirements/lint.in # -r requirements/test.in -pytz==2024.1 +pytz==2024.2 # via babel pyyaml==6.0.2 # via pre-commit diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index a54c0f9224e..7abb4b04e16 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -48,7 +48,7 @@ pyenchant==3.2.2 # via sphinxcontrib-spelling pygments==2.18.0 # via sphinx -pytz==2024.1 +pytz==2024.2 # via babel requests==2.32.3 # via sphinx diff --git a/requirements/doc.txt b/requirements/doc.txt index fd36d67bc1a..324b5b87c93 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -46,7 +46,7 @@ pillow==9.5.0 # blockdiag pygments==2.18.0 # via sphinx -pytz==2024.1 +pytz==2024.2 # via babel requests==2.32.3 # via sphinx From 68980affc69a9f9b8ca3bf9c5e8d07bdb1494bb1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2024 10:49:05 +0000 Subject: [PATCH 193/296] Bump rich from 13.8.0 to 13.8.1 (#9116) Bumps [rich](https://github.com/Textualize/rich) from 13.8.0 to 13.8.1.
Release notes

Sourced from rich's releases.

The Python 3.13 release

[13.8.1] - 2024-09-10

Fixed

Changelog

Sourced from rich's changelog.

[13.8.1] - 2024-09-10

Fixed

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rich&package-manager=pip&previous-version=13.8.0&new-version=13.8.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index fe76d5b3d07..2d6e2171c3b 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -201,7 +201,7 @@ requests==2.32.3 # cherry-picker # python-on-whales # sphinx -rich==13.8.0 +rich==13.8.1 # via typer setuptools-git==1.2 # via -r requirements/test.in diff --git a/requirements/dev.txt b/requirements/dev.txt index 9e6ed748fa6..1995cf3bbe0 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -196,7 +196,7 @@ requests==2.32.3 # cherry-picker # python-on-whales # sphinx -rich==13.8.0 +rich==13.8.1 # via typer setuptools-git==1.2 # via -r requirements/test.in diff --git a/requirements/lint.txt b/requirements/lint.txt index 159b481e30e..43925da0796 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -86,7 +86,7 @@ pyyaml==6.0.2 # via pre-commit requests==2.32.3 # via python-on-whales -rich==13.8.0 +rich==13.8.1 # via typer shellingham==1.5.4 # via typer diff --git a/requirements/test.txt b/requirements/test.txt index 56d9a6c9ceb..7114023eaaf 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -102,7 +102,7 @@ regex==2024.7.24 # via re-assert requests==2.32.3 # via python-on-whales -rich==13.8.0 +rich==13.8.1 # via typer setuptools-git==1.2 # via -r requirements/test.in From 01e89211657d95c5ff53b2fe6d3c93b184e3d5fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2024 10:56:06 +0000 Subject: [PATCH 194/296] Bump importlib-resources from 6.4.4 to 6.4.5 (#9117) Bumps [importlib-resources](https://github.com/python/importlib_resources) from 6.4.4 to 6.4.5.
Changelog

Sourced from importlib-resources's changelog.

v6.4.5

Bugfixes

  • Omit sentinel values from a namespace path. (#311)
Commits
  • 284148b Finalize
  • 63a7bcb Merge pull request #315 from python/bugfix/311-non-path-namespace-paths
  • 2c145c5 Omit sentinel values from a namespace path.
  • 47d73b1 Add test capturing failure when resolving the MultiplexedPath for a namespace...
  • 4875bc5 Add type annotations for _candidate_paths
  • d84ca37 Fix typo in _temp_path comment.
  • 1a6fef2 Merge https://github.com/jaraco/skeleton
  • 790fa6e Include the trailing slash in disable_error_code(overload-overlap), also requ...
  • 2beb8b0 Add support for linking usernames.
  • 0c326f3 Add a degenerate nitpick_ignore for downstream consumers. Add a 'local' comme...
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=importlib-resources&package-manager=pip&previous-version=6.4.4&new-version=6.4.5)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 2d6e2171c3b..bc9196fe9ee 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -99,7 +99,7 @@ importlib-metadata==8.4.0 # via # build # sphinx -importlib-resources==6.4.4 +importlib-resources==6.4.5 # via towncrier incremental==24.7.2 # via towncrier diff --git a/requirements/dev.txt b/requirements/dev.txt index 1995cf3bbe0..5d742ac7407 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -97,7 +97,7 @@ importlib-metadata==8.4.0 # via # build # sphinx -importlib-resources==6.4.4 +importlib-resources==6.4.5 # via towncrier incremental==24.7.2 # via towncrier diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 7abb4b04e16..41bd5bc0886 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -28,7 +28,7 @@ imagesize==1.4.1 # via sphinx importlib-metadata==8.4.0 # via sphinx -importlib-resources==6.4.4 +importlib-resources==6.4.5 # via towncrier incremental==24.7.2 # via towncrier diff --git a/requirements/doc.txt b/requirements/doc.txt index 324b5b87c93..48a67c0643f 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -28,7 +28,7 @@ imagesize==1.4.1 # via sphinx importlib-metadata==8.4.0 # via sphinx -importlib-resources==6.4.4 +importlib-resources==6.4.5 # via towncrier incremental==24.7.2 # via towncrier From fb6726feeee841a480a1b08f4d02a8a73fd8229d Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:09:04 +0100 Subject: [PATCH 195/296] [PR #7043/bee613d0 backport][3.10] Add clarification about `GracefulExit` when using `handle_signals=True` (#9122) **This is a backport of PR #7043 as merged into master (bee613d090cab3b7c00a83604668181961b562ff).** Co-authored-by: Daste --- CHANGES/4414.doc | 1 + docs/web_reference.rst | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 CHANGES/4414.doc diff --git a/CHANGES/4414.doc b/CHANGES/4414.doc new file mode 100644 index 00000000000..b4be46afee8 --- /dev/null +++ b/CHANGES/4414.doc @@ -0,0 +1 @@ +Clarified that ``GracefulExit`` needs to be handled in ``AppRunner`` and ``ServerRunner`` when using ``handle_signals=True``. -- by :user:`Daste745` diff --git a/docs/web_reference.rst b/docs/web_reference.rst index bb22cfd6369..cdfe5a050e9 100644 --- a/docs/web_reference.rst +++ b/docs/web_reference.rst @@ -2734,7 +2734,8 @@ application on specific TCP or Unix socket, e.g.:: :param bool handle_signals: add signal handlers for :data:`signal.SIGINT` and :data:`signal.SIGTERM` (``False`` by - default). + default). These handlers will raise + :exc:`GracefulExit`. :param kwargs: named parameters to pass into web protocol. @@ -2807,7 +2808,8 @@ application on specific TCP or Unix socket, e.g.:: :param bool handle_signals: add signal handlers for :data:`signal.SIGINT` and :data:`signal.SIGTERM` (``False`` by - default). + default). These handlers will raise + :exc:`GracefulExit`. :param kwargs: named parameters to pass into web protocol. @@ -2938,6 +2940,16 @@ application on specific TCP or Unix socket, e.g.:: ``128`` by default. +.. exception:: GracefulExit + + Raised by signal handlers for :data:`signal.SIGINT` and :data:`signal.SIGTERM` + defined in :class:`AppRunner` and :class:`ServerRunner` + when ``handle_signals`` is set to ``True``. + + Inherited from :exc:`SystemExit`, + which exits with error code ``1`` if not handled. + + Utilities --------- From 8596dc2651a0da7ed6ea641517f4a96af31f3466 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:18:34 +0100 Subject: [PATCH 196/296] [PR #7043/bee613d0 backport][3.11] Add clarification about `GracefulExit` when using `handle_signals=True` (#9123) **This is a backport of PR #7043 as merged into master (bee613d090cab3b7c00a83604668181961b562ff).** Co-authored-by: Daste --- CHANGES/4414.doc | 1 + docs/web_reference.rst | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 CHANGES/4414.doc diff --git a/CHANGES/4414.doc b/CHANGES/4414.doc new file mode 100644 index 00000000000..b4be46afee8 --- /dev/null +++ b/CHANGES/4414.doc @@ -0,0 +1 @@ +Clarified that ``GracefulExit`` needs to be handled in ``AppRunner`` and ``ServerRunner`` when using ``handle_signals=True``. -- by :user:`Daste745` diff --git a/docs/web_reference.rst b/docs/web_reference.rst index 39b503de248..f2f5361ca43 100644 --- a/docs/web_reference.rst +++ b/docs/web_reference.rst @@ -2737,7 +2737,8 @@ application on specific TCP or Unix socket, e.g.:: :param bool handle_signals: add signal handlers for :data:`signal.SIGINT` and :data:`signal.SIGTERM` (``False`` by - default). + default). These handlers will raise + :exc:`GracefulExit`. :param kwargs: named parameters to pass into web protocol. @@ -2810,7 +2811,8 @@ application on specific TCP or Unix socket, e.g.:: :param bool handle_signals: add signal handlers for :data:`signal.SIGINT` and :data:`signal.SIGTERM` (``False`` by - default). + default). These handlers will raise + :exc:`GracefulExit`. :param kwargs: named parameters to pass into web protocol. @@ -2941,6 +2943,16 @@ application on specific TCP or Unix socket, e.g.:: ``128`` by default. +.. exception:: GracefulExit + + Raised by signal handlers for :data:`signal.SIGINT` and :data:`signal.SIGTERM` + defined in :class:`AppRunner` and :class:`ServerRunner` + when ``handle_signals`` is set to ``True``. + + Inherited from :exc:`SystemExit`, + which exits with error code ``1`` if not handled. + + Utilities --------- From 72739222fd42c77765077d118e35e849f1532661 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Wed, 11 Sep 2024 16:38:32 +0100 Subject: [PATCH 197/296] Add repr() test (#9121) --- tests/test_streams.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_streams.py b/tests/test_streams.py index 115371c806d..fcf13a91eb3 100644 --- a/tests/test_streams.py +++ b/tests/test_streams.py @@ -1126,6 +1126,7 @@ async def test_unread_empty(self) -> None: async def test_empty_stream_reader() -> None: s = streams.EmptyStreamReader() assert str(s) is not None + assert repr(s) == "" assert s.set_exception(ValueError()) is None assert s.exception() is None assert s.feed_eof() is None From 333a7a8df200a166b4c24925573903cb4eee1d9c Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2024 17:01:47 +0100 Subject: [PATCH 198/296] [PR #9121/72739222 backport][3.10] Add repr() test (#9125) **This is a backport of PR #9121 as merged into 3.11 (72739222fd42c77765077d118e35e849f1532661).** Co-authored-by: Sam Bull --- tests/test_streams.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_streams.py b/tests/test_streams.py index 115371c806d..fcf13a91eb3 100644 --- a/tests/test_streams.py +++ b/tests/test_streams.py @@ -1126,6 +1126,7 @@ async def test_unread_empty(self) -> None: async def test_empty_stream_reader() -> None: s = streams.EmptyStreamReader() assert str(s) is not None + assert repr(s) == "" assert s.set_exception(ValueError()) is None assert s.exception() is None assert s.feed_eof() is None From 9d529deb2b28f5fef411016d89ca8ed64cca49a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 10:40:22 +0000 Subject: [PATCH 199/296] Bump regex from 2024.7.24 to 2024.9.11 (#9127) Bumps [regex](https://github.com/mrabarnett/mrab-regex) from 2024.7.24 to 2024.9.11.
Changelog

Sourced from regex's changelog.

Version: 2024.9.14

Reverted to actions/download-artifact@v3 and
actions/upload-artifact@v3 in main.yml because GitHub Actions failed
when using them.

Version: 2024.9.13

Updated to actions/upload-artifact@v4 in main.yml.

Version: 2024.9.12

Updated to actions/download-artifact@v4 in main.yml.

Version: 2024.9.11

Updated to Unicode 16.0.0.

Version: 2024.7.24

Git issue 539: Bug: Partial matching fails on a simple
example

Version: 2024.6.22

Git issue 535: Regex fails Unicode 15.1 GraphemeBreakTest due
to missing new GB9c rule implementation

Version: 2024.5.15

Git issue 530: hangs with fuzzy and optionals

It's not hanging, it'll finish eventually. It's just an example of catastrophic backtracking.

The error printed when Ctrl+C is pressed does show a bug, though, which is now fixed.

Version: 2024.5.10

Updated for Python 3.13.

<time.h> now needs to be included explicitly because Python.h no longer includes it.

Version: 2024.4.28

Git issue 527: `VERBOSE`/`X` flag breaks `\N` escapes

Version: 2024.4.16

Git issue 525: segfault when fuzzy matching empty list

Version: 2023.12.25

Cannot get release notification action in main.yml to work.
Commenting it out for now.

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=regex&package-manager=pip&previous-version=2024.7.24&new-version=2024.9.11)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index bc9196fe9ee..62936f4aba2 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -194,7 +194,7 @@ pyyaml==6.0.2 # via pre-commit re-assert==1.1.0 # via -r requirements/test.in -regex==2024.7.24 +regex==2024.9.11 # via re-assert requests==2.32.3 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 5d742ac7407..1d288e81f3f 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -189,7 +189,7 @@ pyyaml==6.0.2 # via pre-commit re-assert==1.1.0 # via -r requirements/test.in -regex==2024.7.24 +regex==2024.9.11 # via re-assert requests==2.32.3 # via diff --git a/requirements/test.txt b/requirements/test.txt index 7114023eaaf..86ba7331b84 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -98,7 +98,7 @@ python-on-whales==0.73.0 # via -r requirements/test.in re-assert==1.1.0 # via -r requirements/test.in -regex==2024.7.24 +regex==2024.9.11 # via re-assert requests==2.32.3 # via python-on-whales From 295ed82d261bf6e14605214cd47fc7a8d40d49d3 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:11:35 +0100 Subject: [PATCH 200/296] [PR #7567/55a2af19 backport][3.11] Minor fixes to docs (#9132) **This is a backport of PR #7567 as merged into master (55a2af19d6549c8ddebd15edc25fda3c4b5094a9).** Co-authored-by: Sam Bull --- docs/client_advanced.rst | 4 ++-- docs/client_reference.rst | 8 ++++---- docs/streams.rst | 4 ++-- docs/web_reference.rst | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/client_advanced.rst b/docs/client_advanced.rst index 958e31dcc7c..26594a21b1c 100644 --- a/docs/client_advanced.rst +++ b/docs/client_advanced.rst @@ -618,7 +618,7 @@ Graceful Shutdown ----------------- When :class:`ClientSession` closes at the end of an ``async with`` -block (or through a direct :meth:`ClientSession.close()` call), the +block (or through a direct :meth:`ClientSession.close` call), the underlying connection remains open due to asyncio internal details. In practice, the underlying connection will close after a short while. However, if the event loop is stopped before the underlying @@ -658,7 +658,7 @@ on this. Character Set Detection ----------------------- -If you encounter a :exc:`UnicodeDecodeError` when using :meth:`ClientResponse.text()` +If you encounter a :exc:`UnicodeDecodeError` when using :meth:`ClientResponse.text` this may be because the response does not include the charset needed to decode the body. diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 77230a755c6..a16443f275e 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -134,7 +134,7 @@ The client session supports the context manager protocol for self closing. :param bool raise_for_status: - Automatically call :meth:`ClientResponse.raise_for_status()` for + Automatically call :meth:`ClientResponse.raise_for_status` for each response, ``False`` by default. This parameter can be overridden when making a request, e.g.:: @@ -325,7 +325,7 @@ The client session supports the context manager protocol for self closing. .. attribute:: raise_for_status - Should :meth:`ClientResponse.raise_for_status()` be called for each response + Should :meth:`ClientResponse.raise_for_status` be called for each response Either :class:`bool` or :class:`collections.abc.Callable` @@ -454,7 +454,7 @@ The client session supports the context manager protocol for self closing. :param bool expect100: Expect 100-continue response from server. ``False`` by default (optional). - :param bool raise_for_status: Automatically call :meth:`ClientResponse.raise_for_status()` for + :param bool raise_for_status: Automatically call :meth:`ClientResponse.raise_for_status` for response if set to ``True``. If set to ``None`` value from ``ClientSession`` will be used. ``None`` by default (optional). @@ -876,7 +876,7 @@ certification chaining. ``False`` by default (optional). :param bool raise_for_status: Automatically call - :meth:`ClientResponse.raise_for_status()` + :meth:`ClientResponse.raise_for_status` for response if set to ``True``. If set to ``None`` value from ``ClientSession`` will be used. diff --git a/docs/streams.rst b/docs/streams.rst index 9d49a80f1b6..8e4be9d5343 100644 --- a/docs/streams.rst +++ b/docs/streams.rst @@ -182,7 +182,7 @@ Helpers .. seealso:: - :meth:`StreamReader.at_eof()` + :meth:`StreamReader.at_eof` .. method:: StreamReader.at_eof() @@ -208,7 +208,7 @@ Helpers .. warning:: The method does not wake up waiters. - E.g. :meth:`~StreamReader.read()` will not be resumed. + E.g. :meth:`~StreamReader.read` will not be resumed. .. method:: wait_eof() diff --git a/docs/web_reference.rst b/docs/web_reference.rst index f2f5361ca43..f0da3237bd0 100644 --- a/docs/web_reference.rst +++ b/docs/web_reference.rst @@ -965,8 +965,8 @@ and :ref:`aiohttp-web-signals` handlers:: :meth:`receive` and others. To enable back-pressure from slow websocket clients treat methods - :meth:`ping()`, :meth:`pong()`, :meth:`send_str()`, - :meth:`send_bytes()`, :meth:`send_json()` as coroutines. By + :meth:`ping`, :meth:`pong`, :meth:`send_str`, + :meth:`send_bytes`, :meth:`send_json` as coroutines. By default write buffer size is set to 64k. :param bool autoping: Automatically send @@ -1652,7 +1652,7 @@ Application and Router :async: A :ref:`coroutine` that should be called on - server stopping but before :meth:`cleanup()`. + server stopping but before :meth:`cleanup`. The purpose of the method is calling :attr:`on_shutdown` signal handlers. From 828811a549df4dc55c52c8fa071b129c13bd06eb Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:11:45 +0100 Subject: [PATCH 201/296] [PR #7567/55a2af19 backport][3.10] Minor fixes to docs (#9131) **This is a backport of PR #7567 as merged into master (55a2af19d6549c8ddebd15edc25fda3c4b5094a9).** Co-authored-by: Sam Bull --- docs/client_advanced.rst | 4 ++-- docs/client_reference.rst | 8 ++++---- docs/streams.rst | 4 ++-- docs/web_reference.rst | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/client_advanced.rst b/docs/client_advanced.rst index 958e31dcc7c..26594a21b1c 100644 --- a/docs/client_advanced.rst +++ b/docs/client_advanced.rst @@ -618,7 +618,7 @@ Graceful Shutdown ----------------- When :class:`ClientSession` closes at the end of an ``async with`` -block (or through a direct :meth:`ClientSession.close()` call), the +block (or through a direct :meth:`ClientSession.close` call), the underlying connection remains open due to asyncio internal details. In practice, the underlying connection will close after a short while. However, if the event loop is stopped before the underlying @@ -658,7 +658,7 @@ on this. Character Set Detection ----------------------- -If you encounter a :exc:`UnicodeDecodeError` when using :meth:`ClientResponse.text()` +If you encounter a :exc:`UnicodeDecodeError` when using :meth:`ClientResponse.text` this may be because the response does not include the charset needed to decode the body. diff --git a/docs/client_reference.rst b/docs/client_reference.rst index bcd2108c1eb..1686aa7c113 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -132,7 +132,7 @@ The client session supports the context manager protocol for self closing. :param bool raise_for_status: - Automatically call :meth:`ClientResponse.raise_for_status()` for + Automatically call :meth:`ClientResponse.raise_for_status` for each response, ``False`` by default. This parameter can be overridden when making a request, e.g.:: @@ -323,7 +323,7 @@ The client session supports the context manager protocol for self closing. .. attribute:: raise_for_status - Should :meth:`ClientResponse.raise_for_status()` be called for each response + Should :meth:`ClientResponse.raise_for_status` be called for each response Either :class:`bool` or :class:`collections.abc.Callable` @@ -452,7 +452,7 @@ The client session supports the context manager protocol for self closing. :param bool expect100: Expect 100-continue response from server. ``False`` by default (optional). - :param bool raise_for_status: Automatically call :meth:`ClientResponse.raise_for_status()` for + :param bool raise_for_status: Automatically call :meth:`ClientResponse.raise_for_status` for response if set to ``True``. If set to ``None`` value from ``ClientSession`` will be used. ``None`` by default (optional). @@ -875,7 +875,7 @@ certification chaining. ``False`` by default (optional). :param bool raise_for_status: Automatically call - :meth:`ClientResponse.raise_for_status()` + :meth:`ClientResponse.raise_for_status` for response if set to ``True``. If set to ``None`` value from ``ClientSession`` will be used. diff --git a/docs/streams.rst b/docs/streams.rst index 9d49a80f1b6..8e4be9d5343 100644 --- a/docs/streams.rst +++ b/docs/streams.rst @@ -182,7 +182,7 @@ Helpers .. seealso:: - :meth:`StreamReader.at_eof()` + :meth:`StreamReader.at_eof` .. method:: StreamReader.at_eof() @@ -208,7 +208,7 @@ Helpers .. warning:: The method does not wake up waiters. - E.g. :meth:`~StreamReader.read()` will not be resumed. + E.g. :meth:`~StreamReader.read` will not be resumed. .. method:: wait_eof() diff --git a/docs/web_reference.rst b/docs/web_reference.rst index cdfe5a050e9..4efba726fa9 100644 --- a/docs/web_reference.rst +++ b/docs/web_reference.rst @@ -962,8 +962,8 @@ and :ref:`aiohttp-web-signals` handlers:: :meth:`receive` and others. To enable back-pressure from slow websocket clients treat methods - :meth:`ping()`, :meth:`pong()`, :meth:`send_str()`, - :meth:`send_bytes()`, :meth:`send_json()` as coroutines. By + :meth:`ping`, :meth:`pong`, :meth:`send_str`, + :meth:`send_bytes`, :meth:`send_json` as coroutines. By default write buffer size is set to 64k. :param bool autoping: Automatically send @@ -1649,7 +1649,7 @@ Application and Router :async: A :ref:`coroutine` that should be called on - server stopping but before :meth:`cleanup()`. + server stopping but before :meth:`cleanup`. The purpose of the method is calling :attr:`on_shutdown` signal handlers. From 5ad9ed25884879b45e1137bcb6cc6f6eac2f1427 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:34:27 +0100 Subject: [PATCH 202/296] [PR #9098/94685fb6 backport][3.11] Remove extra ``MultiDictProxy`` wrapper from ``BaseRequest.query`` (#9113) **This is a backport of PR #9098 as merged into master (94685fb672296629c93b41e1528268c2a667c806).** Co-authored-by: J. Nick Koston --- aiohttp/web_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py index 2465e6655ad..eca5063e30e 100644 --- a/aiohttp/web_request.py +++ b/aiohttp/web_request.py @@ -486,7 +486,7 @@ def raw_path(self) -> str: @reify def query(self) -> "MultiMapping[str]": """A multidict with all the variables in the query string.""" - return MultiDictProxy(self._rel_url.query) + return self._rel_url.query @reify def query_string(self) -> str: From 501f50310f989473c9edcd8b91cf514447a07685 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:34:49 +0100 Subject: [PATCH 203/296] [PR #9098/94685fb6 backport][3.10] Remove extra ``MultiDictProxy`` wrapper from ``BaseRequest.query`` (#9112) **This is a backport of PR #9098 as merged into master (94685fb672296629c93b41e1528268c2a667c806).** Co-authored-by: J. Nick Koston --- aiohttp/web_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py index 1d94c576794..f233afbbd44 100644 --- a/aiohttp/web_request.py +++ b/aiohttp/web_request.py @@ -486,7 +486,7 @@ def raw_path(self) -> str: @reify def query(self) -> "MultiMapping[str]": """A multidict with all the variables in the query string.""" - return MultiDictProxy(self._rel_url.query) + return self._rel_url.query @reify def query_string(self) -> str: From a81f1296d1fbefd5713d04231e9749f6a298b5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 12 Sep 2024 23:02:02 +0200 Subject: [PATCH 204/296] Implement binding to IPv6 addresses in the pytest server fixture (#9124) Co-authored-by: tan01 Co-authored-by: Sviatoslav Sydorenko --- CHANGES/4650.bugfix | 1 + aiohttp/test_utils.py | 5 ++++- tests/test_test_utils.py | 12 ++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 CHANGES/4650.bugfix diff --git a/CHANGES/4650.bugfix b/CHANGES/4650.bugfix new file mode 100644 index 00000000000..5c9fc17ff60 --- /dev/null +++ b/CHANGES/4650.bugfix @@ -0,0 +1 @@ +Implement binding to IPv6 addresses in the pytest server fixture. diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py index 13b6f4d9c50..08ce5bff9e1 100644 --- a/aiohttp/test_utils.py +++ b/aiohttp/test_utils.py @@ -119,10 +119,13 @@ async def start_server( await self.runner.setup() if not self.port: self.port = 0 + absolute_host = self.host try: version = ipaddress.ip_address(self.host).version except ValueError: version = 4 + if version == 6: + absolute_host = f"[{self.host}]" family = socket.AF_INET6 if version == 6 else socket.AF_INET _sock = self.socket_factory(self.host, self.port, family) self.host, self.port = _sock.getsockname()[:2] @@ -135,7 +138,7 @@ async def start_server( self.port = sockets[0].getsockname()[1] if not self.scheme: self.scheme = "https" if self._ssl else "http" - self._root = URL(f"{self.scheme}://{self.host}:{self.port}") + self._root = URL(f"{self.scheme}://{absolute_host}:{self.port}") @abstractmethod # pragma: no cover async def _make_runner(self, **kwargs: Any) -> BaseRunner: diff --git a/tests/test_test_utils.py b/tests/test_test_utils.py index 77349246616..a9c5179aedc 100644 --- a/tests/test_test_utils.py +++ b/tests/test_test_utils.py @@ -371,3 +371,15 @@ def factory(*args, **kwargs) -> socket: pass assert factory_called + + +@pytest.mark.parametrize( + ("hostname", "expected_host"), + [("127.0.0.1", "127.0.0.1"), ("localhost", "127.0.0.1"), ("::1", "::1")], +) +async def test_test_server_hostnames(hostname, expected_host, loop) -> None: + app = _create_example_app() + server = _TestServer(app, host=hostname, loop=loop) + async with server: + pass + assert server.host == expected_host From c2df9cb899066aaa7e6c05231e5257ed53e1d1e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 21:25:02 +0000 Subject: [PATCH 205/296] Bump importlib-metadata from 8.4.0 to 8.5.0 (#9128) Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 8.4.0 to 8.5.0.
Changelog

Sourced from importlib-metadata's changelog.

v8.5.0

Features

  • Deferred import of zipfile.Path (#502)
  • Deferred import of json (#503)
  • Rely on zipp overlay for zipfile.Path.
Commits
  • b34810b Finalize
  • 8c1d1fa Merge pull request #501 from Avasam/Pass-mypy-and-link-issues
  • afa39e8 Back out changes to tests._path
  • 8b909f9 Merge pull request #503 from danielhollas/defer-json
  • 2a3f50d Add news fragment.
  • 3f78dc1 Add comment to protect the deferred import.
  • 18eb2da Revert "Defer platform import"
  • 58832f2 Merge pull request #502 from danielhollas/defer-zipp
  • e3ce33b Add news fragment.
  • d11b67f Add comment to protect the deferred import.
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=importlib-metadata&package-manager=pip&previous-version=8.4.0&new-version=8.5.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 62936f4aba2..c6b76c6564a 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -95,7 +95,7 @@ idna==3.3 # yarl imagesize==1.4.1 # via sphinx -importlib-metadata==8.4.0 +importlib-metadata==8.5.0 # via # build # sphinx diff --git a/requirements/dev.txt b/requirements/dev.txt index 1d288e81f3f..6d5b0e200be 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -93,7 +93,7 @@ idna==3.4 # yarl imagesize==1.4.1 # via sphinx -importlib-metadata==8.4.0 +importlib-metadata==8.5.0 # via # build # sphinx diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 41bd5bc0886..17c44816fc9 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -26,7 +26,7 @@ idna==3.4 # via requests imagesize==1.4.1 # via sphinx -importlib-metadata==8.4.0 +importlib-metadata==8.5.0 # via sphinx importlib-resources==6.4.5 # via towncrier diff --git a/requirements/doc.txt b/requirements/doc.txt index 48a67c0643f..dba8517376f 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -26,7 +26,7 @@ idna==3.4 # via requests imagesize==1.4.1 # via sphinx -importlib-metadata==8.4.0 +importlib-metadata==8.5.0 # via sphinx importlib-resources==6.4.5 # via towncrier From 839d06079d81513ea09a9714b5e6c3fd7742673f Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 22:47:32 +0100 Subject: [PATCH 206/296] [PR #9124/a81f1296 backport][3.10] Implement binding to IPv6 addresses in the pytest server fixture (#9135) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **This is a backport of PR #9124 as merged into 3.11 (a81f1296d1fbefd5713d04231e9749f6a298b5e7).** Co-authored-by: Jörg Thalheim --- CHANGES/4650.bugfix | 1 + aiohttp/test_utils.py | 5 ++++- tests/test_test_utils.py | 12 ++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 CHANGES/4650.bugfix diff --git a/CHANGES/4650.bugfix b/CHANGES/4650.bugfix new file mode 100644 index 00000000000..5c9fc17ff60 --- /dev/null +++ b/CHANGES/4650.bugfix @@ -0,0 +1 @@ +Implement binding to IPv6 addresses in the pytest server fixture. diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py index 328561fb6a7..01496b6711a 100644 --- a/aiohttp/test_utils.py +++ b/aiohttp/test_utils.py @@ -119,10 +119,13 @@ async def start_server( await self.runner.setup() if not self.port: self.port = 0 + absolute_host = self.host try: version = ipaddress.ip_address(self.host).version except ValueError: version = 4 + if version == 6: + absolute_host = f"[{self.host}]" family = socket.AF_INET6 if version == 6 else socket.AF_INET _sock = self.socket_factory(self.host, self.port, family) self.host, self.port = _sock.getsockname()[:2] @@ -135,7 +138,7 @@ async def start_server( self.port = sockets[0].getsockname()[1] if not self.scheme: self.scheme = "https" if self._ssl else "http" - self._root = URL(f"{self.scheme}://{self.host}:{self.port}") + self._root = URL(f"{self.scheme}://{absolute_host}:{self.port}") @abstractmethod # pragma: no cover async def _make_runner(self, **kwargs: Any) -> BaseRunner: diff --git a/tests/test_test_utils.py b/tests/test_test_utils.py index 77349246616..a9c5179aedc 100644 --- a/tests/test_test_utils.py +++ b/tests/test_test_utils.py @@ -371,3 +371,15 @@ def factory(*args, **kwargs) -> socket: pass assert factory_called + + +@pytest.mark.parametrize( + ("hostname", "expected_host"), + [("127.0.0.1", "127.0.0.1"), ("localhost", "127.0.0.1"), ("::1", "::1")], +) +async def test_test_server_hostnames(hostname, expected_host, loop) -> None: + app = _create_example_app() + server = _TestServer(app, host=hostname, loop=loop) + async with server: + pass + assert server.host == expected_host From 306b4d0c4cbbf5e6257179f281254153517a5fc4 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Fri, 13 Sep 2024 22:18:55 +0100 Subject: [PATCH 207/296] Fix keepalive race condition (#9140) (#9142) (cherry picked from commit 37e3aa4639193c6423562a4a4dfbf3310772b7a6) --- CHANGES/9140.bugfix.rst | 1 + aiohttp/web_protocol.py | 2 +- tests/test_web_functional.py | 47 ++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 CHANGES/9140.bugfix.rst diff --git a/CHANGES/9140.bugfix.rst b/CHANGES/9140.bugfix.rst new file mode 100644 index 00000000000..c9b8f7bf4ea --- /dev/null +++ b/CHANGES/9140.bugfix.rst @@ -0,0 +1 @@ +Fixed race condition that could cause server to close connection incorrectly at keepalive timeout -- by :user:`Dreamosorcerer`. diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index a2f159c3b7c..a7f7b546903 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -437,7 +437,7 @@ def _process_keepalive(self) -> None: return # handler in idle state - if self._waiter: + if self._waiter and not self._waiter.done(): self.force_close() async def _handle_request( diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index ad9e7c288fc..5b2e5fe9353 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -24,6 +24,7 @@ from aiohttp.hdrs import CONTENT_LENGTH, CONTENT_TYPE, TRANSFER_ENCODING from aiohttp.test_utils import make_mocked_coro from aiohttp.typedefs import Handler +from aiohttp.web_protocol import RequestHandler try: import brotlicffi as brotli @@ -2242,3 +2243,49 @@ async def handler(_): assert TRANSFER_ENCODING not in resp.headers await resp.read() == b"" await resp.release() + + +async def test_keepalive_race_condition(aiohttp_client: Any) -> None: + protocol = None + orig_data_received = RequestHandler.data_received + + def delay_received(self, data: bytes) -> None: + """Emulate race condition. + + The keepalive callback needs to be called between data_received() and + when start() resumes from the waiter set within data_received(). + """ + data = orig_data_received(self, data) + if protocol is None: # First request creating the keepalive connection. + return data + + assert self is protocol + assert protocol._keepalive_handle is not None + # Cancel existing callback that would run at some point in future. + protocol._keepalive_handle.cancel() + protocol._keepalive_handle = None + + # Set next run time into the past and run callback manually. + protocol._next_keepalive_close_time = asyncio.get_running_loop().time() - 1 + protocol._process_keepalive() + + return data + + async def handler(request: web.Request) -> web.Response: + nonlocal protocol + protocol = request.protocol + return web.Response() + + target = "aiohttp.web_protocol.RequestHandler.data_received" + with mock.patch(target, delay_received): + app = web.Application() + app.router.add_get("/", handler) + client = await aiohttp_client(app) + + # Open connection, so we have a keepalive connection and reference to protocol. + async with client.get("/") as resp: + assert resp.status == 200 + assert protocol is not None + # Make 2nd request which will hit the race condition. + async with client.get("/") as resp: + assert resp.status == 200 From 7531deefa3458aa87dcfa61c379fc5542621b67e Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Fri, 13 Sep 2024 22:25:32 +0100 Subject: [PATCH 208/296] Fix keepalive race condition (#9140) (#9143) (cherry picked from commit 37e3aa4639193c6423562a4a4dfbf3310772b7a6) --- CHANGES/9140.bugfix.rst | 1 + aiohttp/web_protocol.py | 2 +- tests/test_web_functional.py | 47 ++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 CHANGES/9140.bugfix.rst diff --git a/CHANGES/9140.bugfix.rst b/CHANGES/9140.bugfix.rst new file mode 100644 index 00000000000..c9b8f7bf4ea --- /dev/null +++ b/CHANGES/9140.bugfix.rst @@ -0,0 +1 @@ +Fixed race condition that could cause server to close connection incorrectly at keepalive timeout -- by :user:`Dreamosorcerer`. diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index a2f159c3b7c..a7f7b546903 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -437,7 +437,7 @@ def _process_keepalive(self) -> None: return # handler in idle state - if self._waiter: + if self._waiter and not self._waiter.done(): self.force_close() async def _handle_request( diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index 6f612ffc011..969153b1603 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -24,6 +24,7 @@ from aiohttp.hdrs import CONTENT_LENGTH, CONTENT_TYPE, TRANSFER_ENCODING from aiohttp.test_utils import make_mocked_coro from aiohttp.typedefs import Handler +from aiohttp.web_protocol import RequestHandler try: import brotlicffi as brotli @@ -2246,3 +2247,49 @@ async def handler(_): assert TRANSFER_ENCODING not in resp.headers await resp.read() == b"" await resp.release() + + +async def test_keepalive_race_condition(aiohttp_client: Any) -> None: + protocol = None + orig_data_received = RequestHandler.data_received + + def delay_received(self, data: bytes) -> None: + """Emulate race condition. + + The keepalive callback needs to be called between data_received() and + when start() resumes from the waiter set within data_received(). + """ + data = orig_data_received(self, data) + if protocol is None: # First request creating the keepalive connection. + return data + + assert self is protocol + assert protocol._keepalive_handle is not None + # Cancel existing callback that would run at some point in future. + protocol._keepalive_handle.cancel() + protocol._keepalive_handle = None + + # Set next run time into the past and run callback manually. + protocol._next_keepalive_close_time = asyncio.get_running_loop().time() - 1 + protocol._process_keepalive() + + return data + + async def handler(request: web.Request) -> web.Response: + nonlocal protocol + protocol = request.protocol + return web.Response() + + target = "aiohttp.web_protocol.RequestHandler.data_received" + with mock.patch(target, delay_received): + app = web.Application() + app.router.add_get("/", handler) + client = await aiohttp_client(app) + + # Open connection, so we have a keepalive connection and reference to protocol. + async with client.get("/") as resp: + assert resp.status == 200 + assert protocol is not None + # Make 2nd request which will hit the race condition. + async with client.get("/") as resp: + assert resp.status == 200 From 73bb415d4b7b4a6a6bf4663018ac94f33d51f5a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:31:49 +0000 Subject: [PATCH 209/296] Bump zipp from 3.20.1 to 3.20.2 (#9151) Bumps [zipp](https://github.com/jaraco/zipp) from 3.20.1 to 3.20.2.
Changelog

Sourced from zipp's changelog.

v3.20.2

Bugfixes

  • Make zipp.compat.overlay.zipfile hashable. (#126)
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=zipp&package-manager=pip&previous-version=3.20.1&new-version=3.20.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 c6b76c6564a..6a62b419c33 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -289,7 +289,7 @@ wheel==0.44.0 # via pip-tools yarl==1.11.1 # via -r requirements/runtime-deps.in -zipp==3.20.1 +zipp==3.20.2 # via # importlib-metadata # importlib-resources diff --git a/requirements/dev.txt b/requirements/dev.txt index 6d5b0e200be..51d5161a1f3 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -281,7 +281,7 @@ wheel==0.44.0 # via pip-tools yarl==1.11.1 # via -r requirements/runtime-deps.in -zipp==3.20.1 +zipp==3.20.2 # via # importlib-metadata # importlib-resources diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 17c44816fc9..5ec2afcb9a6 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -90,7 +90,7 @@ urllib3==2.2.2 # via requests webcolors==24.8.0 # via blockdiag -zipp==3.20.1 +zipp==3.20.2 # via # importlib-metadata # importlib-resources diff --git a/requirements/doc.txt b/requirements/doc.txt index dba8517376f..5236e1c23d8 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -85,7 +85,7 @@ urllib3==2.2.2 # via requests webcolors==24.8.0 # via blockdiag -zipp==3.20.1 +zipp==3.20.2 # via # importlib-metadata # importlib-resources From de998dd3e8f51d37b1c5a045718fe1ca53fb679e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:47:50 +0000 Subject: [PATCH 210/296] Bump setuptools from 74.1.2 to 75.0.0 (#9153) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [setuptools](https://github.com/pypa/setuptools) from 74.1.2 to 75.0.0.
Changelog

Sourced from setuptools's changelog.

v75.0.0

Features

  • Declare also the dependencies used by distutils (adds jaraco.collections).

Deprecations and Removals

  • Removed upload_docs command. (#2971)
  • pypa/distutils#294#4649)

v74.1.3

Bugfixes

  • Fix cross-platform compilation using distutils._msvccompiler.MSVCCompiler -- by :user:saschanaz and :user:Avasam (#4648)
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=setuptools&package-manager=pip&previous-version=74.1.2&new-version=75.0.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 6a62b419c33..3f28b098138 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -297,7 +297,7 @@ zipp==3.20.2 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via pip-tools -setuptools==74.1.2 +setuptools==75.0.0 # via # blockdiag # incremental diff --git a/requirements/dev.txt b/requirements/dev.txt index 51d5161a1f3..f7df4925022 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -289,7 +289,7 @@ zipp==3.20.2 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via pip-tools -setuptools==74.1.2 +setuptools==75.0.0 # via # blockdiag # incremental diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 5ec2afcb9a6..056e7455347 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -96,7 +96,7 @@ zipp==3.20.2 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -setuptools==74.1.2 +setuptools==75.0.0 # via # blockdiag # incremental diff --git a/requirements/doc.txt b/requirements/doc.txt index 5236e1c23d8..4bf88490f35 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -91,7 +91,7 @@ zipp==3.20.2 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -setuptools==74.1.2 +setuptools==75.0.0 # via # blockdiag # incremental From 2b7ca81708f90d9223e1fe597a28993a0a03e617 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:54:23 +0000 Subject: [PATCH 211/296] Bump urllib3 from 2.2.2 to 2.2.3 (#9154) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.2.2 to 2.2.3.
Release notes

Sourced from urllib3's releases.

2.2.3

🚀 urllib3 is fundraising for HTTP/2 support

urllib3 is raising ~$40,000 USD to release HTTP/2 support and ensure long-term sustainable maintenance of the project after a sharp decline in financial support for 2023. If your company or organization uses Python and would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and thousands of other projects please consider contributing financially to ensure HTTP/2 support is developed sustainably and maintained for the long-haul.

Thank you for your support.

Features

  • Added support for Python 3.13. (#3473)

Bugfixes

  • Fixed the default encoding of chunked request bodies to be UTF-8 instead of ISO-8859-1. All other methods of supplying a request body already use UTF-8 starting in urllib3 v2.0. (#3053)
  • Fixed ResourceWarning on CONNECT with Python < 3.11.4 by backporting python/cpython#103472. (`#3252)
  • Adjust tolerance for floating-point comparison on Windows to avoid flakiness in CI (#3413)
  • Fixed a crash where certain standard library hash functions were absent in restricted environments. (#3432)
  • Fixed mypy error when adding to HTTPConnection.default_socket_options. (#3448)

HTTP/2 (experimental)

HTTP/2 support is still in early development.

  • Excluded Transfer-Encoding: chunked from HTTP/2 request body (#3425)
  • Added version checking for h2 (https://pypi.org/project/h2/) usage. Now only accepting supported h2 major version 4.x.x. (#3290)
  • Added a probing mechanism for determining whether a given target origin supports HTTP/2 via ALPN. (#3301)
  • Add support for sending a request body with HTTP/2 (#3302)

Full Changelog: https://github.com/urllib3/urllib3/compare/2.2.2...2.2.3

Changelog

Sourced from urllib3's changelog.

2.2.3 (2024-09-12)

Features

  • Added support for Python 3.13. ([#3473](https://github.com/urllib3/urllib3/issues/3473) <https://github.com/urllib3/urllib3/issues/3473>__)

Bugfixes

  • Fixed the default encoding of chunked request bodies to be UTF-8 instead of ISO-8859-1. All other methods of supplying a request body already use UTF-8 starting in urllib3 v2.0. ([#3053](https://github.com/urllib3/urllib3/issues/3053) <https://github.com/urllib3/urllib3/issues/3053>__)
  • Fixed ResourceWarning on CONNECT with Python `__)
  • Adjust tolerance for floating-point comparison on Windows to avoid flakiness in CI ([#3413](https://github.com/urllib3/urllib3/issues/3413) <https://github.com/urllib3/urllib3/issues/3413>__)
  • Fixed a crash where certain standard library hash functions were absent in restricted environments. ([#3432](https://github.com/urllib3/urllib3/issues/3432) <https://github.com/urllib3/urllib3/issues/3432>__)
  • Fixed mypy error when adding to HTTPConnection.default_socket_options. ([#3448](https://github.com/urllib3/urllib3/issues/3448) <https://github.com/urllib3/urllib3/issues/3448>__)

HTTP/2 (experimental)

HTTP/2 support is still in early development.

  • Excluded Transfer-Encoding: chunked from HTTP/2 request body ([#3425](https://github.com/urllib3/urllib3/issues/3425) <https://github.com/urllib3/urllib3/issues/3425>__)

  • Added version checking for h2 (https://pypi.org/project/h2/) usage.

    Now only accepting supported h2 major version 4.x.x. ([#3290](https://github.com/urllib3/urllib3/issues/3290) <https://github.com/urllib3/urllib3/issues/3290>__)

  • Added a probing mechanism for determining whether a given target origin supports HTTP/2 via ALPN. ([#3301](https://github.com/urllib3/urllib3/issues/3301) <https://github.com/urllib3/urllib3/issues/3301>__)

  • Add support for sending a request body with HTTP/2 ([#3302](https://github.com/urllib3/urllib3/issues/3302) <https://github.com/urllib3/urllib3/issues/3302>__)

Deprecations and Removals

  • Note for downstream distributors: the _version.py file has been removed and is now created at build time by hatch-vcs. ([#3412](https://github.com/urllib3/urllib3/issues/3412) <https://github.com/urllib3/urllib3/issues/3412>__)
  • Drop support for end-of-life PyPy3.8 and PyPy3.9. ([#3475](https://github.com/urllib3/urllib3/issues/3475) <https://github.com/urllib3/urllib3/issues/3475>__)
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=urllib3&package-manager=pip&previous-version=2.2.2&new-version=2.2.3)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 +- requirements/lint.txt | 2 +- requirements/test.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 3f28b098138..6be5b505474 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -273,7 +273,7 @@ typing-extensions==4.12.2 # typer uritemplate==4.1.1 # via gidgethub -urllib3==2.2.2 +urllib3==2.2.3 # via requests uvloop==0.20.0 ; platform_system != "Windows" # via diff --git a/requirements/dev.txt b/requirements/dev.txt index f7df4925022..e44e850e71e 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -265,7 +265,7 @@ typing-extensions==4.12.2 # typer uritemplate==4.1.1 # via gidgethub -urllib3==2.2.2 +urllib3==2.2.3 # via requests uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" # via diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 056e7455347..3045e447bca 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -86,7 +86,7 @@ towncrier==23.11.0 # via # -r requirements/doc.in # sphinxcontrib-towncrier -urllib3==2.2.2 +urllib3==2.2.3 # via requests webcolors==24.8.0 # via blockdiag diff --git a/requirements/doc.txt b/requirements/doc.txt index 4bf88490f35..32c5882a4ff 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -81,7 +81,7 @@ towncrier==23.11.0 # via # -r requirements/doc.in # sphinxcontrib-towncrier -urllib3==2.2.2 +urllib3==2.2.3 # via requests webcolors==24.8.0 # via blockdiag diff --git a/requirements/lint.txt b/requirements/lint.txt index 43925da0796..713418389b3 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -115,7 +115,7 @@ typing-extensions==4.12.2 # python-on-whales # rich # typer -urllib3==2.2.2 +urllib3==2.2.3 # via requests uvloop==0.20.0 ; platform_system != "Windows" # via -r requirements/lint.in diff --git a/requirements/test.txt b/requirements/test.txt index 86ba7331b84..d1d78bb00e1 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -131,7 +131,7 @@ typing-extensions==4.12.2 # python-on-whales # rich # typer -urllib3==2.2.2 +urllib3==2.2.3 # via requests uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in From 78187d8aaaa21374924ca2fc5c84df9a2b030aea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:58:19 +0000 Subject: [PATCH 212/296] Bump platformdirs from 4.3.2 to 4.3.3 (#9155) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [platformdirs](https://github.com/tox-dev/platformdirs) from 4.3.2 to 4.3.3.
Release notes

Sourced from platformdirs's releases.

4.3.3

What's Changed

New Contributors

Full Changelog: https://github.com/tox-dev/platformdirs/compare/4.3.2...4.3.3

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=platformdirs&package-manager=pip&previous-version=4.3.2&new-version=4.3.3)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 6be5b505474..9f985a4fe86 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -140,7 +140,7 @@ pillow==9.5.0 # blockdiag pip-tools==7.4.1 # via -r requirements/dev.in -platformdirs==4.3.2 +platformdirs==4.3.3 # via virtualenv pluggy==1.5.0 # via pytest diff --git a/requirements/dev.txt b/requirements/dev.txt index e44e850e71e..1592975b89c 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -137,7 +137,7 @@ pillow==9.5.0 # blockdiag pip-tools==7.4.1 # via -r requirements/dev.in -platformdirs==4.3.2 +platformdirs==4.3.3 # via virtualenv pluggy==1.5.0 # via pytest diff --git a/requirements/lint.txt b/requirements/lint.txt index 713418389b3..5e8e8a0ec51 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -56,7 +56,7 @@ nodeenv==1.9.1 # via pre-commit packaging==24.1 # via pytest -platformdirs==4.3.2 +platformdirs==4.3.3 # via virtualenv pluggy==1.5.0 # via pytest From c27fe0d1f2460c5c91674c4421e496d24173cdee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:04:49 +0000 Subject: [PATCH 213/296] Bump identify from 2.6.0 to 2.6.1 (#9156) Bumps [identify](https://github.com/pre-commit/identify) from 2.6.0 to 2.6.1.
Commits
  • d1032c9 v2.6.1
  • b980f11 Merge pull request #476 from AleksaC/astro
  • 52ba50e Merge pull request #477 from pre-commit/pre-commit-ci-update-config
  • f4ca44e [pre-commit.ci] pre-commit autoupdate
  • 2573941 add astro extension
  • eca58eb Merge pull request #475 from pre-commit/pre-commit-ci-update-config
  • 4cbbd37 [pre-commit.ci] pre-commit autoupdate
  • bcde20e Merge pull request #472 from pre-commit/pre-commit-ci-update-config
  • 53d2329 [pre-commit.ci] pre-commit autoupdate
  • 1d48177 Merge pull request #471 from pre-commit/pre-commit-ci-update-config
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=identify&package-manager=pip&previous-version=2.6.0&new-version=2.6.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 9f985a4fe86..7868f665797 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -86,7 +86,7 @@ gidgethub==5.3.0 # via cherry-picker gunicorn==23.0.0 # via -r requirements/base.in -identify==2.6.0 +identify==2.6.1 # via pre-commit idna==3.3 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 1592975b89c..12b6c9e3491 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -84,7 +84,7 @@ gidgethub==5.3.0 # via cherry-picker gunicorn==23.0.0 # via -r requirements/base.in -identify==2.6.0 +identify==2.6.1 # via pre-commit idna==3.4 # via diff --git a/requirements/lint.txt b/requirements/lint.txt index 5e8e8a0ec51..dc451770b10 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -36,7 +36,7 @@ filelock==3.16.0 # via virtualenv freezegun==1.5.1 # via -r requirements/lint.in -identify==2.6.0 +identify==2.6.1 # via pre-commit idna==3.7 # via From 478d27dd43f57436176c3c4d5d69d6d0b522845c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 11:13:48 +0000 Subject: [PATCH 214/296] Bump pypa/cibuildwheel from 2.20.0 to 2.21.1 (#9163) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.20.0 to 2.21.1.
Release notes

Sourced from pypa/cibuildwheel's releases.

Version 2.21.1

  • 🐛 Fix a bug in the Linux build, where files copied to the container would have invalid ownership permissions (#2007)
  • 🐛 Fix a bug on Windows where cibuildwheel would call upon uv to install dependencies for versions of CPython that it does not support (#2005)
  • 🐛 Fix a bug where uv 0.4.10 would not use the right Python when testing on Linux. (#2008)
  • 🛠 Bump our documentation pins, fixes an issue with a missing package (#2011)

Version 2.21.0

  • ⚠️ Update CPython 3.12 to 3.12.6, which changes the macOS minimum deployment target on CPython 3.12 from macOS 10.9 to macOS 10.13 (#1998)
  • 🛠 Changes the behaviour when inheriting config-settings in TOML overrides - rather than extending each key, which is rarely useful, individual keys will override previously set values. (#1803)
  • 🛠 Update CPython 3.13 to 3.13.0rc2 (#1998)
  • ✨ Adds support for multiarch OCI images (#1961)
  • 🐛 Fixes some bugs building Linux wheels on macOS. (#1961)
  • ⚠️ Changes the minimum version of Docker/Podman to Docker API version 1.43, Podman API version 3. The only mainstream runner this should affect is Travis Graviton2 runners - if so you can upgrade your version of Docker. (#1961)
Changelog

Sourced from pypa/cibuildwheel's changelog.

v2.21.1

16 September 2024

  • 🐛 Fix a bug in the Linux build, where files copied to the container would have invalid ownership permissions (#2007)
  • 🐛 Fix a bug on Windows where cibuildwheel would call upon uv to install dependencies for versions of CPython that it does not support (#2005)
  • 🐛 Fix a bug where uv 0.4.10 would not use the right Python when testing on Linux. (#2008)
  • 🛠 Bump our documentation pins, fixes an issue with a missing package (#2011)

v2.21.0

13 September 2024

  • ⚠️ Update CPython 3.12 to 3.12.6, which changes the macOS minimum deployment target on CPython 3.12 from macOS 10.9 to macOS 10.13 (#1998)
  • 🛠 Changes the behaviour when inheriting config-settings in TOML overrides - rather than extending each key, which is rarely useful, individual keys will override previously set values. (#1803)
  • 🛠 Update CPython 3.13 to 3.13.0rc2 (#1998)
  • ✨ Adds support for multiarch OCI images (#1961)
  • 🐛 Fixes some bugs building Linux wheels on macOS. (#1961)
  • ⚠️ Changes the minimum version of Docker/Podman to Docker API version 1.43, Podman API version 3. The only mainstream runner this should affect is Travis Graviton2 runners - if so you can upgrade your version of Docker. (#1961)
Commits
  • d4a2945 Bump version: v2.21.1
  • 9913c03 [pre-commit.ci] pre-commit autoupdate (#2013)
  • c0e28d3 fix: support uv 0.4.10+ on Linux and update dependencies (#2008)
  • 8c42e79 fix: file ownership of files copied into the container (#2007)
  • 01ecd4e docs: bump pinned versions (#2011)
  • 33da1f7 fix: do not use uv to setup python on windows when conditions are not met (...
  • 79b0dd3 Bump version: v2.21.0
  • 0787a44 fix: enforce minimum version of docker/podman (#1961)
  • fd11286 [Bot] Update dependencies (#1998)
  • 22dc864 [pre-commit.ci] pre-commit autoupdate (#2000)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pypa/cibuildwheel&package-manager=github_actions&previous-version=2.20.0&new-version=2.21.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 df27a9108d6..54ceb6b74cd 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -354,7 +354,7 @@ jobs: run: | make cythonize - name: Build wheels - uses: pypa/cibuildwheel@v2.20.0 + uses: pypa/cibuildwheel@v2.21.1 env: CIBW_ARCHS_MACOS: x86_64 arm64 universal2 - uses: actions/upload-artifact@v3 From 22885ae872a7acb09c701f907714fe987dca92bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 11:51:02 +0000 Subject: [PATCH 215/296] Bump setuptools from 75.0.0 to 75.1.0 (#9164) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [setuptools](https://github.com/pypa/setuptools) from 75.0.0 to 75.1.0.
Changelog

Sourced from setuptools's changelog.

v75.1.0

Features

  • Deprecated bdist_wheel.universal configuration. (#4617)

Bugfixes

  • Removed reference to upload_docs module in entry points. (#4650)
Commits
  • 3106af0 Bump version: 75.0.0 → 75.1.0
  • 37c3d27 Removed reference to upload_docs module in entry points.
  • 9fb53fd Merge pull request #4617 from abravalheri/issue-4612
  • cd3ba7d Merge pull request #4644 from DimitriPapadopoulos/codespell
  • 8513d29 Fix a couple typos found by codespell
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=setuptools&package-manager=pip&previous-version=75.0.0&new-version=75.1.0)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 7868f665797..18ab4a97475 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -297,7 +297,7 @@ zipp==3.20.2 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via pip-tools -setuptools==75.0.0 +setuptools==75.1.0 # via # blockdiag # incremental diff --git a/requirements/dev.txt b/requirements/dev.txt index 12b6c9e3491..4e9506ffbe4 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -289,7 +289,7 @@ zipp==3.20.2 # The following packages are considered to be unsafe in a requirements file: pip==24.2 # via pip-tools -setuptools==75.0.0 +setuptools==75.1.0 # via # blockdiag # incremental diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 3045e447bca..57cfa253fe7 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -96,7 +96,7 @@ zipp==3.20.2 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -setuptools==75.0.0 +setuptools==75.1.0 # via # blockdiag # incremental diff --git a/requirements/doc.txt b/requirements/doc.txt index 32c5882a4ff..49c8f3864ac 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -91,7 +91,7 @@ zipp==3.20.2 # importlib-resources # The following packages are considered to be unsafe in a requirements file: -setuptools==75.0.0 +setuptools==75.1.0 # via # blockdiag # incremental From d664327d2cb954eb65bbd006482c815f44467986 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 17 Sep 2024 17:45:33 +0200 Subject: [PATCH 216/296] [PR #9158/bf022b3 backport][3.10] Cache construction of middleware handlers (#9165) --- CHANGES/9158.misc.rst | 3 +++ aiohttp/web_app.py | 42 +++++++++++++++++++++++++++--------- tests/test_web_middleware.py | 22 ++++++++++++------- 3 files changed, 49 insertions(+), 18 deletions(-) create mode 100644 CHANGES/9158.misc.rst diff --git a/CHANGES/9158.misc.rst b/CHANGES/9158.misc.rst new file mode 100644 index 00000000000..8d87623c056 --- /dev/null +++ b/CHANGES/9158.misc.rst @@ -0,0 +1,3 @@ +Significantly improved performance of middlewares -- by :user:`bdraco`. + +The construction of the middleware wrappers is now cached and is built once per handler instead of on every request. diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index 3510bffda60..b8768064507 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -1,7 +1,7 @@ import asyncio import logging import warnings -from functools import partial, update_wrapper +from functools import lru_cache, partial, update_wrapper from typing import ( TYPE_CHECKING, Any, @@ -38,7 +38,7 @@ from .http_parser import RawRequestMessage from .log import web_logger from .streams import StreamReader -from .typedefs import Middleware +from .typedefs import Handler, Middleware from .web_exceptions import NotAppKeyWarning from .web_log import AccessLogger from .web_middlewares import _fix_request_current_app @@ -79,6 +79,17 @@ _Resource = TypeVar("_Resource", bound=AbstractResource) +@lru_cache(None) +def _build_middlewares( + handler: Handler, apps: Tuple["Application", ...] +) -> Callable[[Request], Awaitable[StreamResponse]]: + """Apply middlewares to handler.""" + for app in apps: + for m, _ in app._middlewares_handlers: # type: ignore[union-attr] + handler = update_wrapper(partial(m, handler=handler), handler) # type: ignore[misc] + return handler + + class Application(MutableMapping[Union[str, AppKey[Any]], Any]): ATTRS = frozenset( [ @@ -89,6 +100,7 @@ class Application(MutableMapping[Union[str, AppKey[Any]], Any]): "_handler_args", "_middlewares", "_middlewares_handlers", + "_has_legacy_middlewares", "_run_middlewares", "_state", "_frozen", @@ -143,6 +155,7 @@ def __init__( self._middlewares_handlers: _MiddlewaresHandlers = None # initialized on freezing self._run_middlewares: Optional[bool] = None + self._has_legacy_middlewares: bool = True self._state: Dict[Union[AppKey[Any], str], object] = {} self._frozen = False @@ -228,6 +241,9 @@ def __len__(self) -> int: def __iter__(self) -> Iterator[Union[str, AppKey[Any]]]: return iter(self._state) + def __hash__(self) -> int: + return id(self) + @overload # type: ignore[override] def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: ... @@ -284,6 +300,9 @@ def pre_freeze(self) -> None: self._on_shutdown.freeze() self._on_cleanup.freeze() self._middlewares_handlers = tuple(self._prepare_middleware()) + self._has_legacy_middlewares = any( + not new_style for _, new_style in self._middlewares_handlers + ) # If current app and any subapp do not have middlewares avoid run all # of the code footprint that it implies, which have a middleware @@ -525,14 +544,17 @@ async def _handle(self, request: Request) -> StreamResponse: handler = match_info.handler if self._run_middlewares: - for app in match_info.apps[::-1]: - for m, new_style in app._middlewares_handlers: # type: ignore[union-attr] - if new_style: - handler = update_wrapper( - partial(m, handler=handler), handler # type: ignore[misc] - ) - else: - handler = await m(app, handler) # type: ignore[arg-type,assignment] + if not self._has_legacy_middlewares: + handler = _build_middlewares(handler, match_info.apps[::-1]) + else: + for app in match_info.apps[::-1]: + for m, new_style in app._middlewares_handlers: # type: ignore[union-attr] + if new_style: + handler = update_wrapper( + partial(m, handler=handler), handler # type: ignore[misc] + ) + else: + handler = await m(app, handler) # type: ignore[arg-type,assignment] resp = await handler(request) diff --git a/tests/test_web_middleware.py b/tests/test_web_middleware.py index dbe23e02035..9c4462be409 100644 --- a/tests/test_web_middleware.py +++ b/tests/test_web_middleware.py @@ -24,10 +24,13 @@ async def middleware(request, handler: Handler): app.middlewares.append(middleware) app.router.add_route("GET", "/", handler) client = await aiohttp_client(app) - resp = await client.get("/") - assert 201 == resp.status - txt = await resp.text() - assert "OK[MIDDLEWARE]" == txt + + # Call twice to verify cache works + for _ in range(2): + resp = await client.get("/") + assert 201 == resp.status + txt = await resp.text() + assert "OK[MIDDLEWARE]" == txt async def test_middleware_handles_exception(loop, aiohttp_client) -> None: @@ -44,10 +47,13 @@ async def middleware(request, handler: Handler): app.middlewares.append(middleware) app.router.add_route("GET", "/", handler) client = await aiohttp_client(app) - resp = await client.get("/") - assert 501 == resp.status - txt = await resp.text() - assert "Error text[MIDDLEWARE]" == txt + + # Call twice to verify cache works + for _ in range(2): + resp = await client.get("/") + assert 501 == resp.status + txt = await resp.text() + assert "Error text[MIDDLEWARE]" == txt async def test_middleware_chain(loop, aiohttp_client) -> None: From 94ecbaaf6f361542bdc7db7bb393bae0258e7125 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 17 Sep 2024 17:52:20 +0200 Subject: [PATCH 217/296] [PR #9158/bf022b3 backport][3.11] Cache construction of middleware handlers (#9166) --- CHANGES/9158.misc.rst | 3 +++ aiohttp/web_app.py | 42 +++++++++++++++++++++++++++--------- tests/test_web_middleware.py | 22 ++++++++++++------- 3 files changed, 49 insertions(+), 18 deletions(-) create mode 100644 CHANGES/9158.misc.rst diff --git a/CHANGES/9158.misc.rst b/CHANGES/9158.misc.rst new file mode 100644 index 00000000000..8d87623c056 --- /dev/null +++ b/CHANGES/9158.misc.rst @@ -0,0 +1,3 @@ +Significantly improved performance of middlewares -- by :user:`bdraco`. + +The construction of the middleware wrappers is now cached and is built once per handler instead of on every request. diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index 8403bbbc826..8d109f793ca 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -1,7 +1,7 @@ import asyncio import logging import warnings -from functools import partial, update_wrapper +from functools import lru_cache, partial, update_wrapper from typing import ( TYPE_CHECKING, Any, @@ -38,7 +38,7 @@ from .http_parser import RawRequestMessage from .log import web_logger from .streams import StreamReader -from .typedefs import Middleware +from .typedefs import Handler, Middleware from .web_exceptions import NotAppKeyWarning from .web_log import AccessLogger from .web_middlewares import _fix_request_current_app @@ -79,6 +79,17 @@ _Resource = TypeVar("_Resource", bound=AbstractResource) +@lru_cache(None) +def _build_middlewares( + handler: Handler, apps: Tuple["Application", ...] +) -> Callable[[Request], Awaitable[StreamResponse]]: + """Apply middlewares to handler.""" + for app in apps: + for m, _ in app._middlewares_handlers: # type: ignore[union-attr] + handler = update_wrapper(partial(m, handler=handler), handler) # type: ignore[misc] + return handler + + class Application(MutableMapping[Union[str, AppKey[Any]], Any]): ATTRS = frozenset( [ @@ -89,6 +100,7 @@ class Application(MutableMapping[Union[str, AppKey[Any]], Any]): "_handler_args", "_middlewares", "_middlewares_handlers", + "_has_legacy_middlewares", "_run_middlewares", "_state", "_frozen", @@ -143,6 +155,7 @@ def __init__( self._middlewares_handlers: _MiddlewaresHandlers = None # initialized on freezing self._run_middlewares: Optional[bool] = None + self._has_legacy_middlewares: bool = True self._state: Dict[Union[AppKey[Any], str], object] = {} self._frozen = False @@ -228,6 +241,9 @@ def __len__(self) -> int: def __iter__(self) -> Iterator[Union[str, AppKey[Any]]]: return iter(self._state) + def __hash__(self) -> int: + return id(self) + @overload # type: ignore[override] def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: ... @@ -284,6 +300,9 @@ def pre_freeze(self) -> None: self._on_shutdown.freeze() self._on_cleanup.freeze() self._middlewares_handlers = tuple(self._prepare_middleware()) + self._has_legacy_middlewares = any( + not new_style for _, new_style in self._middlewares_handlers + ) # If current app and any subapp do not have middlewares avoid run all # of the code footprint that it implies, which have a middleware @@ -525,14 +544,17 @@ async def _handle(self, request: Request) -> StreamResponse: handler = match_info.handler if self._run_middlewares: - for app in match_info.apps[::-1]: - for m, new_style in app._middlewares_handlers: # type: ignore[union-attr] - if new_style: - handler = update_wrapper( - partial(m, handler=handler), handler # type: ignore[misc] - ) - else: - handler = await m(app, handler) # type: ignore[arg-type,assignment] + if not self._has_legacy_middlewares: + handler = _build_middlewares(handler, match_info.apps[::-1]) + else: + for app in match_info.apps[::-1]: + for m, new_style in app._middlewares_handlers: # type: ignore[union-attr] + if new_style: + handler = update_wrapper( + partial(m, handler=handler), handler # type: ignore[misc] + ) + else: + handler = await m(app, handler) # type: ignore[arg-type,assignment] resp = await handler(request) diff --git a/tests/test_web_middleware.py b/tests/test_web_middleware.py index dbe23e02035..9c4462be409 100644 --- a/tests/test_web_middleware.py +++ b/tests/test_web_middleware.py @@ -24,10 +24,13 @@ async def middleware(request, handler: Handler): app.middlewares.append(middleware) app.router.add_route("GET", "/", handler) client = await aiohttp_client(app) - resp = await client.get("/") - assert 201 == resp.status - txt = await resp.text() - assert "OK[MIDDLEWARE]" == txt + + # Call twice to verify cache works + for _ in range(2): + resp = await client.get("/") + assert 201 == resp.status + txt = await resp.text() + assert "OK[MIDDLEWARE]" == txt async def test_middleware_handles_exception(loop, aiohttp_client) -> None: @@ -44,10 +47,13 @@ async def middleware(request, handler: Handler): app.middlewares.append(middleware) app.router.add_route("GET", "/", handler) client = await aiohttp_client(app) - resp = await client.get("/") - assert 501 == resp.status - txt = await resp.text() - assert "Error text[MIDDLEWARE]" == txt + + # Call twice to verify cache works + for _ in range(2): + resp = await client.get("/") + assert 501 == resp.status + txt = await resp.text() + assert "Error text[MIDDLEWARE]" == txt async def test_middleware_chain(loop, aiohttp_client) -> None: From 9ad236dfe0894829a05a72c3b568ec5d0c9430ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:06:45 +0000 Subject: [PATCH 218/296] Bump platformdirs from 4.3.3 to 4.3.6 (#9176) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [platformdirs](https://github.com/tox-dev/platformdirs) from 4.3.3 to 4.3.6.
Release notes

Sourced from platformdirs's releases.

4.3.6

What's Changed

Full Changelog: https://github.com/tox-dev/platformdirs/compare/4.3.5...4.3.6

4.3.5

What's Changed

Full Changelog: https://github.com/tox-dev/platformdirs/compare/4.3.4...4.3.5

4.3.4

What's Changed

Full Changelog: https://github.com/tox-dev/platformdirs/compare/4.3.3...4.3.4

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=platformdirs&package-manager=pip&previous-version=4.3.3&new-version=4.3.6)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 18ab4a97475..4aad0716b8c 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -140,7 +140,7 @@ pillow==9.5.0 # blockdiag pip-tools==7.4.1 # via -r requirements/dev.in -platformdirs==4.3.3 +platformdirs==4.3.6 # via virtualenv pluggy==1.5.0 # via pytest diff --git a/requirements/dev.txt b/requirements/dev.txt index 4e9506ffbe4..98c40875498 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -137,7 +137,7 @@ pillow==9.5.0 # blockdiag pip-tools==7.4.1 # via -r requirements/dev.in -platformdirs==4.3.3 +platformdirs==4.3.6 # via virtualenv pluggy==1.5.0 # via pytest diff --git a/requirements/lint.txt b/requirements/lint.txt index dc451770b10..ab0282a1b68 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -56,7 +56,7 @@ nodeenv==1.9.1 # via pre-commit packaging==24.1 # via pytest -platformdirs==4.3.3 +platformdirs==4.3.6 # via virtualenv pluggy==1.5.0 # via pytest From 930f42d30f590f179727ee77df52d041f8bbc024 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:26:15 +0000 Subject: [PATCH 219/296] Bump filelock from 3.16.0 to 3.16.1 (#9177) 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.16.0 to 3.16.1.
Release notes

Sourced from filelock's releases.

3.16.1

What's Changed

Full Changelog: https://github.com/tox-dev/filelock/compare/3.16.0...3.16.1

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=filelock&package-manager=pip&previous-version=3.16.0&new-version=3.16.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 4aad0716b8c..15b8eac1c91 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -70,7 +70,7 @@ docutils==0.20.1 # via sphinx exceptiongroup==1.2.2 # via pytest -filelock==3.16.0 +filelock==3.16.1 # via virtualenv freezegun==1.5.1 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 98c40875498..d9ea10584ef 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -68,7 +68,7 @@ docutils==0.20.1 # via sphinx exceptiongroup==1.2.2 # via pytest -filelock==3.16.0 +filelock==3.16.1 # via virtualenv freezegun==1.5.1 # via diff --git a/requirements/lint.txt b/requirements/lint.txt index ab0282a1b68..29695178a5a 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -32,7 +32,7 @@ distlib==0.3.8 # via virtualenv exceptiongroup==1.2.2 # via pytest -filelock==3.16.0 +filelock==3.16.1 # via virtualenv freezegun==1.5.1 # via -r requirements/lint.in From 1ec476bab7aab9d5d43dcb790387ad690d2647a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:09:12 +0000 Subject: [PATCH 220/296] Bump pydantic from 2.9.1 to 2.9.2 (#9184) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.9.1 to 2.9.2.
Release notes

Sourced from pydantic's releases.

v2.9.2 (2024-09-17)

What's Changed

Fixes

Full Changelog: https://github.com/pydantic/pydantic/compare/v2.9.1...v2.9.2

Changelog

Sourced from pydantic's changelog.

v2.9.2 (2024-09-17)

GitHub release

What's Changed

Fixes

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pydantic&package-manager=pip&previous-version=2.9.1&new-version=2.9.2)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 | 4 ++-- requirements/dev.txt | 4 ++-- requirements/lint.txt | 4 ++-- requirements/test.txt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 15b8eac1c91..f9ce299d906 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -152,9 +152,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.9.1 +pydantic==2.9.2 # via python-on-whales -pydantic-core==2.23.3 +pydantic-core==2.23.4 # via pydantic pyenchant==3.2.2 # via sphinxcontrib-spelling diff --git a/requirements/dev.txt b/requirements/dev.txt index d9ea10584ef..634a1085ecd 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -149,9 +149,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.9.1 +pydantic==2.9.2 # via python-on-whales -pydantic-core==2.23.3 +pydantic-core==2.23.4 # via pydantic pygments==2.18.0 # via diff --git a/requirements/lint.txt b/requirements/lint.txt index 29695178a5a..0e51b0ac55c 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -66,9 +66,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.9.1 +pydantic==2.9.2 # via python-on-whales -pydantic-core==2.23.3 +pydantic-core==2.23.4 # via pydantic pygments==2.18.0 # via rich diff --git a/requirements/test.txt b/requirements/test.txt index d1d78bb00e1..bf1be8a9e33 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -77,9 +77,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.9.1 +pydantic==2.9.2 # via python-on-whales -pydantic-core==2.23.3 +pydantic-core==2.23.4 # via pydantic pygments==2.18.0 # via rich From 5e43625bab8f23f443d4d5999084b7de1ec18660 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:13:33 +0000 Subject: [PATCH 221/296] Bump virtualenv from 20.26.4 to 20.26.5 (#9179) Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.26.4 to 20.26.5.
Changelog

Sourced from virtualenv's changelog.

v20.26.5 (2024-09-17)

Bugfixes - 20.26.5

- Upgrade embedded wheels: setuptools to ``75.1.0`` from
``74.1.2`` - by :user:`gaborbernat`. (:issue:`2765`)
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=virtualenv&package-manager=pip&previous-version=20.26.4&new-version=20.26.5)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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 f9ce299d906..d1069b4f590 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -279,7 +279,7 @@ uvloop==0.20.0 ; platform_system != "Windows" # via # -r requirements/base.in # -r requirements/lint.in -virtualenv==20.26.4 +virtualenv==20.26.5 # via pre-commit wait-for-it==2.2.2 # via -r requirements/test.in diff --git a/requirements/dev.txt b/requirements/dev.txt index 634a1085ecd..46be3754db8 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -271,7 +271,7 @@ uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpytho # via # -r requirements/base.in # -r requirements/lint.in -virtualenv==20.26.4 +virtualenv==20.26.5 # via pre-commit wait-for-it==2.2.2 # via -r requirements/test.in diff --git a/requirements/lint.txt b/requirements/lint.txt index 0e51b0ac55c..f5bdccfd695 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -119,5 +119,5 @@ urllib3==2.2.3 # via requests uvloop==0.20.0 ; platform_system != "Windows" # via -r requirements/lint.in -virtualenv==20.26.4 +virtualenv==20.26.5 # via pre-commit From a24d690a46f70fecb8b35ae4dda1fc41f982e226 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Peeters Date: Wed, 18 Sep 2024 14:22:41 +0200 Subject: [PATCH 222/296] [PR #9160 backport][3.11] Fix badly encoded charset crashing instead of falling back to detector (#9182) Backport of #9160 to 3.11 --- CHANGES/9160.bugfix | 1 + CONTRIBUTORS.txt | 1 + aiohttp/client_reqrep.py | 2 +- tests/test_client_response.py | 32 +++++++++++++++++++++++++++++++- 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 CHANGES/9160.bugfix diff --git a/CHANGES/9160.bugfix b/CHANGES/9160.bugfix new file mode 100644 index 00000000000..253cfd07d50 --- /dev/null +++ b/CHANGES/9160.bugfix @@ -0,0 +1 @@ +Fixed badly encoded charset crashing when getting response text instead of falling back to charset detector. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index cf22583989f..92e1666fbc6 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -272,6 +272,7 @@ Pawel Kowalski Pawel Miech Pepe Osca Philipp A. +Pierre-Louis Peeters Pieter van Beek Qiao Han Rafael Viotti diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 7d4467dbdbb..3fe34e21968 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -1159,7 +1159,7 @@ def get_encoding(self) -> str: encoding = mimetype.parameters.get("charset") if encoding: - with contextlib.suppress(LookupError): + with contextlib.suppress(LookupError, ValueError): return codecs.lookup(encoding).name if mimetype.type == "application" and ( diff --git a/tests/test_client_response.py b/tests/test_client_response.py index 628e3d71b92..ede3950a755 100644 --- a/tests/test_client_response.py +++ b/tests/test_client_response.py @@ -6,7 +6,7 @@ from unittest import mock import pytest -from multidict import CIMultiDict +from multidict import CIMultiDict, CIMultiDictProxy from yarl import URL import aiohttp @@ -423,6 +423,36 @@ def side_effect(*args, **kwargs): assert response._connection is None +async def test_text_badly_encoded_encoding_header(loop, session) -> None: + session._resolve_charset = lambda *_: "utf-8" + response = ClientResponse( + "get", + URL("http://def-cl-resp.org"), + request_info=mock.Mock(), + writer=WriterMock(), + continue100=None, + timer=TimerNoop(), + traces=[], + loop=loop, + session=session, + ) + + def side_effect(*args: object, **kwargs: object): + fut = loop.create_future() + fut.set_result(b"foo") + return fut + + h = {"Content-Type": "text/html; charset=\udc81gutf-8\udc81\udc8d"} + response._headers = CIMultiDictProxy(CIMultiDict(h)) + content = response.content = mock.Mock() + content.read.side_effect = side_effect + + await response.read() + encoding = response.get_encoding() + + assert encoding == "utf-8" + + async def test_text_custom_encoding(loop, session) -> None: response = ClientResponse( "get", From b15c89558f1a52eb22ca400ff8624def096123d0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Peeters Date: Wed, 18 Sep 2024 14:23:09 +0200 Subject: [PATCH 223/296] [PR #9160 backport][3.10] Fix badly encoded charset crashing instead of falling back to detector (#9181) Backport of #9160 to 3.10 --- CHANGES/9160.bugfix | 1 + CONTRIBUTORS.txt | 1 + aiohttp/client_reqrep.py | 2 +- tests/test_client_response.py | 32 +++++++++++++++++++++++++++++++- 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 CHANGES/9160.bugfix diff --git a/CHANGES/9160.bugfix b/CHANGES/9160.bugfix new file mode 100644 index 00000000000..253cfd07d50 --- /dev/null +++ b/CHANGES/9160.bugfix @@ -0,0 +1 @@ +Fixed badly encoded charset crashing when getting response text instead of falling back to charset detector. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index e7214dfedd4..c318f7cc669 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -271,6 +271,7 @@ Pawel Kowalski Pawel Miech Pepe Osca Philipp A. +Pierre-Louis Peeters Pieter van Beek Qiao Han Rafael Viotti diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 93e7b59a8a1..57f3323a60c 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -1168,7 +1168,7 @@ def get_encoding(self) -> str: encoding = mimetype.parameters.get("charset") if encoding: - with contextlib.suppress(LookupError): + with contextlib.suppress(LookupError, ValueError): return codecs.lookup(encoding).name if mimetype.type == "application" and ( diff --git a/tests/test_client_response.py b/tests/test_client_response.py index 628e3d71b92..ede3950a755 100644 --- a/tests/test_client_response.py +++ b/tests/test_client_response.py @@ -6,7 +6,7 @@ from unittest import mock import pytest -from multidict import CIMultiDict +from multidict import CIMultiDict, CIMultiDictProxy from yarl import URL import aiohttp @@ -423,6 +423,36 @@ def side_effect(*args, **kwargs): assert response._connection is None +async def test_text_badly_encoded_encoding_header(loop, session) -> None: + session._resolve_charset = lambda *_: "utf-8" + response = ClientResponse( + "get", + URL("http://def-cl-resp.org"), + request_info=mock.Mock(), + writer=WriterMock(), + continue100=None, + timer=TimerNoop(), + traces=[], + loop=loop, + session=session, + ) + + def side_effect(*args: object, **kwargs: object): + fut = loop.create_future() + fut.set_result(b"foo") + return fut + + h = {"Content-Type": "text/html; charset=\udc81gutf-8\udc81\udc8d"} + response._headers = CIMultiDictProxy(CIMultiDict(h)) + content = response.content = mock.Mock() + content.read.side_effect = side_effect + + await response.read() + encoding = response.get_encoding() + + assert encoding == "utf-8" + + async def test_text_custom_encoding(loop, session) -> None: response = ClientResponse( "get", From 71c47e45ce808366ad9d49efb6c6456b31a0db6d Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:41:57 +0000 Subject: [PATCH 224/296] [PR #9171/0462ae6b backport][3.11] Switch to using `yarl.URL.absolute` over `yarl.URL.is_absolute()` (#9185) Co-authored-by: J. Nick Koston --- CHANGES/9171.misc.rst | 3 +++ aiohttp/client.py | 2 +- aiohttp/test_utils.py | 2 +- aiohttp/web_request.py | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 CHANGES/9171.misc.rst diff --git a/CHANGES/9171.misc.rst b/CHANGES/9171.misc.rst new file mode 100644 index 00000000000..c6742edd891 --- /dev/null +++ b/CHANGES/9171.misc.rst @@ -0,0 +1,3 @@ +Improved performance of determining if a URL is absolute -- by :user:`bdraco`. + +The property :attr:`~yarl.URL.absolute` is more performant than the method ``URL.is_absolute()`` and preferred when newer versions of yarl are used. diff --git a/aiohttp/client.py b/aiohttp/client.py index edf4090832f..d59d03fa5ec 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -447,7 +447,7 @@ def _build_url(self, str_or_url: StrOrURL) -> URL: if self._base_url is None: return url else: - assert not url.is_absolute() and url.path.startswith("/") + assert not url.absolute and url.path.startswith("/") return self._base_url.join(url) async def _request( diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py index 08ce5bff9e1..5ab3381f9e6 100644 --- a/aiohttp/test_utils.py +++ b/aiohttp/test_utils.py @@ -148,7 +148,7 @@ def make_url(self, path: StrOrURL) -> URL: assert self._root is not None url = URL(path) if not self.skip_url_asserts: - assert not url.is_absolute() + assert not url.absolute return self._root.join(url) else: return URL(str(self._root) + str(path)) diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py index eca5063e30e..f3521153603 100644 --- a/aiohttp/web_request.py +++ b/aiohttp/web_request.py @@ -174,7 +174,7 @@ def __init__( self._version = message.version self._cache: Dict[str, Any] = {} url = message.url - if url.is_absolute(): + if url.absolute: if scheme is not None: url = url.with_scheme(scheme) if host is not None: From df0f28ba8ee89c2856308ccb0e0a56a165c6186d Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:18:08 +0000 Subject: [PATCH 225/296] [PR #9173/d3c3c6a7 backport][3.10] Avoid calling response prepare hook if its empty (#9188) Co-authored-by: J. Nick Koston --- CHANGES/9173.misc.rst | 1 + aiohttp/web_request.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 CHANGES/9173.misc.rst diff --git a/CHANGES/9173.misc.rst b/CHANGES/9173.misc.rst new file mode 100644 index 00000000000..6fcc098747f --- /dev/null +++ b/CHANGES/9173.misc.rst @@ -0,0 +1 @@ +Improved performance of starting web requests when there is no response prepare hook -- by :user:`bdraco`. diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py index f233afbbd44..eca71e4413a 100644 --- a/aiohttp/web_request.py +++ b/aiohttp/web_request.py @@ -915,4 +915,5 @@ async def _prepare_hook(self, response: StreamResponse) -> None: if match_info is None: return for app in match_info._apps: - await app.on_response_prepare.send(self, response) + if on_response_prepare := app.on_response_prepare: + await on_response_prepare.send(self, response) From 45dd34cde391c62559eeb2770630d899a1e07cf5 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:18:18 +0000 Subject: [PATCH 226/296] [PR #9173/d3c3c6a7 backport][3.11] Avoid calling response prepare hook if its empty (#9189) Co-authored-by: J. Nick Koston --- CHANGES/9173.misc.rst | 1 + aiohttp/web_request.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 CHANGES/9173.misc.rst diff --git a/CHANGES/9173.misc.rst b/CHANGES/9173.misc.rst new file mode 100644 index 00000000000..6fcc098747f --- /dev/null +++ b/CHANGES/9173.misc.rst @@ -0,0 +1 @@ +Improved performance of starting web requests when there is no response prepare hook -- by :user:`bdraco`. diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py index f3521153603..f7e511fa477 100644 --- a/aiohttp/web_request.py +++ b/aiohttp/web_request.py @@ -915,4 +915,5 @@ async def _prepare_hook(self, response: StreamResponse) -> None: if match_info is None: return for app in match_info._apps: - await app.on_response_prepare.send(self, response) + if on_response_prepare := app.on_response_prepare: + await on_response_prepare.send(self, response) From 31c0753dc77806585029c504d349df791211bf63 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 18 Sep 2024 16:26:42 +0200 Subject: [PATCH 227/296] [PR #9170/eacf2e0 backport][3.10] Move reversing slice of middleware apps into the cache (#9186) --- CHANGES/9170.misc.rst | 1 + aiohttp/web_app.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 120000 CHANGES/9170.misc.rst diff --git a/CHANGES/9170.misc.rst b/CHANGES/9170.misc.rst new file mode 120000 index 00000000000..e41cbad0125 --- /dev/null +++ b/CHANGES/9170.misc.rst @@ -0,0 +1 @@ +9158.misc.rst \ No newline at end of file diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index b8768064507..c4199b12271 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -84,7 +84,7 @@ def _build_middlewares( handler: Handler, apps: Tuple["Application", ...] ) -> Callable[[Request], Awaitable[StreamResponse]]: """Apply middlewares to handler.""" - for app in apps: + for app in apps[::-1]: for m, _ in app._middlewares_handlers: # type: ignore[union-attr] handler = update_wrapper(partial(m, handler=handler), handler) # type: ignore[misc] return handler @@ -545,7 +545,7 @@ async def _handle(self, request: Request) -> StreamResponse: if self._run_middlewares: if not self._has_legacy_middlewares: - handler = _build_middlewares(handler, match_info.apps[::-1]) + handler = _build_middlewares(handler, match_info.apps) else: for app in match_info.apps[::-1]: for m, new_style in app._middlewares_handlers: # type: ignore[union-attr] From 2770e6181bd7779a15273e3b0cca88b9e46424fa Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 15:08:47 +0000 Subject: [PATCH 228/296] [PR #9174/98b363e4 backport][3.10] Add a cache to must_be_empty_body (#9190) Co-authored-by: J. Nick Koston --- CHANGES/9174.misc.rst | 1 + aiohttp/helpers.py | 1 + 2 files changed, 2 insertions(+) create mode 100644 CHANGES/9174.misc.rst diff --git a/CHANGES/9174.misc.rst b/CHANGES/9174.misc.rst new file mode 100644 index 00000000000..13dc00ec1de --- /dev/null +++ b/CHANGES/9174.misc.rst @@ -0,0 +1 @@ +Improved performance of web requests -- by :user:`bdraco`. diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index 6abbe74d8cf..40705b16d71 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -973,6 +973,7 @@ def parse_http_date(date_str: Optional[str]) -> Optional[datetime.datetime]: return None +@functools.lru_cache def must_be_empty_body(method: str, code: int) -> bool: """Check if a request must return an empty body.""" return ( From 8d7a5ca6d1d825e286301ad89bd6f4b1324e36ed Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 15:35:45 +0000 Subject: [PATCH 229/296] [PR #9174/98b363e4 backport][3.11] Add a cache to must_be_empty_body (#9191) Co-authored-by: J. Nick Koston --- CHANGES/9174.misc.rst | 1 + aiohttp/helpers.py | 1 + 2 files changed, 2 insertions(+) create mode 100644 CHANGES/9174.misc.rst diff --git a/CHANGES/9174.misc.rst b/CHANGES/9174.misc.rst new file mode 100644 index 00000000000..13dc00ec1de --- /dev/null +++ b/CHANGES/9174.misc.rst @@ -0,0 +1 @@ +Improved performance of web requests -- by :user:`bdraco`. diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index 88fc7412ea8..f5540a19662 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -969,6 +969,7 @@ def parse_http_date(date_str: Optional[str]) -> Optional[datetime.datetime]: return None +@functools.lru_cache def must_be_empty_body(method: str, code: int) -> bool: """Check if a request must return an empty body.""" return ( From c717b25d100e6e727800ed1b498fd127b495aaba Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:21:18 +0000 Subject: [PATCH 230/296] [PR #9172/b93ef57c backport][3.10] Improve performance of starting web requests when content length is not set (#9192) Co-authored-by: J. Nick Koston --- CHANGES/9172.misc.rst | 1 + aiohttp/web_response.py | 6 +++--- tests/test_web_response.py | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) create mode 120000 CHANGES/9172.misc.rst diff --git a/CHANGES/9172.misc.rst b/CHANGES/9172.misc.rst new file mode 120000 index 00000000000..d6a2f2aaaab --- /dev/null +++ b/CHANGES/9172.misc.rst @@ -0,0 +1 @@ +9174.misc.rst \ No newline at end of file diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index f583789d82e..71a94eec248 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -742,10 +742,10 @@ async def write_eof(self, data: bytes = b"") -> None: await super().write_eof() async def _start(self, request: "BaseRequest") -> AbstractStreamWriter: - if should_remove_content_length(request.method, self.status): - if hdrs.CONTENT_LENGTH in self._headers: + if hdrs.CONTENT_LENGTH in self._headers: + if should_remove_content_length(request.method, self.status): del self._headers[hdrs.CONTENT_LENGTH] - elif not self._chunked and hdrs.CONTENT_LENGTH not in self._headers: + elif not self._chunked: if isinstance(self._body, Payload): if self._body.size is not None: self._headers[hdrs.CONTENT_LENGTH] = str(self._body.size) diff --git a/tests/test_web_response.py b/tests/test_web_response.py index 2e1e332e0a5..36642d3d244 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -666,6 +666,22 @@ async def write_headers(status_line, headers): assert resp.content_length is None +async def test_rm_content_length_if_204() -> None: + """Ensure content-length is removed for 204 responses.""" + writer = mock.create_autospec(StreamWriter, spec_set=True, instance=True) + + async def write_headers(status_line, headers): + assert hdrs.CONTENT_LENGTH not in headers + + writer.write_headers.side_effect = write_headers + req = make_request("GET", "/", writer=writer) + payload = BytesPayload(b"answer", headers={"Content-Length": "6"}) + resp = Response(body=payload, status=204) + resp.body = payload + await resp.prepare(req) + assert resp.content_length is None + + @pytest.mark.parametrize("status", (100, 101, 204, 304)) async def test_rm_transfer_encoding_rfc_9112_6_3_http_11(status: int) -> None: """Remove transfer encoding for RFC 9112 sec 6.3 with HTTP/1.1.""" From fa1307d7239b68d7c13fa9dc3bb6bda17e59b6eb Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:27:26 +0000 Subject: [PATCH 231/296] [PR #9172/b93ef57c backport][3.11] Improve performance of starting web requests when content length is not set (#9193) Co-authored-by: J. Nick Koston --- CHANGES/9172.misc.rst | 1 + aiohttp/web_response.py | 6 +++--- tests/test_web_response.py | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) create mode 120000 CHANGES/9172.misc.rst diff --git a/CHANGES/9172.misc.rst b/CHANGES/9172.misc.rst new file mode 120000 index 00000000000..d6a2f2aaaab --- /dev/null +++ b/CHANGES/9172.misc.rst @@ -0,0 +1 @@ +9174.misc.rst \ No newline at end of file diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index d4f18271a83..2ba135f54d2 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -759,10 +759,10 @@ async def write_eof(self, data: bytes = b"") -> None: await super().write_eof() async def _start(self, request: "BaseRequest") -> AbstractStreamWriter: - if should_remove_content_length(request.method, self.status): - if hdrs.CONTENT_LENGTH in self._headers: + if hdrs.CONTENT_LENGTH in self._headers: + if should_remove_content_length(request.method, self.status): del self._headers[hdrs.CONTENT_LENGTH] - elif not self._chunked and hdrs.CONTENT_LENGTH not in self._headers: + elif not self._chunked: if isinstance(self._body, Payload): if self._body.size is not None: self._headers[hdrs.CONTENT_LENGTH] = str(self._body.size) diff --git a/tests/test_web_response.py b/tests/test_web_response.py index b71730868e4..264ca5e93ee 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -667,6 +667,22 @@ async def write_headers(status_line, headers): assert resp.content_length is None +async def test_rm_content_length_if_204() -> None: + """Ensure content-length is removed for 204 responses.""" + writer = mock.create_autospec(StreamWriter, spec_set=True, instance=True) + + async def write_headers(status_line, headers): + assert hdrs.CONTENT_LENGTH not in headers + + writer.write_headers.side_effect = write_headers + req = make_request("GET", "/", writer=writer) + payload = BytesPayload(b"answer", headers={"Content-Length": "6"}) + resp = Response(body=payload, status=204) + resp.body = payload + await resp.prepare(req) + assert resp.content_length is None + + @pytest.mark.parametrize("status", (100, 101, 204, 304)) async def test_rm_transfer_encoding_rfc_9112_6_3_http_11(status: int) -> None: """Remove transfer encoding for RFC 9112 sec 6.3 with HTTP/1.1.""" From eb685564f339679d719eb748dc615fdb0d97f604 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Wed, 18 Sep 2024 18:28:19 +0100 Subject: [PATCH 232/296] Add and use ClientConnectionResetError (#9137) (#9194) (cherry picked from commit f95bcaf4e0b2344d09df8dbb565150dcb4e73c0f) --- CHANGES/9137.bugfix.rst | 2 ++ aiohttp/__init__.py | 2 ++ aiohttp/base_protocol.py | 3 ++- aiohttp/client.py | 2 ++ aiohttp/client_exceptions.py | 9 +++++++-- aiohttp/http_websocket.py | 5 +++-- aiohttp/http_writer.py | 3 ++- docs/client_reference.rst | 6 ++++++ tests/test_client_ws.py | 15 ++++++++++----- tests/test_client_ws_functional.py | 4 ++-- tests/test_http_writer.py | 10 ++++++---- 11 files changed, 44 insertions(+), 17 deletions(-) create mode 100644 CHANGES/9137.bugfix.rst diff --git a/CHANGES/9137.bugfix.rst b/CHANGES/9137.bugfix.rst new file mode 100644 index 00000000000..d99802095bd --- /dev/null +++ b/CHANGES/9137.bugfix.rst @@ -0,0 +1,2 @@ +Added :exc:`aiohttp.ClientConnectionResetError`. Client code that previously threw :exc:`ConnectionResetError` +will now throw this -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py index 15602a7dc85..c5f13c6dc49 100644 --- a/aiohttp/__init__.py +++ b/aiohttp/__init__.py @@ -6,6 +6,7 @@ from .client import ( BaseConnector, ClientConnectionError, + ClientConnectionResetError, ClientConnectorCertificateError, ClientConnectorError, ClientConnectorSSLError, @@ -125,6 +126,7 @@ # client "BaseConnector", "ClientConnectionError", + "ClientConnectionResetError", "ClientConnectorCertificateError", "ClientConnectorError", "ClientConnectorSSLError", diff --git a/aiohttp/base_protocol.py b/aiohttp/base_protocol.py index dc1f24f99cd..2fc2fa65885 100644 --- a/aiohttp/base_protocol.py +++ b/aiohttp/base_protocol.py @@ -1,6 +1,7 @@ import asyncio from typing import Optional, cast +from .client_exceptions import ClientConnectionResetError from .helpers import set_exception from .tcp_helpers import tcp_nodelay @@ -85,7 +86,7 @@ def connection_lost(self, exc: Optional[BaseException]) -> None: async def _drain_helper(self) -> None: if not self.connected: - raise ConnectionResetError("Connection lost") + raise ClientConnectionResetError("Connection lost") if not self._paused: return waiter = self._drain_waiter diff --git a/aiohttp/client.py b/aiohttp/client.py index d59d03fa5ec..443335c6061 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -40,6 +40,7 @@ from .abc import AbstractCookieJar from .client_exceptions import ( ClientConnectionError, + ClientConnectionResetError, ClientConnectorCertificateError, ClientConnectorError, ClientConnectorSSLError, @@ -106,6 +107,7 @@ __all__ = ( # client_exceptions "ClientConnectionError", + "ClientConnectionResetError", "ClientConnectorCertificateError", "ClientConnectorError", "ClientConnectorSSLError", diff --git a/aiohttp/client_exceptions.py b/aiohttp/client_exceptions.py index 36bb6d1c0d8..94991c42477 100644 --- a/aiohttp/client_exceptions.py +++ b/aiohttp/client_exceptions.py @@ -6,7 +6,6 @@ from multidict import MultiMapping -from .http_parser import RawResponseMessage from .typedefs import StrOrURL try: @@ -19,12 +18,14 @@ if TYPE_CHECKING: from .client_reqrep import ClientResponse, ConnectionKey, Fingerprint, RequestInfo + from .http_parser import RawResponseMessage else: - RequestInfo = ClientResponse = ConnectionKey = None + RequestInfo = ClientResponse = ConnectionKey = RawResponseMessage = None __all__ = ( "ClientError", "ClientConnectionError", + "ClientConnectionResetError", "ClientOSError", "ClientConnectorError", "ClientProxyConnectionError", @@ -159,6 +160,10 @@ class ClientConnectionError(ClientError): """Base class for client socket errors.""" +class ClientConnectionResetError(ClientConnectionError, ConnectionResetError): + """ConnectionResetError""" + + class ClientOSError(ClientConnectionError, OSError): """OSError error.""" diff --git a/aiohttp/http_websocket.py b/aiohttp/http_websocket.py index 9d03d2773c7..c6521695d94 100644 --- a/aiohttp/http_websocket.py +++ b/aiohttp/http_websocket.py @@ -25,6 +25,7 @@ ) from .base_protocol import BaseProtocol +from .client_exceptions import ClientConnectionResetError from .compression_utils import ZLibCompressor, ZLibDecompressor from .helpers import NO_EXTENSIONS, set_exception from .streams import DataQueue @@ -624,7 +625,7 @@ async def _send_frame( ) -> None: """Send a frame over the websocket with message as its payload.""" if self._closing and not (opcode & WSMsgType.CLOSE): - raise ConnectionResetError("Cannot write to closing transport") + raise ClientConnectionResetError("Cannot write to closing transport") # RSV are the reserved bits in the frame header. They are used to # indicate that the frame is using an extension. @@ -719,7 +720,7 @@ def _make_compress_obj(self, compress: int) -> ZLibCompressor: def _write(self, data: bytes) -> None: if self.transport is None or self.transport.is_closing(): - raise ConnectionResetError("Cannot write to closing transport") + raise ClientConnectionResetError("Cannot write to closing transport") self.transport.write(data) async def pong(self, message: Union[bytes, str] = b"") -> None: diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index d6b02e6f566..f54fa0f0774 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -8,6 +8,7 @@ from .abc import AbstractStreamWriter from .base_protocol import BaseProtocol +from .client_exceptions import ClientConnectionResetError from .compression_utils import ZLibCompressor from .helpers import NO_EXTENSIONS @@ -72,7 +73,7 @@ def _write(self, chunk: bytes) -> None: self.output_size += size transport = self.transport if not self._protocol.connected or transport is None or transport.is_closing(): - raise ConnectionResetError("Cannot write to closing transport") + raise ClientConnectionResetError("Cannot write to closing transport") transport.write(chunk) async def write( diff --git a/docs/client_reference.rst b/docs/client_reference.rst index a16443f275e..7f88fda14c9 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -2225,6 +2225,10 @@ Connection errors Derived from :exc:`ClientError` +.. class:: ClientConnectionResetError + + Derived from :exc:`ClientConnectionError` and :exc:`ConnectionResetError` + .. class:: ClientOSError Subset of connection errors that are initiated by an :exc:`OSError` @@ -2311,6 +2315,8 @@ Hierarchy of exceptions * :exc:`ClientConnectionError` + * :exc:`ClientConnectionResetError` + * :exc:`ClientOSError` * :exc:`ClientConnectorError` diff --git a/tests/test_client_ws.py b/tests/test_client_ws.py index 31ec7576c97..afe7983648f 100644 --- a/tests/test_client_ws.py +++ b/tests/test_client_ws.py @@ -2,14 +2,13 @@ import base64 import hashlib import os -from typing import Any +from typing import Any, Type from unittest import mock import pytest import aiohttp -from aiohttp import client, hdrs -from aiohttp.client_exceptions import ServerDisconnectedError +from aiohttp import ClientConnectionResetError, ServerDisconnectedError, client, hdrs from aiohttp.http import WS_KEY from aiohttp.streams import EofStream from aiohttp.test_utils import make_mocked_coro @@ -508,7 +507,13 @@ async def test_close_exc2(loop, ws_key, key_data) -> None: await resp.close() -async def test_send_data_after_close(ws_key, key_data, loop) -> None: +@pytest.mark.parametrize("exc", (ClientConnectionResetError, ConnectionResetError)) +async def test_send_data_after_close( + exc: Type[Exception], + ws_key: bytes, + key_data: bytes, + loop: asyncio.AbstractEventLoop, +) -> None: resp = mock.Mock() resp.status = 101 resp.headers = { @@ -533,7 +538,7 @@ async def test_send_data_after_close(ws_key, key_data, loop) -> None: (resp.send_bytes, (b"b",)), (resp.send_json, ({},)), ): - with pytest.raises(ConnectionResetError): + with pytest.raises(exc): # Verify exc can be caught with both classes await meth(*args) diff --git a/tests/test_client_ws_functional.py b/tests/test_client_ws_functional.py index 30da0dca802..0a8008f07ca 100644 --- a/tests/test_client_ws_functional.py +++ b/tests/test_client_ws_functional.py @@ -6,7 +6,7 @@ import pytest import aiohttp -from aiohttp import ServerTimeoutError, WSMsgType, hdrs, web +from aiohttp import ClientConnectionResetError, ServerTimeoutError, WSMsgType, hdrs, web from aiohttp.client_ws import ClientWSTimeout from aiohttp.http import WSCloseCode from aiohttp.pytest_plugin import AiohttpClient @@ -681,7 +681,7 @@ async def handler(request: web.Request) -> NoReturn: # would cancel the heartbeat task and we wouldn't get a ping assert resp._conn is not None with mock.patch.object( - resp._conn.transport, "write", side_effect=ConnectionResetError + resp._conn.transport, "write", side_effect=ClientConnectionResetError ), mock.patch.object(resp._writer, "ping", wraps=resp._writer.ping) as ping: await resp.receive() ping_count = ping.call_count diff --git a/tests/test_http_writer.py b/tests/test_http_writer.py index db50ad65f67..ed853c8744a 100644 --- a/tests/test_http_writer.py +++ b/tests/test_http_writer.py @@ -5,7 +5,7 @@ import pytest from multidict import CIMultiDict -from aiohttp import http +from aiohttp import ClientConnectionResetError, http from aiohttp.test_utils import make_mocked_coro @@ -232,12 +232,12 @@ async def test_write_to_closing_transport(protocol, transport, loop) -> None: await msg.write(b"Before closing") transport.is_closing.return_value = True - with pytest.raises(ConnectionResetError): + with pytest.raises(ClientConnectionResetError): await msg.write(b"After closing") async def test_write_to_closed_transport(protocol, transport, loop) -> None: - """Test that writing to a closed transport raises ConnectionResetError. + """Test that writing to a closed transport raises ClientConnectionResetError. The StreamWriter checks to see if protocol.transport is None before writing to the transport. If it is None, it raises ConnectionResetError. @@ -247,7 +247,9 @@ async def test_write_to_closed_transport(protocol, transport, loop) -> None: await msg.write(b"Before transport close") protocol.transport = None - with pytest.raises(ConnectionResetError, match="Cannot write to closing transport"): + with pytest.raises( + ClientConnectionResetError, match="Cannot write to closing transport" + ): await msg.write(b"After transport closed") From 1856c5995e507e35e807469d01191e571bc329c0 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Wed, 18 Sep 2024 18:28:31 +0100 Subject: [PATCH 233/296] Add and use ClientConnectionResetError (#9137) (#9195) (cherry picked from commit f95bcaf4e0b2344d09df8dbb565150dcb4e73c0f) --- CHANGES/9137.bugfix.rst | 2 ++ aiohttp/__init__.py | 2 ++ aiohttp/base_protocol.py | 3 ++- aiohttp/client.py | 2 ++ aiohttp/client_exceptions.py | 9 +++++++-- aiohttp/http_websocket.py | 5 +++-- aiohttp/http_writer.py | 3 ++- docs/client_reference.rst | 6 ++++++ tests/test_client_ws.py | 15 ++++++++++----- tests/test_client_ws_functional.py | 4 ++-- tests/test_http_writer.py | 10 ++++++---- 11 files changed, 44 insertions(+), 17 deletions(-) create mode 100644 CHANGES/9137.bugfix.rst diff --git a/CHANGES/9137.bugfix.rst b/CHANGES/9137.bugfix.rst new file mode 100644 index 00000000000..d99802095bd --- /dev/null +++ b/CHANGES/9137.bugfix.rst @@ -0,0 +1,2 @@ +Added :exc:`aiohttp.ClientConnectionResetError`. Client code that previously threw :exc:`ConnectionResetError` +will now throw this -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py index f321cdaba45..63367052646 100644 --- a/aiohttp/__init__.py +++ b/aiohttp/__init__.py @@ -6,6 +6,7 @@ from .client import ( BaseConnector, ClientConnectionError, + ClientConnectionResetError, ClientConnectorCertificateError, ClientConnectorError, ClientConnectorSSLError, @@ -124,6 +125,7 @@ # client "BaseConnector", "ClientConnectionError", + "ClientConnectionResetError", "ClientConnectorCertificateError", "ClientConnectorError", "ClientConnectorSSLError", diff --git a/aiohttp/base_protocol.py b/aiohttp/base_protocol.py index dc1f24f99cd..2fc2fa65885 100644 --- a/aiohttp/base_protocol.py +++ b/aiohttp/base_protocol.py @@ -1,6 +1,7 @@ import asyncio from typing import Optional, cast +from .client_exceptions import ClientConnectionResetError from .helpers import set_exception from .tcp_helpers import tcp_nodelay @@ -85,7 +86,7 @@ def connection_lost(self, exc: Optional[BaseException]) -> None: async def _drain_helper(self) -> None: if not self.connected: - raise ConnectionResetError("Connection lost") + raise ClientConnectionResetError("Connection lost") if not self._paused: return waiter = self._drain_waiter diff --git a/aiohttp/client.py b/aiohttp/client.py index 5f9e95f4706..61bea70aa9b 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -40,6 +40,7 @@ from .abc import AbstractCookieJar from .client_exceptions import ( ClientConnectionError, + ClientConnectionResetError, ClientConnectorCertificateError, ClientConnectorError, ClientConnectorSSLError, @@ -102,6 +103,7 @@ __all__ = ( # client_exceptions "ClientConnectionError", + "ClientConnectionResetError", "ClientConnectorCertificateError", "ClientConnectorError", "ClientConnectorSSLError", diff --git a/aiohttp/client_exceptions.py b/aiohttp/client_exceptions.py index 36bb6d1c0d8..94991c42477 100644 --- a/aiohttp/client_exceptions.py +++ b/aiohttp/client_exceptions.py @@ -6,7 +6,6 @@ from multidict import MultiMapping -from .http_parser import RawResponseMessage from .typedefs import StrOrURL try: @@ -19,12 +18,14 @@ if TYPE_CHECKING: from .client_reqrep import ClientResponse, ConnectionKey, Fingerprint, RequestInfo + from .http_parser import RawResponseMessage else: - RequestInfo = ClientResponse = ConnectionKey = None + RequestInfo = ClientResponse = ConnectionKey = RawResponseMessage = None __all__ = ( "ClientError", "ClientConnectionError", + "ClientConnectionResetError", "ClientOSError", "ClientConnectorError", "ClientProxyConnectionError", @@ -159,6 +160,10 @@ class ClientConnectionError(ClientError): """Base class for client socket errors.""" +class ClientConnectionResetError(ClientConnectionError, ConnectionResetError): + """ConnectionResetError""" + + class ClientOSError(ClientConnectionError, OSError): """OSError error.""" diff --git a/aiohttp/http_websocket.py b/aiohttp/http_websocket.py index 2ea2c9191e1..fb00ebc7d35 100644 --- a/aiohttp/http_websocket.py +++ b/aiohttp/http_websocket.py @@ -25,6 +25,7 @@ ) from .base_protocol import BaseProtocol +from .client_exceptions import ClientConnectionResetError from .compression_utils import ZLibCompressor, ZLibDecompressor from .helpers import NO_EXTENSIONS, set_exception from .streams import DataQueue @@ -624,7 +625,7 @@ async def _send_frame( ) -> None: """Send a frame over the websocket with message as its payload.""" if self._closing and not (opcode & WSMsgType.CLOSE): - raise ConnectionResetError("Cannot write to closing transport") + raise ClientConnectionResetError("Cannot write to closing transport") # RSV are the reserved bits in the frame header. They are used to # indicate that the frame is using an extension. @@ -719,7 +720,7 @@ def _make_compress_obj(self, compress: int) -> ZLibCompressor: def _write(self, data: bytes) -> None: if self.transport is None or self.transport.is_closing(): - raise ConnectionResetError("Cannot write to closing transport") + raise ClientConnectionResetError("Cannot write to closing transport") self.transport.write(data) async def pong(self, message: Union[bytes, str] = b"") -> None: diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index d6b02e6f566..f54fa0f0774 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -8,6 +8,7 @@ from .abc import AbstractStreamWriter from .base_protocol import BaseProtocol +from .client_exceptions import ClientConnectionResetError from .compression_utils import ZLibCompressor from .helpers import NO_EXTENSIONS @@ -72,7 +73,7 @@ def _write(self, chunk: bytes) -> None: self.output_size += size transport = self.transport if not self._protocol.connected or transport is None or transport.is_closing(): - raise ConnectionResetError("Cannot write to closing transport") + raise ClientConnectionResetError("Cannot write to closing transport") transport.write(chunk) async def write( diff --git a/docs/client_reference.rst b/docs/client_reference.rst index 1686aa7c113..7379743ae02 100644 --- a/docs/client_reference.rst +++ b/docs/client_reference.rst @@ -2207,6 +2207,10 @@ Connection errors Derived from :exc:`ClientError` +.. class:: ClientConnectionResetError + + Derived from :exc:`ClientConnectionError` and :exc:`ConnectionResetError` + .. class:: ClientOSError Subset of connection errors that are initiated by an :exc:`OSError` @@ -2293,6 +2297,8 @@ Hierarchy of exceptions * :exc:`ClientConnectionError` + * :exc:`ClientConnectionResetError` + * :exc:`ClientOSError` * :exc:`ClientConnectorError` diff --git a/tests/test_client_ws.py b/tests/test_client_ws.py index a790fba43ec..ec08db01e4c 100644 --- a/tests/test_client_ws.py +++ b/tests/test_client_ws.py @@ -2,14 +2,13 @@ import base64 import hashlib import os -from typing import Any +from typing import Any, Type from unittest import mock import pytest import aiohttp -from aiohttp import client, hdrs -from aiohttp.client_exceptions import ServerDisconnectedError +from aiohttp import ClientConnectionResetError, ServerDisconnectedError, client, hdrs from aiohttp.http import WS_KEY from aiohttp.streams import EofStream from aiohttp.test_utils import make_mocked_coro @@ -508,7 +507,13 @@ async def test_close_exc2(loop, ws_key, key_data) -> None: await resp.close() -async def test_send_data_after_close(ws_key, key_data, loop) -> None: +@pytest.mark.parametrize("exc", (ClientConnectionResetError, ConnectionResetError)) +async def test_send_data_after_close( + exc: Type[Exception], + ws_key: bytes, + key_data: bytes, + loop: asyncio.AbstractEventLoop, +) -> None: resp = mock.Mock() resp.status = 101 resp.headers = { @@ -533,7 +538,7 @@ async def test_send_data_after_close(ws_key, key_data, loop) -> None: (resp.send_bytes, (b"b",)), (resp.send_json, ({},)), ): - with pytest.raises(ConnectionResetError): + with pytest.raises(exc): # Verify exc can be caught with both classes await meth(*args) diff --git a/tests/test_client_ws_functional.py b/tests/test_client_ws_functional.py index 274092a189a..0421fb9616b 100644 --- a/tests/test_client_ws_functional.py +++ b/tests/test_client_ws_functional.py @@ -6,7 +6,7 @@ import pytest import aiohttp -from aiohttp import ServerTimeoutError, WSMsgType, hdrs, web +from aiohttp import ClientConnectionResetError, ServerTimeoutError, WSMsgType, hdrs, web from aiohttp.http import WSCloseCode from aiohttp.pytest_plugin import AiohttpClient @@ -620,7 +620,7 @@ async def handler(request: web.Request) -> NoReturn: # would cancel the heartbeat task and we wouldn't get a ping assert resp._conn is not None with mock.patch.object( - resp._conn.transport, "write", side_effect=ConnectionResetError + resp._conn.transport, "write", side_effect=ClientConnectionResetError ), mock.patch.object(resp._writer, "ping", wraps=resp._writer.ping) as ping: await resp.receive() ping_count = ping.call_count diff --git a/tests/test_http_writer.py b/tests/test_http_writer.py index 5649f32f792..82ad07d046f 100644 --- a/tests/test_http_writer.py +++ b/tests/test_http_writer.py @@ -5,7 +5,7 @@ import pytest from multidict import CIMultiDict -from aiohttp import http +from aiohttp import ClientConnectionResetError, http from aiohttp.test_utils import make_mocked_coro @@ -232,12 +232,12 @@ async def test_write_to_closing_transport(protocol, transport, loop) -> None: await msg.write(b"Before closing") transport.is_closing.return_value = True - with pytest.raises(ConnectionResetError): + with pytest.raises(ClientConnectionResetError): await msg.write(b"After closing") async def test_write_to_closed_transport(protocol, transport, loop) -> None: - """Test that writing to a closed transport raises ConnectionResetError. + """Test that writing to a closed transport raises ClientConnectionResetError. The StreamWriter checks to see if protocol.transport is None before writing to the transport. If it is None, it raises ConnectionResetError. @@ -247,7 +247,9 @@ async def test_write_to_closed_transport(protocol, transport, loop) -> None: await msg.write(b"Before transport close") protocol.transport = None - with pytest.raises(ConnectionResetError, match="Cannot write to closing transport"): + with pytest.raises( + ClientConnectionResetError, match="Cannot write to closing transport" + ): await msg.write(b"After transport closed") From 8e4678a69e365f63b237753b1042962bec4b922e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 18 Sep 2024 21:07:33 +0200 Subject: [PATCH 234/296] [PR #9170/eacf2e0 backport][3.11] Move reversing slice of middleware apps into the cache (#9187) --- CHANGES/9170.misc.rst | 1 + aiohttp/web_app.py | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) create mode 120000 CHANGES/9170.misc.rst diff --git a/CHANGES/9170.misc.rst b/CHANGES/9170.misc.rst new file mode 120000 index 00000000000..e41cbad0125 --- /dev/null +++ b/CHANGES/9170.misc.rst @@ -0,0 +1 @@ +9158.misc.rst \ No newline at end of file diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index 8d109f793ca..b59d0d1b0ff 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -1,7 +1,7 @@ import asyncio import logging import warnings -from functools import lru_cache, partial, update_wrapper +from functools import cache, partial, update_wrapper from typing import ( TYPE_CHECKING, Any, @@ -79,12 +79,12 @@ _Resource = TypeVar("_Resource", bound=AbstractResource) -@lru_cache(None) +@cache def _build_middlewares( handler: Handler, apps: Tuple["Application", ...] ) -> Callable[[Request], Awaitable[StreamResponse]]: """Apply middlewares to handler.""" - for app in apps: + for app in apps[::-1]: for m, _ in app._middlewares_handlers: # type: ignore[union-attr] handler = update_wrapper(partial(m, handler=handler), handler) # type: ignore[misc] return handler @@ -545,7 +545,7 @@ async def _handle(self, request: Request) -> StreamResponse: if self._run_middlewares: if not self._has_legacy_middlewares: - handler = _build_middlewares(handler, match_info.apps[::-1]) + handler = _build_middlewares(handler, match_info.apps) else: for app in match_info.apps[::-1]: for m, new_style in app._middlewares_handlers: # type: ignore[union-attr] From e9609ad14aef19dfd62e29f99c01a25294ff8d6b Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Thu, 19 Sep 2024 13:28:31 +0100 Subject: [PATCH 235/296] Disallow newlines in reason (#9167) (#9198) (cherry picked from commit 88f383427e918360fd2762a9a3256897159e2d6b) --- CHANGES/9167.bugfix.rst | 1 + aiohttp/web_response.py | 2 ++ tests/test_web_exceptions.py | 5 +++++ tests/test_web_response.py | 13 +++++++++---- 4 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 CHANGES/9167.bugfix.rst diff --git a/CHANGES/9167.bugfix.rst b/CHANGES/9167.bugfix.rst new file mode 100644 index 00000000000..4c33c8ad355 --- /dev/null +++ b/CHANGES/9167.bugfix.rst @@ -0,0 +1 @@ +Rejected `\n` in `reason` values to avoid sending broken HTTP messages -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 2ba135f54d2..bf184980700 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -145,6 +145,8 @@ def set_status( reason = HTTPStatus(self._status).phrase except ValueError: reason = "" + if "\n" in reason: + raise ValueError("Reason cannot contain \\n") self._reason = reason @property diff --git a/tests/test_web_exceptions.py b/tests/test_web_exceptions.py index 69deb27a062..3358a947d3d 100644 --- a/tests/test_web_exceptions.py +++ b/tests/test_web_exceptions.py @@ -270,3 +270,8 @@ def test_unicode_text_body_unauthorized() -> None: ): resp = web.HTTPUnauthorized(body="text") assert resp.status == 401 + + +def test_multiline_reason() -> None: + with pytest.raises(ValueError, match=r"Reason cannot contain \\n"): + web.HTTPOk(reason="Bad\r\nInjected-header: foo") diff --git a/tests/test_web_response.py b/tests/test_web_response.py index 264ca5e93ee..ec9522b05a5 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -945,14 +945,14 @@ async def test_start_force_close() -> None: async def test___repr__() -> None: req = make_request("GET", "/path/to") - resp = StreamResponse(reason=301) + resp = StreamResponse(reason="foo") await resp.prepare(req) - assert "" == repr(resp) + assert "" == repr(resp) def test___repr___not_prepared() -> None: - resp = StreamResponse(reason=301) - assert "" == repr(resp) + resp = StreamResponse(reason="foo") + assert "" == repr(resp) async def test_keep_alive_http10_default() -> None: @@ -1226,6 +1226,11 @@ async def test_render_with_body(buf, writer) -> None: ) +async def test_multiline_reason(buf, writer) -> None: + with pytest.raises(ValueError, match=r"Reason cannot contain \\n"): + Response(reason="Bad\r\nInjected-header: foo") + + async def test_send_set_cookie_header(buf, writer) -> None: resp = Response() resp.cookies["name"] = "value" From dd5bb073107caa1c764158b87fb8482124aad6c1 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Thu, 19 Sep 2024 13:28:41 +0100 Subject: [PATCH 236/296] Disallow newlines in reason (#9167) (#9199) (cherry picked from commit 88f383427e918360fd2762a9a3256897159e2d6b) --- CHANGES/9167.bugfix.rst | 1 + aiohttp/web_response.py | 2 ++ tests/test_web_exceptions.py | 5 +++++ tests/test_web_response.py | 13 +++++++++---- 4 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 CHANGES/9167.bugfix.rst diff --git a/CHANGES/9167.bugfix.rst b/CHANGES/9167.bugfix.rst new file mode 100644 index 00000000000..4c33c8ad355 --- /dev/null +++ b/CHANGES/9167.bugfix.rst @@ -0,0 +1 @@ +Rejected `\n` in `reason` values to avoid sending broken HTTP messages -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 71a94eec248..c14a7544d6f 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -143,6 +143,8 @@ def set_status( reason = HTTPStatus(self._status).phrase except ValueError: reason = "" + if "\n" in reason: + raise ValueError("Reason cannot contain \\n") self._reason = reason @property diff --git a/tests/test_web_exceptions.py b/tests/test_web_exceptions.py index 69deb27a062..3358a947d3d 100644 --- a/tests/test_web_exceptions.py +++ b/tests/test_web_exceptions.py @@ -270,3 +270,8 @@ def test_unicode_text_body_unauthorized() -> None: ): resp = web.HTTPUnauthorized(body="text") assert resp.status == 401 + + +def test_multiline_reason() -> None: + with pytest.raises(ValueError, match=r"Reason cannot contain \\n"): + web.HTTPOk(reason="Bad\r\nInjected-header: foo") diff --git a/tests/test_web_response.py b/tests/test_web_response.py index 36642d3d244..3694e65948b 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -944,14 +944,14 @@ async def test_start_force_close() -> None: async def test___repr__() -> None: req = make_request("GET", "/path/to") - resp = StreamResponse(reason=301) + resp = StreamResponse(reason="foo") await resp.prepare(req) - assert "" == repr(resp) + assert "" == repr(resp) def test___repr___not_prepared() -> None: - resp = StreamResponse(reason=301) - assert "" == repr(resp) + resp = StreamResponse(reason="foo") + assert "" == repr(resp) async def test_keep_alive_http10_default() -> None: @@ -1225,6 +1225,11 @@ async def test_render_with_body(buf, writer) -> None: ) +async def test_multiline_reason(buf, writer) -> None: + with pytest.raises(ValueError, match=r"Reason cannot contain \\n"): + Response(reason="Bad\r\nInjected-header: foo") + + async def test_send_set_cookie_header(buf, writer) -> None: resp = Response() resp.cookies["name"] = "value" From 2508faca25c0f952a99aaf9cb316bb9966132bac Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Fri, 20 Sep 2024 14:06:05 +0100 Subject: [PATCH 237/296] Fix lost details on HttpProcessingError (#9052) (#9205) (cherry picked from commit 89114196040e6b4e435799939dfde6141223cc12) --- CHANGES/9052.bugfix.rst | 1 + aiohttp/client_proto.py | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 CHANGES/9052.bugfix.rst diff --git a/CHANGES/9052.bugfix.rst b/CHANGES/9052.bugfix.rst new file mode 100644 index 00000000000..913288d3368 --- /dev/null +++ b/CHANGES/9052.bugfix.rst @@ -0,0 +1 @@ +Fixed exception information getting lost on ``HttpProcessingError`` -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/client_proto.py b/aiohttp/client_proto.py index e612450c746..9230ae5145b 100644 --- a/aiohttp/client_proto.py +++ b/aiohttp/client_proto.py @@ -266,7 +266,15 @@ def data_received(self, data: bytes) -> None: # closed in this case self.transport.close() # should_close is True after the call - self.set_exception(HttpProcessingError(), underlying_exc) + if isinstance(underlying_exc, HttpProcessingError): + exc = HttpProcessingError( + code=underlying_exc.code, + message=underlying_exc.message, + headers=underlying_exc.headers, + ) + else: + exc = HttpProcessingError() + self.set_exception(exc, underlying_exc) return self._upgraded = upgraded From eba4d1c75df4551affc8dd390ba9ff2a3096d6b8 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Fri, 20 Sep 2024 14:19:43 +0100 Subject: [PATCH 238/296] Fix lost details on HttpProcessingError (#9052) (#9206) (cherry picked from commit 89114196040e6b4e435799939dfde6141223cc12) --- CHANGES/9052.bugfix.rst | 1 + aiohttp/client_proto.py | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 CHANGES/9052.bugfix.rst diff --git a/CHANGES/9052.bugfix.rst b/CHANGES/9052.bugfix.rst new file mode 100644 index 00000000000..913288d3368 --- /dev/null +++ b/CHANGES/9052.bugfix.rst @@ -0,0 +1 @@ +Fixed exception information getting lost on ``HttpProcessingError`` -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/client_proto.py b/aiohttp/client_proto.py index f8c83240209..c6c262d3bfe 100644 --- a/aiohttp/client_proto.py +++ b/aiohttp/client_proto.py @@ -269,7 +269,15 @@ def data_received(self, data: bytes) -> None: # closed in this case self.transport.close() # should_close is True after the call - self.set_exception(HttpProcessingError(), underlying_exc) + if isinstance(underlying_exc, HttpProcessingError): + exc = HttpProcessingError( + code=underlying_exc.code, + message=underlying_exc.message, + headers=underlying_exc.headers, + ) + else: + exc = HttpProcessingError() + self.set_exception(exc, underlying_exc) return self._upgraded = upgraded From 1d7b0df336cf1cb35a676f56104c2e48e895ca2a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 21 Sep 2024 11:31:40 +0200 Subject: [PATCH 239/296] =?UTF-8?q?[PR=C2=A0#9203/6e70c0a=20backport][3.10?= =?UTF-8?q?]=20Implement=20heapq=20for=20cookie=20expire=20times=20(#9208)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES/9203.misc.rst | 3 + aiohttp/cookiejar.py | 100 ++++++++++++++++------- tests/test_cookiejar.py | 174 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 246 insertions(+), 31 deletions(-) create mode 100644 CHANGES/9203.misc.rst diff --git a/CHANGES/9203.misc.rst b/CHANGES/9203.misc.rst new file mode 100644 index 00000000000..766fdc01a57 --- /dev/null +++ b/CHANGES/9203.misc.rst @@ -0,0 +1,3 @@ +Significantly improved performance of expiring cookies -- by :user:`bdraco`. + +Expiring cookies has been redesigned to use :mod:`heapq` instead of a linear search, to better scale. diff --git a/aiohttp/cookiejar.py b/aiohttp/cookiejar.py index e9997ce2935..72c431a275c 100644 --- a/aiohttp/cookiejar.py +++ b/aiohttp/cookiejar.py @@ -2,6 +2,7 @@ import calendar import contextlib import datetime +import heapq import itertools import os # noqa import pathlib @@ -10,7 +11,6 @@ import time from collections import defaultdict from http.cookies import BaseCookie, Morsel, SimpleCookie -from math import ceil from typing import ( DefaultDict, Dict, @@ -40,6 +40,11 @@ _FORMAT_PATH = "{}/{}".format _FORMAT_DOMAIN_REVERSED = "{1}.{0}".format +# The minimum number of scheduled cookie expirations before we start cleaning up +# the expiration heap. This is a performance optimization to avoid cleaning up the +# heap too often when there are only a few scheduled expirations. +_MIN_SCHEDULED_COOKIE_EXPIRATION = 100 + class CookieJar(AbstractCookieJar): """Implements cookie storage adhering to RFC 6265.""" @@ -105,7 +110,7 @@ def __init__( for url in treat_as_secure_origin ] self._treat_as_secure_origin = treat_as_secure_origin - self._next_expiration: float = ceil(time.time()) + self._expire_heap: List[Tuple[float, Tuple[str, str, str]]] = [] self._expirations: Dict[Tuple[str, str, str], float] = {} def save(self, file_path: PathLike) -> None: @@ -120,34 +125,25 @@ def load(self, file_path: PathLike) -> None: def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None: if predicate is None: - self._next_expiration = ceil(time.time()) + self._expire_heap.clear() self._cookies.clear() self._host_only_cookies.clear() self._expirations.clear() return - to_del = [] now = time.time() - for (domain, path), cookie in self._cookies.items(): - for name, morsel in cookie.items(): - key = (domain, path, name) - if ( - key in self._expirations and self._expirations[key] <= now - ) or predicate(morsel): - to_del.append(key) - - for domain, path, name in to_del: - self._host_only_cookies.discard((domain, name)) - key = (domain, path, name) - if key in self._expirations: - del self._expirations[(domain, path, name)] - self._cookies[(domain, path)].pop(name, None) - - self._next_expiration = ( - min(*self._expirations.values(), self.SUB_MAX_TIME) + 1 - if self._expirations - else self.MAX_TIME - ) + to_del = [ + key + for (domain, path), cookie in self._cookies.items() + for name, morsel in cookie.items() + if ( + (key := (domain, path, name)) in self._expirations + and self._expirations[key] <= now + ) + or predicate(morsel) + ] + if to_del: + self._delete_cookies(to_del) def clear_domain(self, domain: str) -> None: self.clear(lambda x: self._is_domain_match(domain, x["domain"])) @@ -166,11 +162,61 @@ def __len__(self) -> int: return sum(len(cookie.values()) for cookie in self._cookies.values()) def _do_expiration(self) -> None: - self.clear(lambda x: False) + """Remove expired cookies.""" + if not (expire_heap_len := len(self._expire_heap)): + return + + # If the expiration heap grows larger than the number expirations + # times two, we clean it up to avoid keeping expired entries in + # the heap and consuming memory. We guard this with a minimum + # threshold to avoid cleaning up the heap too often when there are + # only a few scheduled expirations. + if ( + expire_heap_len > _MIN_SCHEDULED_COOKIE_EXPIRATION + and expire_heap_len > len(self._expirations) * 2 + ): + # Remove any expired entries from the expiration heap + # that do not match the expiration time in the expirations + # as it means the cookie has been re-added to the heap + # with a different expiration time. + self._expire_heap = [ + entry + for entry in self._expire_heap + if self._expirations.get(entry[1]) == entry[0] + ] + heapq.heapify(self._expire_heap) + + now = time.time() + to_del: List[Tuple[str, str, str]] = [] + # Find any expired cookies and add them to the to-delete list + while self._expire_heap: + when, cookie_key = self._expire_heap[0] + if when > now: + break + heapq.heappop(self._expire_heap) + # Check if the cookie hasn't been re-added to the heap + # with a different expiration time as it will be removed + # later when it reaches the top of the heap and its + # expiration time is met. + if self._expirations.get(cookie_key) == when: + to_del.append(cookie_key) + + if to_del: + self._delete_cookies(to_del) + + def _delete_cookies(self, to_del: List[Tuple[str, str, str]]) -> None: + for domain, path, name in to_del: + self._host_only_cookies.discard((domain, name)) + self._cookies[(domain, path)].pop(name, None) + self._expirations.pop((domain, path, name), None) def _expire_cookie(self, when: float, domain: str, path: str, name: str) -> None: - self._next_expiration = min(self._next_expiration, when) - self._expirations[(domain, path, name)] = when + cookie_key = (domain, path, name) + if self._expirations.get(cookie_key) == when: + # Avoid adding duplicates to the heap + return + heapq.heappush(self._expire_heap, (when, cookie_key)) + self._expirations[cookie_key] = when def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: """Update cookies.""" diff --git a/tests/test_cookiejar.py b/tests/test_cookiejar.py index 91352f50c3d..248d0d419e3 100644 --- a/tests/test_cookiejar.py +++ b/tests/test_cookiejar.py @@ -1,10 +1,12 @@ import asyncio import datetime +import heapq import itertools import pathlib import pickle import unittest from http.cookies import BaseCookie, Morsel, SimpleCookie +from operator import not_ from unittest import mock import pytest @@ -847,12 +849,98 @@ async def test_cookie_jar_clear_expired(): with freeze_time("1980-01-01"): sut.update_cookies(cookie) - sut.clear(lambda x: False) - with freeze_time("1980-01-01"): - assert len(sut) == 0 + for _ in range(2): + sut.clear(not_) + with freeze_time("1980-01-01"): + assert len(sut) == 0 + + +async def test_cookie_jar_expired_changes() -> None: + """Test that expire time changes are handled as expected.""" + jar = CookieJar() + + cookie_eleven_am = SimpleCookie() + cookie_eleven_am["foo"] = "bar" + cookie_eleven_am["foo"]["expires"] = "Tue, 1 Jan 1990 11:00:00 GMT" + + cookie_noon = SimpleCookie() + cookie_noon["foo"] = "bar" + cookie_noon["foo"]["expires"] = "Tue, 1 Jan 1990 12:00:00 GMT" + + cookie_one_pm = SimpleCookie() + cookie_one_pm["foo"] = "bar" + cookie_one_pm["foo"]["expires"] = "Tue, 1 Jan 1990 13:00:00 GMT" + + cookie_two_pm = SimpleCookie() + cookie_two_pm["foo"] = "bar" + cookie_two_pm["foo"]["expires"] = "Tue, 1 Jan 1990 14:00:00 GMT" + + with freeze_time() as freezer: + freezer.move_to("1990-01-01 10:00:00+00:00") + jar.update_cookies(cookie_noon) + assert len(jar) == 1 + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 1 + assert "foo" in matched_cookies + + jar.update_cookies(cookie_eleven_am) + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 1 + assert "foo" in matched_cookies + + jar.update_cookies(cookie_one_pm) + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 1 + assert "foo" in matched_cookies + + jar.update_cookies(cookie_two_pm) + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 1 + assert "foo" in matched_cookies + + freezer.move_to("1990-01-01 13:00:00+00:00") + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 1 + assert "foo" in matched_cookies + + freezer.move_to("1990-01-01 14:00:00+00:00") + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 0 + + +async def test_cookie_jar_duplicates_with_expire_heap() -> None: + """Test that duplicate cookies do not grow the expires heap.""" + jar = CookieJar() + + cookie_eleven_am = SimpleCookie() + cookie_eleven_am["foo"] = "bar" + cookie_eleven_am["foo"]["expires"] = "Tue, 1 Jan 1990 11:00:00 GMT" + + cookie_two_pm = SimpleCookie() + cookie_two_pm["foo"] = "bar" + cookie_two_pm["foo"]["expires"] = "Tue, 1 Jan 1990 14:00:00 GMT" + + with freeze_time() as freezer: + freezer.move_to("1990-01-01 10:00:00+00:00") + for _ in range(10): + jar.update_cookies(cookie_eleven_am) -async def test_cookie_jar_filter_cookies_expires(): + assert len(jar) == 1 + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 1 + assert "foo" in matched_cookies + + assert len(jar._expire_heap) == 1 + + freezer.move_to("1990-01-01 16:00:00+00:00") + jar.update_cookies(cookie_two_pm) + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 0 + assert len(jar._expire_heap) == 0 + + +async def test_cookie_jar_filter_cookies_expires() -> None: """Test that calling filter_cookies will expire stale cookies.""" jar = CookieJar() assert len(jar) == 0 @@ -873,6 +961,84 @@ async def test_cookie_jar_filter_cookies_expires(): assert len(jar) == 0 +async def test_cookie_jar_heap_cleanup() -> None: + """Test that the heap gets cleaned up when there are many old expirations.""" + jar = CookieJar() + # The heap should not be cleaned up when there are less than 100 expiration changes + min_cookies_to_cleanup = 100 + + with freeze_time() as freezer: + freezer.move_to("1990-01-01 09:00:00+00:00") + + start_time = datetime.datetime( + 1990, 1, 1, 10, 0, 0, tzinfo=datetime.timezone.utc + ) + for i in range(min_cookies_to_cleanup): + cookie = SimpleCookie() + cookie["foo"] = "bar" + cookie["foo"]["expires"] = ( + start_time + datetime.timedelta(seconds=i) + ).strftime("%a, %d %b %Y %H:%M:%S GMT") + jar.update_cookies(cookie) + assert len(jar._expire_heap) == i + 1 + + assert len(jar._expire_heap) == min_cookies_to_cleanup + + # Now that we reached the minimum number of cookies to cleanup, + # add one more cookie to trigger the cleanup + cookie = SimpleCookie() + cookie["foo"] = "bar" + cookie["foo"]["expires"] = ( + start_time + datetime.timedelta(seconds=i + 1) + ).strftime("%a, %d %b %Y %H:%M:%S GMT") + jar.update_cookies(cookie) + + # Verify that the heap has been cleaned up + assert len(jar) == 1 + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 1 + assert "foo" in matched_cookies + # The heap should have been cleaned up + assert len(jar._expire_heap) == 1 + + +async def test_cookie_jar_heap_maintains_order_after_cleanup() -> None: + """Test that order is maintained after cleanup.""" + jar = CookieJar() + # The heap should not be cleaned up when there are less than 100 expiration changes + min_cookies_to_cleanup = 100 + + with freeze_time() as freezer: + freezer.move_to("1990-01-01 09:00:00+00:00") + + for hour in (12, 13): + for i in range(min_cookies_to_cleanup): + cookie = SimpleCookie() + cookie["foo"] = "bar" + cookie["foo"]["domain"] = f"example{i}.com" + cookie["foo"]["expires"] = f"Tue, 1 Jan 1990 {hour}:00:00 GMT" + jar.update_cookies(cookie) + + # Get the jar into a state where the next cookie will trigger the cleanup + assert len(jar._expire_heap) == min_cookies_to_cleanup * 2 + assert len(jar._expirations) == min_cookies_to_cleanup + + cookie = SimpleCookie() + cookie["foo"] = "bar" + cookie["foo"]["domain"] = "example0.com" + cookie["foo"]["expires"] = "Tue, 1 Jan 1990 14:00:00 GMT" + jar.update_cookies(cookie) + + assert len(jar) == 100 + # The heap should have been cleaned up + assert len(jar._expire_heap) == 100 + + # Verify that the heap is still ordered + heap_before = jar._expire_heap.copy() + heapq.heapify(jar._expire_heap) + assert heap_before == jar._expire_heap + + async def test_cookie_jar_clear_domain() -> None: sut = CookieJar() cookie = SimpleCookie() From 6608ffee20a1cec8e83765dbcc041cdbc08b23b8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 21 Sep 2024 11:45:03 +0200 Subject: [PATCH 240/296] =?UTF-8?q?[PR=C2=A0#9203/6e70c0a=20backport][3.11?= =?UTF-8?q?]=20Implement=20heapq=20for=20cookie=20expire=20times=20(#9209)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES/9203.misc.rst | 3 + aiohttp/cookiejar.py | 100 ++++++++++++++++------- tests/test_cookiejar.py | 174 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 246 insertions(+), 31 deletions(-) create mode 100644 CHANGES/9203.misc.rst diff --git a/CHANGES/9203.misc.rst b/CHANGES/9203.misc.rst new file mode 100644 index 00000000000..766fdc01a57 --- /dev/null +++ b/CHANGES/9203.misc.rst @@ -0,0 +1,3 @@ +Significantly improved performance of expiring cookies -- by :user:`bdraco`. + +Expiring cookies has been redesigned to use :mod:`heapq` instead of a linear search, to better scale. diff --git a/aiohttp/cookiejar.py b/aiohttp/cookiejar.py index c57604b5e59..241a9e3e0a4 100644 --- a/aiohttp/cookiejar.py +++ b/aiohttp/cookiejar.py @@ -2,6 +2,7 @@ import calendar import contextlib import datetime +import heapq import itertools import os # noqa import pathlib @@ -10,7 +11,6 @@ import time from collections import defaultdict from http.cookies import BaseCookie, Morsel, SimpleCookie -from math import ceil from typing import ( DefaultDict, Dict, @@ -40,6 +40,11 @@ _FORMAT_PATH = "{}/{}".format _FORMAT_DOMAIN_REVERSED = "{1}.{0}".format +# The minimum number of scheduled cookie expirations before we start cleaning up +# the expiration heap. This is a performance optimization to avoid cleaning up the +# heap too often when there are only a few scheduled expirations. +_MIN_SCHEDULED_COOKIE_EXPIRATION = 100 + class CookieJar(AbstractCookieJar): """Implements cookie storage adhering to RFC 6265.""" @@ -105,7 +110,7 @@ def __init__( for url in treat_as_secure_origin ] self._treat_as_secure_origin = treat_as_secure_origin - self._next_expiration: float = ceil(time.time()) + self._expire_heap: List[Tuple[float, Tuple[str, str, str]]] = [] self._expirations: Dict[Tuple[str, str, str], float] = {} def save(self, file_path: PathLike) -> None: @@ -120,34 +125,25 @@ def load(self, file_path: PathLike) -> None: def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None: if predicate is None: - self._next_expiration = ceil(time.time()) + self._expire_heap.clear() self._cookies.clear() self._host_only_cookies.clear() self._expirations.clear() return - to_del = [] now = time.time() - for (domain, path), cookie in self._cookies.items(): - for name, morsel in cookie.items(): - key = (domain, path, name) - if ( - key in self._expirations and self._expirations[key] <= now - ) or predicate(morsel): - to_del.append(key) - - for domain, path, name in to_del: - self._host_only_cookies.discard((domain, name)) - key = (domain, path, name) - if key in self._expirations: - del self._expirations[(domain, path, name)] - self._cookies[(domain, path)].pop(name, None) - - self._next_expiration = ( - min(*self._expirations.values(), self.SUB_MAX_TIME) + 1 - if self._expirations - else self.MAX_TIME - ) + to_del = [ + key + for (domain, path), cookie in self._cookies.items() + for name, morsel in cookie.items() + if ( + (key := (domain, path, name)) in self._expirations + and self._expirations[key] <= now + ) + or predicate(morsel) + ] + if to_del: + self._delete_cookies(to_del) def clear_domain(self, domain: str) -> None: self.clear(lambda x: self._is_domain_match(domain, x["domain"])) @@ -166,11 +162,61 @@ def __len__(self) -> int: return sum(len(cookie.values()) for cookie in self._cookies.values()) def _do_expiration(self) -> None: - self.clear(lambda x: False) + """Remove expired cookies.""" + if not (expire_heap_len := len(self._expire_heap)): + return + + # If the expiration heap grows larger than the number expirations + # times two, we clean it up to avoid keeping expired entries in + # the heap and consuming memory. We guard this with a minimum + # threshold to avoid cleaning up the heap too often when there are + # only a few scheduled expirations. + if ( + expire_heap_len > _MIN_SCHEDULED_COOKIE_EXPIRATION + and expire_heap_len > len(self._expirations) * 2 + ): + # Remove any expired entries from the expiration heap + # that do not match the expiration time in the expirations + # as it means the cookie has been re-added to the heap + # with a different expiration time. + self._expire_heap = [ + entry + for entry in self._expire_heap + if self._expirations.get(entry[1]) == entry[0] + ] + heapq.heapify(self._expire_heap) + + now = time.time() + to_del: List[Tuple[str, str, str]] = [] + # Find any expired cookies and add them to the to-delete list + while self._expire_heap: + when, cookie_key = self._expire_heap[0] + if when > now: + break + heapq.heappop(self._expire_heap) + # Check if the cookie hasn't been re-added to the heap + # with a different expiration time as it will be removed + # later when it reaches the top of the heap and its + # expiration time is met. + if self._expirations.get(cookie_key) == when: + to_del.append(cookie_key) + + if to_del: + self._delete_cookies(to_del) + + def _delete_cookies(self, to_del: List[Tuple[str, str, str]]) -> None: + for domain, path, name in to_del: + self._host_only_cookies.discard((domain, name)) + self._cookies[(domain, path)].pop(name, None) + self._expirations.pop((domain, path, name), None) def _expire_cookie(self, when: float, domain: str, path: str, name: str) -> None: - self._next_expiration = min(self._next_expiration, when) - self._expirations[(domain, path, name)] = when + cookie_key = (domain, path, name) + if self._expirations.get(cookie_key) == when: + # Avoid adding duplicates to the heap + return + heapq.heappush(self._expire_heap, (when, cookie_key)) + self._expirations[cookie_key] = when def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: """Update cookies.""" diff --git a/tests/test_cookiejar.py b/tests/test_cookiejar.py index 91352f50c3d..248d0d419e3 100644 --- a/tests/test_cookiejar.py +++ b/tests/test_cookiejar.py @@ -1,10 +1,12 @@ import asyncio import datetime +import heapq import itertools import pathlib import pickle import unittest from http.cookies import BaseCookie, Morsel, SimpleCookie +from operator import not_ from unittest import mock import pytest @@ -847,12 +849,98 @@ async def test_cookie_jar_clear_expired(): with freeze_time("1980-01-01"): sut.update_cookies(cookie) - sut.clear(lambda x: False) - with freeze_time("1980-01-01"): - assert len(sut) == 0 + for _ in range(2): + sut.clear(not_) + with freeze_time("1980-01-01"): + assert len(sut) == 0 + + +async def test_cookie_jar_expired_changes() -> None: + """Test that expire time changes are handled as expected.""" + jar = CookieJar() + + cookie_eleven_am = SimpleCookie() + cookie_eleven_am["foo"] = "bar" + cookie_eleven_am["foo"]["expires"] = "Tue, 1 Jan 1990 11:00:00 GMT" + + cookie_noon = SimpleCookie() + cookie_noon["foo"] = "bar" + cookie_noon["foo"]["expires"] = "Tue, 1 Jan 1990 12:00:00 GMT" + + cookie_one_pm = SimpleCookie() + cookie_one_pm["foo"] = "bar" + cookie_one_pm["foo"]["expires"] = "Tue, 1 Jan 1990 13:00:00 GMT" + + cookie_two_pm = SimpleCookie() + cookie_two_pm["foo"] = "bar" + cookie_two_pm["foo"]["expires"] = "Tue, 1 Jan 1990 14:00:00 GMT" + + with freeze_time() as freezer: + freezer.move_to("1990-01-01 10:00:00+00:00") + jar.update_cookies(cookie_noon) + assert len(jar) == 1 + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 1 + assert "foo" in matched_cookies + + jar.update_cookies(cookie_eleven_am) + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 1 + assert "foo" in matched_cookies + + jar.update_cookies(cookie_one_pm) + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 1 + assert "foo" in matched_cookies + + jar.update_cookies(cookie_two_pm) + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 1 + assert "foo" in matched_cookies + + freezer.move_to("1990-01-01 13:00:00+00:00") + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 1 + assert "foo" in matched_cookies + + freezer.move_to("1990-01-01 14:00:00+00:00") + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 0 + + +async def test_cookie_jar_duplicates_with_expire_heap() -> None: + """Test that duplicate cookies do not grow the expires heap.""" + jar = CookieJar() + + cookie_eleven_am = SimpleCookie() + cookie_eleven_am["foo"] = "bar" + cookie_eleven_am["foo"]["expires"] = "Tue, 1 Jan 1990 11:00:00 GMT" + + cookie_two_pm = SimpleCookie() + cookie_two_pm["foo"] = "bar" + cookie_two_pm["foo"]["expires"] = "Tue, 1 Jan 1990 14:00:00 GMT" + + with freeze_time() as freezer: + freezer.move_to("1990-01-01 10:00:00+00:00") + for _ in range(10): + jar.update_cookies(cookie_eleven_am) -async def test_cookie_jar_filter_cookies_expires(): + assert len(jar) == 1 + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 1 + assert "foo" in matched_cookies + + assert len(jar._expire_heap) == 1 + + freezer.move_to("1990-01-01 16:00:00+00:00") + jar.update_cookies(cookie_two_pm) + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 0 + assert len(jar._expire_heap) == 0 + + +async def test_cookie_jar_filter_cookies_expires() -> None: """Test that calling filter_cookies will expire stale cookies.""" jar = CookieJar() assert len(jar) == 0 @@ -873,6 +961,84 @@ async def test_cookie_jar_filter_cookies_expires(): assert len(jar) == 0 +async def test_cookie_jar_heap_cleanup() -> None: + """Test that the heap gets cleaned up when there are many old expirations.""" + jar = CookieJar() + # The heap should not be cleaned up when there are less than 100 expiration changes + min_cookies_to_cleanup = 100 + + with freeze_time() as freezer: + freezer.move_to("1990-01-01 09:00:00+00:00") + + start_time = datetime.datetime( + 1990, 1, 1, 10, 0, 0, tzinfo=datetime.timezone.utc + ) + for i in range(min_cookies_to_cleanup): + cookie = SimpleCookie() + cookie["foo"] = "bar" + cookie["foo"]["expires"] = ( + start_time + datetime.timedelta(seconds=i) + ).strftime("%a, %d %b %Y %H:%M:%S GMT") + jar.update_cookies(cookie) + assert len(jar._expire_heap) == i + 1 + + assert len(jar._expire_heap) == min_cookies_to_cleanup + + # Now that we reached the minimum number of cookies to cleanup, + # add one more cookie to trigger the cleanup + cookie = SimpleCookie() + cookie["foo"] = "bar" + cookie["foo"]["expires"] = ( + start_time + datetime.timedelta(seconds=i + 1) + ).strftime("%a, %d %b %Y %H:%M:%S GMT") + jar.update_cookies(cookie) + + # Verify that the heap has been cleaned up + assert len(jar) == 1 + matched_cookies = jar.filter_cookies(URL("/")) + assert len(matched_cookies) == 1 + assert "foo" in matched_cookies + # The heap should have been cleaned up + assert len(jar._expire_heap) == 1 + + +async def test_cookie_jar_heap_maintains_order_after_cleanup() -> None: + """Test that order is maintained after cleanup.""" + jar = CookieJar() + # The heap should not be cleaned up when there are less than 100 expiration changes + min_cookies_to_cleanup = 100 + + with freeze_time() as freezer: + freezer.move_to("1990-01-01 09:00:00+00:00") + + for hour in (12, 13): + for i in range(min_cookies_to_cleanup): + cookie = SimpleCookie() + cookie["foo"] = "bar" + cookie["foo"]["domain"] = f"example{i}.com" + cookie["foo"]["expires"] = f"Tue, 1 Jan 1990 {hour}:00:00 GMT" + jar.update_cookies(cookie) + + # Get the jar into a state where the next cookie will trigger the cleanup + assert len(jar._expire_heap) == min_cookies_to_cleanup * 2 + assert len(jar._expirations) == min_cookies_to_cleanup + + cookie = SimpleCookie() + cookie["foo"] = "bar" + cookie["foo"]["domain"] = "example0.com" + cookie["foo"]["expires"] = "Tue, 1 Jan 1990 14:00:00 GMT" + jar.update_cookies(cookie) + + assert len(jar) == 100 + # The heap should have been cleaned up + assert len(jar._expire_heap) == 100 + + # Verify that the heap is still ordered + heap_before = jar._expire_heap.copy() + heapq.heapify(jar._expire_heap) + assert heap_before == jar._expire_heap + + async def test_cookie_jar_clear_domain() -> None: sut = CookieJar() cookie = SimpleCookie() From 0408ba675de2c008028752df653e73e28a9f64d1 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sat, 21 Sep 2024 09:58:13 +0000 Subject: [PATCH 241/296] [PR #9168/5e15ea61 backport][3.11] Avoid creating handler waiter until shutdown (#9211) --- CHANGES/9168.misc.rst | 1 + aiohttp/web_protocol.py | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) create mode 120000 CHANGES/9168.misc.rst diff --git a/CHANGES/9168.misc.rst b/CHANGES/9168.misc.rst new file mode 120000 index 00000000000..d6a2f2aaaab --- /dev/null +++ b/CHANGES/9168.misc.rst @@ -0,0 +1 @@ +9174.misc.rst \ No newline at end of file diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index a7f7b546903..dd819de7236 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -162,6 +162,7 @@ class RequestHandler(BaseProtocol): "_force_close", "_current_request", "_timeout_ceil_threshold", + "_request_in_progress", ) def __init__( @@ -238,6 +239,7 @@ def __init__( self._close = False self._force_close = False + self._request_in_progress = False def __repr__(self) -> str: return "<{} {}>".format( @@ -261,7 +263,11 @@ async def shutdown(self, timeout: Optional[float] = 15.0) -> None: self._keepalive_handle.cancel() # Wait for graceful handler completion - if self._handler_waiter is not None: + if self._request_in_progress: + # The future is only created when we are shutting + # down while the handler is still processing a request + # to avoid creating a future for every request. + self._handler_waiter = self._loop.create_future() with suppress(asyncio.CancelledError, asyncio.TimeoutError): async with ceil_timeout(timeout): await self._handler_waiter @@ -446,7 +452,7 @@ async def _handle_request( start_time: float, request_handler: Callable[[BaseRequest], Awaitable[StreamResponse]], ) -> Tuple[StreamResponse, bool]: - self._handler_waiter = self._loop.create_future() + self._request_in_progress = True try: try: self._current_request = request @@ -477,7 +483,9 @@ async def _handle_request( resp, reset = await self.finish_response(request, resp, start_time) finally: - self._handler_waiter.set_result(None) + self._request_in_progress = False + if self._handler_waiter is not None: + self._handler_waiter.set_result(None) return resp, reset From 2b11f5e5252d7696b4089c7498cc1612404304de Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sat, 21 Sep 2024 09:58:13 +0000 Subject: [PATCH 242/296] [PR #9168/5e15ea61 backport][3.10] Avoid creating handler waiter until shutdown (#9210) --- CHANGES/9168.misc.rst | 1 + aiohttp/web_protocol.py | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) create mode 120000 CHANGES/9168.misc.rst diff --git a/CHANGES/9168.misc.rst b/CHANGES/9168.misc.rst new file mode 120000 index 00000000000..d6a2f2aaaab --- /dev/null +++ b/CHANGES/9168.misc.rst @@ -0,0 +1 @@ +9174.misc.rst \ No newline at end of file diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index a7f7b546903..dd819de7236 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -162,6 +162,7 @@ class RequestHandler(BaseProtocol): "_force_close", "_current_request", "_timeout_ceil_threshold", + "_request_in_progress", ) def __init__( @@ -238,6 +239,7 @@ def __init__( self._close = False self._force_close = False + self._request_in_progress = False def __repr__(self) -> str: return "<{} {}>".format( @@ -261,7 +263,11 @@ async def shutdown(self, timeout: Optional[float] = 15.0) -> None: self._keepalive_handle.cancel() # Wait for graceful handler completion - if self._handler_waiter is not None: + if self._request_in_progress: + # The future is only created when we are shutting + # down while the handler is still processing a request + # to avoid creating a future for every request. + self._handler_waiter = self._loop.create_future() with suppress(asyncio.CancelledError, asyncio.TimeoutError): async with ceil_timeout(timeout): await self._handler_waiter @@ -446,7 +452,7 @@ async def _handle_request( start_time: float, request_handler: Callable[[BaseRequest], Awaitable[StreamResponse]], ) -> Tuple[StreamResponse, bool]: - self._handler_waiter = self._loop.create_future() + self._request_in_progress = True try: try: self._current_request = request @@ -477,7 +483,9 @@ async def _handle_request( resp, reset = await self.finish_response(request, resp, start_time) finally: - self._handler_waiter.set_result(None) + self._request_in_progress = False + if self._handler_waiter is not None: + self._handler_waiter.set_result(None) return resp, reset From e232f61b8433b058f1763c5fa4b985ef65fc21c6 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sat, 21 Sep 2024 20:17:14 +0000 Subject: [PATCH 243/296] [PR #9175/756fae80 backport][3.11] Speed up finding the reason if its unset (#9213) Co-authored-by: J. Nick Koston --- CHANGES/9175.misc.rst | 1 + aiohttp/web_response.py | 14 ++++++++------ tests/test_web_response.py | 8 ++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) create mode 120000 CHANGES/9175.misc.rst diff --git a/CHANGES/9175.misc.rst b/CHANGES/9175.misc.rst new file mode 120000 index 00000000000..d6a2f2aaaab --- /dev/null +++ b/CHANGES/9175.misc.rst @@ -0,0 +1 @@ +9174.misc.rst \ No newline at end of file diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index bf184980700..0cbbe84260f 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -42,6 +42,8 @@ from .payload import Payload from .typedefs import JSONEncoder, LooseHeaders +REASON_PHRASES = {http_status.value: http_status.phrase for http_status in HTTPStatus} + __all__ = ("ContentCoding", "StreamResponse", "Response", "json_response") @@ -102,7 +104,7 @@ def __init__( else: self._headers = CIMultiDict() - self.set_status(status, reason) + self._set_status(status, reason) @property def prepared(self) -> bool: @@ -139,13 +141,13 @@ def set_status( assert ( not self.prepared ), "Cannot change the response status code after the headers have been sent" + self._set_status(status, reason) + + def _set_status(self, status: int, reason: Optional[str]) -> None: self._status = int(status) if reason is None: - try: - reason = HTTPStatus(self._status).phrase - except ValueError: - reason = "" - if "\n" in reason: + reason = REASON_PHRASES.get(self._status, "") + elif "\n" in reason: raise ValueError("Reason cannot contain \\n") self._reason = reason diff --git a/tests/test_web_response.py b/tests/test_web_response.py index ec9522b05a5..080edaf57c3 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -933,6 +933,14 @@ def test_set_status_with_reason() -> None: assert "Everything is fine!" == resp.reason +def test_set_status_with_empty_reason() -> None: + resp = StreamResponse() + + resp.set_status(200, "") + assert resp.status == 200 + assert resp.reason == "" + + async def test_start_force_close() -> None: req = make_request("GET", "/") resp = StreamResponse() From 00e06f038c99f1236af5f632b9d15ebf262aa679 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 21 Sep 2024 22:17:17 +0200 Subject: [PATCH 244/296] [PR #9175/756fae80 backport][3.10] Speed up finding the reason if its unset (#9214) Co-authored-by: pre-commit-ci[bot] --- CHANGES/9175.misc.rst | 1 + aiohttp/web_response.py | 20 +++++++++++--------- tests/test_web_response.py | 8 ++++++++ 3 files changed, 20 insertions(+), 9 deletions(-) create mode 120000 CHANGES/9175.misc.rst diff --git a/CHANGES/9175.misc.rst b/CHANGES/9175.misc.rst new file mode 120000 index 00000000000..d6a2f2aaaab --- /dev/null +++ b/CHANGES/9175.misc.rst @@ -0,0 +1 @@ +9174.misc.rst \ No newline at end of file diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index c14a7544d6f..3188ac59c77 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -41,6 +41,8 @@ from .payload import Payload from .typedefs import JSONEncoder, LooseHeaders +REASON_PHRASES = {http_status.value: http_status.phrase for http_status in HTTPStatus} + __all__ = ("ContentCoding", "StreamResponse", "Response", "json_response") @@ -100,7 +102,7 @@ def __init__( else: self._headers = CIMultiDict() - self.set_status(status, reason) + self._set_status(status, reason) @property def prepared(self) -> bool: @@ -134,16 +136,16 @@ def set_status( status: int, reason: Optional[str] = None, ) -> None: - assert not self.prepared, ( - "Cannot change the response status code after " "the headers have been sent" - ) + assert ( + not self.prepared + ), "Cannot change the response status code after the headers have been sent" + self._set_status(status, reason) + + def _set_status(self, status: int, reason: Optional[str]) -> None: self._status = int(status) if reason is None: - try: - reason = HTTPStatus(self._status).phrase - except ValueError: - reason = "" - if "\n" in reason: + reason = REASON_PHRASES.get(self._status, "") + elif "\n" in reason: raise ValueError("Reason cannot contain \\n") self._reason = reason diff --git a/tests/test_web_response.py b/tests/test_web_response.py index 3694e65948b..25e464f7bed 100644 --- a/tests/test_web_response.py +++ b/tests/test_web_response.py @@ -932,6 +932,14 @@ def test_set_status_with_reason() -> None: assert "Everything is fine!" == resp.reason +def test_set_status_with_empty_reason() -> None: + resp = StreamResponse() + + resp.set_status(200, "") + assert resp.status == 200 + assert resp.reason == "" + + async def test_start_force_close() -> None: req = make_request("GET", "/") resp = StreamResponse() From 206afde308a9138254129eb829d8c52e318e3f33 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 22 Sep 2024 13:28:18 +0100 Subject: [PATCH 245/296] Backport some changes from #9215 (#9217) --- aiohttp/pytest_plugin.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/aiohttp/pytest_plugin.py b/aiohttp/pytest_plugin.py index c862b409566..55964ead041 100644 --- a/aiohttp/pytest_plugin.py +++ b/aiohttp/pytest_plugin.py @@ -16,8 +16,6 @@ import pytest -from aiohttp.web import Application - from .test_utils import ( BaseTestServer, RawTestServer, @@ -28,14 +26,14 @@ teardown_test_loop, unused_port as _unused_port, ) +from .web import Application +from .web_protocol import _RequestHandler try: import uvloop except ImportError: # pragma: no cover uvloop = None # type: ignore[assignment] -AiohttpRawServer = Callable[[Application], Awaitable[RawTestServer]] - class AiohttpClient(Protocol): def __call__( @@ -53,6 +51,12 @@ def __call__( ) -> Awaitable[TestServer]: ... +class AiohttpRawServer(Protocol): + def __call__( + self, handler: _RequestHandler, *, port: Optional[int] = None, **kwargs: Any + ) -> Awaitable[RawTestServer]: ... + + def pytest_addoption(parser): # type: ignore[no-untyped-def] parser.addoption( "--aiohttp-fast", @@ -321,7 +325,9 @@ def aiohttp_raw_server(loop: asyncio.AbstractEventLoop) -> Iterator[AiohttpRawSe """ servers = [] - async def go(handler, *, port=None, **kwargs): # type: ignore[no-untyped-def] + async def go( + handler: _RequestHandler, *, port: Optional[int] = None, **kwargs: Any + ) -> RawTestServer: server = RawTestServer(handler, port=port) await server.start_server(loop=loop, **kwargs) servers.append(server) From ec5de6c9391fa4682f9b6966f6ceb7ac26f14f23 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 14:42:14 +0100 Subject: [PATCH 246/296] [PR #9217/206afde3 backport][3.10] Backport some changes from #9215 (#9218) **This is a backport of PR #9217 as merged into 3.11 (206afde308a9138254129eb829d8c52e318e3f33).** Co-authored-by: Sam Bull --- aiohttp/pytest_plugin.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/aiohttp/pytest_plugin.py b/aiohttp/pytest_plugin.py index c862b409566..55964ead041 100644 --- a/aiohttp/pytest_plugin.py +++ b/aiohttp/pytest_plugin.py @@ -16,8 +16,6 @@ import pytest -from aiohttp.web import Application - from .test_utils import ( BaseTestServer, RawTestServer, @@ -28,14 +26,14 @@ teardown_test_loop, unused_port as _unused_port, ) +from .web import Application +from .web_protocol import _RequestHandler try: import uvloop except ImportError: # pragma: no cover uvloop = None # type: ignore[assignment] -AiohttpRawServer = Callable[[Application], Awaitable[RawTestServer]] - class AiohttpClient(Protocol): def __call__( @@ -53,6 +51,12 @@ def __call__( ) -> Awaitable[TestServer]: ... +class AiohttpRawServer(Protocol): + def __call__( + self, handler: _RequestHandler, *, port: Optional[int] = None, **kwargs: Any + ) -> Awaitable[RawTestServer]: ... + + def pytest_addoption(parser): # type: ignore[no-untyped-def] parser.addoption( "--aiohttp-fast", @@ -321,7 +325,9 @@ def aiohttp_raw_server(loop: asyncio.AbstractEventLoop) -> Iterator[AiohttpRawSe """ servers = [] - async def go(handler, *, port=None, **kwargs): # type: ignore[no-untyped-def] + async def go( + handler: _RequestHandler, *, port: Optional[int] = None, **kwargs: Any + ) -> RawTestServer: server = RawTestServer(handler, port=port) await server.start_server(loop=loop, **kwargs) servers.append(server) From 689bf1f4706a07ba346422b4528ec24e69279a07 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 11:00:05 -0400 Subject: [PATCH 247/296] [PR #9216/3644101 backport][3.10] Small cleanup to should_close in client_proto (#9219) --- aiohttp/client_proto.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/aiohttp/client_proto.py b/aiohttp/client_proto.py index c6c262d3bfe..8055811e40d 100644 --- a/aiohttp/client_proto.py +++ b/aiohttp/client_proto.py @@ -50,15 +50,13 @@ def upgraded(self) -> bool: @property def should_close(self) -> bool: - if self._payload is not None and not self._payload.is_eof() or self._upgraded: - return True - return ( self._should_close + or (self._payload is not None and not self._payload.is_eof()) or self._upgraded - or self.exception() is not None + or self._exception is not None or self._payload_parser is not None - or len(self) > 0 + or bool(self._buffer) or bool(self._tail) ) From c8f89ce7d2a512757974f69bda45f978de0c7d77 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 11:00:05 -0400 Subject: [PATCH 248/296] [PR #9216/3644101 backport][3.11] Small cleanup to should_close in client_proto (#9220) --- aiohttp/client_proto.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/aiohttp/client_proto.py b/aiohttp/client_proto.py index 9230ae5145b..a3e29c01cc6 100644 --- a/aiohttp/client_proto.py +++ b/aiohttp/client_proto.py @@ -50,15 +50,13 @@ def upgraded(self) -> bool: @property def should_close(self) -> bool: - if self._payload is not None and not self._payload.is_eof() or self._upgraded: - return True - return ( self._should_close + or (self._payload is not None and not self._payload.is_eof()) or self._upgraded - or self.exception() is not None + or self._exception is not None or self._payload_parser is not None - or len(self) > 0 + or bool(self._buffer) or bool(self._tail) ) From 985c00f91e2d69b53f6b81a4ba028e7bd3374ac2 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 15:31:14 +0000 Subject: [PATCH 249/296] [PR #9169/fce4f8ec backport][3.10] Speed up starting compression (#9222) Co-authored-by: J. Nick Koston --- CHANGES/9169.misc.rst | 1 + aiohttp/web_response.py | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 120000 CHANGES/9169.misc.rst diff --git a/CHANGES/9169.misc.rst b/CHANGES/9169.misc.rst new file mode 120000 index 00000000000..d6a2f2aaaab --- /dev/null +++ b/CHANGES/9169.misc.rst @@ -0,0 +1 @@ +9174.misc.rst \ No newline at end of file diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 3188ac59c77..4d5095a4fea 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -65,6 +65,8 @@ class ContentCoding(enum.Enum): identity = "identity" +CONTENT_CODINGS = {coding.value: coding for coding in ContentCoding} + ############################################################ # HTTP Response classes ############################################################ @@ -410,8 +412,8 @@ async def _start_compression(self, request: "BaseRequest") -> None: # Encoding comparisons should be case-insensitive # https://www.rfc-editor.org/rfc/rfc9110#section-8.4.1 accept_encoding = request.headers.get(hdrs.ACCEPT_ENCODING, "").lower() - for coding in ContentCoding: - if coding.value in accept_encoding: + for value, coding in CONTENT_CODINGS.items(): + if value in accept_encoding: await self._do_start_compression(coding) return From 1401f6edf445b1d9951b1978d3386b9305cfdfdf Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 15:31:34 +0000 Subject: [PATCH 250/296] [PR #9169/fce4f8ec backport][3.11] Speed up starting compression (#9223) Co-authored-by: J. Nick Koston --- CHANGES/9169.misc.rst | 1 + aiohttp/web_response.py | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 120000 CHANGES/9169.misc.rst diff --git a/CHANGES/9169.misc.rst b/CHANGES/9169.misc.rst new file mode 120000 index 00000000000..d6a2f2aaaab --- /dev/null +++ b/CHANGES/9169.misc.rst @@ -0,0 +1 @@ +9174.misc.rst \ No newline at end of file diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 0cbbe84260f..5c0a3be1d21 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -66,6 +66,8 @@ class ContentCoding(enum.Enum): identity = "identity" +CONTENT_CODINGS = {coding.value: coding for coding in ContentCoding} + ############################################################ # HTTP Response classes ############################################################ @@ -427,8 +429,8 @@ async def _start_compression(self, request: "BaseRequest") -> None: # Encoding comparisons should be case-insensitive # https://www.rfc-editor.org/rfc/rfc9110#section-8.4.1 accept_encoding = request.headers.get(hdrs.ACCEPT_ENCODING, "").lower() - for coding in ContentCoding: - if coding.value in accept_encoding: + for value, coding in CONTENT_CODINGS.items(): + if value in accept_encoding: await self._do_start_compression(coding) return From f43762449252cb6ee4b38f53a685fee8dd652215 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 11:44:41 -0400 Subject: [PATCH 251/296] [PR #9064/7b11e23 backport][3.11] Fix sendfile test fixture (#9225) Co-authored-by: pre-commit-ci[bot] Co-authored-by: Sam Bull --- tests/test_web_sendfile_functional.py | 31 ++++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/tests/test_web_sendfile_functional.py b/tests/test_web_sendfile_functional.py index e2cfb7a1f0e..c9189a21fb0 100644 --- a/tests/test_web_sendfile_functional.py +++ b/tests/test_web_sendfile_functional.py @@ -5,6 +5,7 @@ import socket import zlib from typing import Any, Iterable, Optional +from unittest import mock import pytest @@ -47,15 +48,6 @@ def hello_txt(request, tmp_path_factory) -> pathlib.Path: return hello[encoding] -@pytest.fixture -def loop_without_sendfile(loop): - def sendfile(*args, **kwargs): - raise NotImplementedError - - loop.sendfile = sendfile - return loop - - @pytest.fixture def loop_with_mocked_native_sendfile(loop: Any): def sendfile(transport, fobj, offset, count): @@ -68,14 +60,27 @@ def sendfile(transport, fobj, offset, count): @pytest.fixture(params=["sendfile", "no_sendfile"], ids=["sendfile", "no_sendfile"]) -def sender(request, loop_without_sendfile): +def sender(request: Any, loop: Any): + sendfile_mock = None + def maker(*args, **kwargs): ret = web.FileResponse(*args, **kwargs) - if request.param == "no_sendfile": - asyncio.set_event_loop(loop_without_sendfile) + rloop = asyncio.get_running_loop() + is_patched = rloop.sendfile is sendfile_mock + assert is_patched if request.param == "no_sendfile" else not is_patched return ret - return maker + if request.param == "no_sendfile": + with mock.patch.object( + loop, + "sendfile", + autospec=True, + spec_set=True, + side_effect=NotImplementedError, + ) as sendfile_mock: + yield maker + else: + yield maker @pytest.fixture From 34ac151daa2a6be6910f7c97d368cb391a35eb7c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 11:44:48 -0400 Subject: [PATCH 252/296] [PR #9064/7b11e23 backport][3.10] Fix sendfile test fixture (#9224) Co-authored-by: pre-commit-ci[bot] Co-authored-by: Sam Bull --- tests/test_web_sendfile_functional.py | 31 ++++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/tests/test_web_sendfile_functional.py b/tests/test_web_sendfile_functional.py index e2cfb7a1f0e..c9189a21fb0 100644 --- a/tests/test_web_sendfile_functional.py +++ b/tests/test_web_sendfile_functional.py @@ -5,6 +5,7 @@ import socket import zlib from typing import Any, Iterable, Optional +from unittest import mock import pytest @@ -47,15 +48,6 @@ def hello_txt(request, tmp_path_factory) -> pathlib.Path: return hello[encoding] -@pytest.fixture -def loop_without_sendfile(loop): - def sendfile(*args, **kwargs): - raise NotImplementedError - - loop.sendfile = sendfile - return loop - - @pytest.fixture def loop_with_mocked_native_sendfile(loop: Any): def sendfile(transport, fobj, offset, count): @@ -68,14 +60,27 @@ def sendfile(transport, fobj, offset, count): @pytest.fixture(params=["sendfile", "no_sendfile"], ids=["sendfile", "no_sendfile"]) -def sender(request, loop_without_sendfile): +def sender(request: Any, loop: Any): + sendfile_mock = None + def maker(*args, **kwargs): ret = web.FileResponse(*args, **kwargs) - if request.param == "no_sendfile": - asyncio.set_event_loop(loop_without_sendfile) + rloop = asyncio.get_running_loop() + is_patched = rloop.sendfile is sendfile_mock + assert is_patched if request.param == "no_sendfile" else not is_patched return ret - return maker + if request.param == "no_sendfile": + with mock.patch.object( + loop, + "sendfile", + autospec=True, + spec_set=True, + side_effect=NotImplementedError, + ) as sendfile_mock: + yield maker + else: + yield maker @pytest.fixture From a3699f1059a9e76e4ba0ed81794063fb3cb47b4d Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 16:31:13 +0000 Subject: [PATCH 253/296] [PR #9221/e079c413 backport][3.10] Small cleanups to cookiejar (#9227) Co-authored-by: J. Nick Koston --- aiohttp/cookiejar.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/aiohttp/cookiejar.py b/aiohttp/cookiejar.py index 72c431a275c..0a6e35461f7 100644 --- a/aiohttp/cookiejar.py +++ b/aiohttp/cookiejar.py @@ -238,7 +238,7 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No domain = cookie["domain"] # ignore domains with trailing dots - if domain.endswith("."): + if domain and domain[-1] == ".": domain = "" del cookie["domain"] @@ -248,7 +248,7 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No self._host_only_cookies.add((hostname, name)) domain = cookie["domain"] = hostname - if domain.startswith("."): + if domain and domain[0] == ".": # Remove leading dot domain = domain[1:] cookie["domain"] = domain @@ -258,7 +258,7 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No continue path = cookie["path"] - if not path or not path.startswith("/"): + if not path or path[0] != "/": # Set the cookie's path to the response path path = response_url.path if not path.startswith("/"): @@ -269,8 +269,7 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No cookie["path"] = path path = path.rstrip("/") - max_age = cookie["max-age"] - if max_age: + if max_age := cookie["max-age"]: try: delta_seconds = int(max_age) max_age_expiration = min(time.time() + delta_seconds, self.MAX_TIME) @@ -278,14 +277,11 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No except ValueError: cookie["max-age"] = "" - else: - expires = cookie["expires"] - if expires: - expire_time = self._parse_date(expires) - if expire_time: - self._expire_cookie(expire_time, domain, path, name) - else: - cookie["expires"] = "" + elif expires := cookie["expires"]: + if expire_time := self._parse_date(expires): + self._expire_cookie(expire_time, domain, path, name) + else: + cookie["expires"] = "" self._cookies[(domain, path)][name] = cookie @@ -341,9 +337,8 @@ def filter_cookies(self, request_url: URL = URL()) -> "BaseCookie[str]": name = cookie.key domain = cookie["domain"] - if (domain, name) in self._host_only_cookies: - if domain != hostname: - continue + if (domain, name) in self._host_only_cookies and domain != hostname: + continue # Skip edge case when the cookie has a trailing slash but request doesn't. if len(cookie["path"]) > path_len: From d0a55c28cc55be94b74d51b6660e7bd59a12f10e Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 17:50:51 +0000 Subject: [PATCH 254/296] [PR #9221/e079c413 backport][3.11] Small cleanups to cookiejar (#9228) Co-authored-by: J. Nick Koston --- aiohttp/cookiejar.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/aiohttp/cookiejar.py b/aiohttp/cookiejar.py index 241a9e3e0a4..448ab7aa7e0 100644 --- a/aiohttp/cookiejar.py +++ b/aiohttp/cookiejar.py @@ -238,7 +238,7 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No domain = cookie["domain"] # ignore domains with trailing dots - if domain.endswith("."): + if domain and domain[-1] == ".": domain = "" del cookie["domain"] @@ -248,7 +248,7 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No self._host_only_cookies.add((hostname, name)) domain = cookie["domain"] = hostname - if domain.startswith("."): + if domain and domain[0] == ".": # Remove leading dot domain = domain[1:] cookie["domain"] = domain @@ -258,7 +258,7 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No continue path = cookie["path"] - if not path or not path.startswith("/"): + if not path or path[0] != "/": # Set the cookie's path to the response path path = response_url.path if not path.startswith("/"): @@ -269,8 +269,7 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No cookie["path"] = path path = path.rstrip("/") - max_age = cookie["max-age"] - if max_age: + if max_age := cookie["max-age"]: try: delta_seconds = int(max_age) max_age_expiration = min(time.time() + delta_seconds, self.MAX_TIME) @@ -278,14 +277,11 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No except ValueError: cookie["max-age"] = "" - else: - expires = cookie["expires"] - if expires: - expire_time = self._parse_date(expires) - if expire_time: - self._expire_cookie(expire_time, domain, path, name) - else: - cookie["expires"] = "" + elif expires := cookie["expires"]: + if expire_time := self._parse_date(expires): + self._expire_cookie(expire_time, domain, path, name) + else: + cookie["expires"] = "" self._cookies[(domain, path)][name] = cookie @@ -341,9 +337,8 @@ def filter_cookies(self, request_url: URL = URL()) -> "BaseCookie[str]": name = cookie.key domain = cookie["domain"] - if (domain, name) in self._host_only_cookies: - if domain != hostname: - continue + if (domain, name) in self._host_only_cookies and domain != hostname: + continue # Skip edge case when the cookie has a trailing slash but request doesn't. if len(cookie["path"]) > path_len: From 16b4bc4e081d19a6d6ee14850ce38576d8fdef03 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 18:10:59 +0000 Subject: [PATCH 255/296] [PR #8983/ceb7ae51 backport][3.10] Add more info to add_static() warning (#9229) Co-authored-by: Sam Bull Fixes #7232. --- docs/web_reference.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/web_reference.rst b/docs/web_reference.rst index 4efba726fa9..06c1c03f598 100644 --- a/docs/web_reference.rst +++ b/docs/web_reference.rst @@ -1878,7 +1878,10 @@ Application and Router Use :meth:`add_static` for development only. In production, static content should be processed by web servers like *nginx* - or *apache*. + or *apache*. Such web servers will be able to provide significantly + better performance and security for static assets. Several past security + vulnerabilities in aiohttp only affected applications using + :meth:`add_static`. :param str prefix: URL path prefix for handled static files From 75459a5ac68947b48c8782a91d0a6a6cc6438d87 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 18:11:02 +0000 Subject: [PATCH 256/296] [PR #8983/ceb7ae51 backport][3.11] Add more info to add_static() warning (#9230) Co-authored-by: Sam Bull Fixes #7232. --- docs/web_reference.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/web_reference.rst b/docs/web_reference.rst index f0da3237bd0..931210894c8 100644 --- a/docs/web_reference.rst +++ b/docs/web_reference.rst @@ -1881,7 +1881,10 @@ Application and Router Use :meth:`add_static` for development only. In production, static content should be processed by web servers like *nginx* - or *apache*. + or *apache*. Such web servers will be able to provide significantly + better performance and security for static assets. Several past security + vulnerabilities in aiohttp only affected applications using + :meth:`add_static`. :param str prefix: URL path prefix for handled static files From 066e69a8eba6e0b2777099824bb4146d7dc6ea09 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 14:11:06 -0400 Subject: [PATCH 257/296] [PR #9200/42930b0 backport][3.11] Improve middleware performance (#9232) --- CHANGES/9200.breaking.rst | 3 +++ aiohttp/web_middlewares.py | 7 ++++++- aiohttp/web_urldispatcher.py | 10 ++-------- 3 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 CHANGES/9200.breaking.rst diff --git a/CHANGES/9200.breaking.rst b/CHANGES/9200.breaking.rst new file mode 100644 index 00000000000..0282e165c41 --- /dev/null +++ b/CHANGES/9200.breaking.rst @@ -0,0 +1,3 @@ +Improved middleware performance -- by :user:`bdraco`. + +The ``set_current_app`` method was removed from ``UrlMappingMatchInfo`` because it is no longer used, and it was unlikely external caller would ever use it. diff --git a/aiohttp/web_middlewares.py b/aiohttp/web_middlewares.py index 5da1533c0df..2f1f5f58e6e 100644 --- a/aiohttp/web_middlewares.py +++ b/aiohttp/web_middlewares.py @@ -110,7 +110,12 @@ async def impl(request: Request, handler: Handler) -> StreamResponse: def _fix_request_current_app(app: "Application") -> Middleware: @middleware async def impl(request: Request, handler: Handler) -> StreamResponse: - with request.match_info.set_current_app(app): + match_info = request.match_info + prev = match_info.current_app + match_info.current_app = app + try: return await handler(request) + finally: + match_info.current_app = prev return impl diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 07c8f6e6ff3..9c07f4ee9ad 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -10,7 +10,6 @@ import re import sys import warnings -from contextlib import contextmanager from functools import wraps from pathlib import Path from types import MappingProxyType @@ -293,8 +292,8 @@ def current_app(self) -> "Application": assert app is not None return app - @contextmanager - def set_current_app(self, app: "Application") -> Generator[None, None, None]: + @current_app.setter + def current_app(self, app: "Application") -> None: if DEBUG: # pragma: no cover if app not in self._apps: raise RuntimeError( @@ -302,12 +301,7 @@ def set_current_app(self, app: "Application") -> Generator[None, None, None]: self._apps, app ) ) - prev = self._current_app self._current_app = app - try: - yield - finally: - self._current_app = prev def freeze(self) -> None: self._frozen = True From febf52528877c88df7db5fbfc53bf581dff05772 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 14:11:19 -0400 Subject: [PATCH 258/296] [PR #9200/42930b0 backport][3.10] Improve middleware performance (#9231) --- CHANGES/9200.breaking.rst | 3 +++ aiohttp/web_middlewares.py | 7 ++++++- aiohttp/web_urldispatcher.py | 10 ++-------- 3 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 CHANGES/9200.breaking.rst diff --git a/CHANGES/9200.breaking.rst b/CHANGES/9200.breaking.rst new file mode 100644 index 00000000000..0282e165c41 --- /dev/null +++ b/CHANGES/9200.breaking.rst @@ -0,0 +1,3 @@ +Improved middleware performance -- by :user:`bdraco`. + +The ``set_current_app`` method was removed from ``UrlMappingMatchInfo`` because it is no longer used, and it was unlikely external caller would ever use it. diff --git a/aiohttp/web_middlewares.py b/aiohttp/web_middlewares.py index 5da1533c0df..2f1f5f58e6e 100644 --- a/aiohttp/web_middlewares.py +++ b/aiohttp/web_middlewares.py @@ -110,7 +110,12 @@ async def impl(request: Request, handler: Handler) -> StreamResponse: def _fix_request_current_app(app: "Application") -> Middleware: @middleware async def impl(request: Request, handler: Handler) -> StreamResponse: - with request.match_info.set_current_app(app): + match_info = request.match_info + prev = match_info.current_app + match_info.current_app = app + try: return await handler(request) + finally: + match_info.current_app = prev return impl diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index c302351500b..0f6d1b2bcd6 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -10,7 +10,6 @@ import re import sys import warnings -from contextlib import contextmanager from functools import wraps from pathlib import Path from types import MappingProxyType @@ -293,8 +292,8 @@ def current_app(self) -> "Application": assert app is not None return app - @contextmanager - def set_current_app(self, app: "Application") -> Generator[None, None, None]: + @current_app.setter + def current_app(self, app: "Application") -> None: if DEBUG: # pragma: no cover if app not in self._apps: raise RuntimeError( @@ -302,12 +301,7 @@ def set_current_app(self, app: "Application") -> Generator[None, None, None]: self._apps, app ) ) - prev = self._current_app self._current_app = app - try: - yield - finally: - self._current_app = prev def freeze(self) -> None: self._frozen = True From 871fedcb014c0e568f4eed3ef5c74ae94c59c936 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 14:14:08 -0400 Subject: [PATCH 259/296] [PR #9033/d0e60d5 backport][3.11] Don't listen on TCP when only path is passed (#9233) Co-authored-by: Sam Bull --- CHANGES/9033.misc.rst | 1 + aiohttp/web.py | 18 ++++++++++++------ docs/web_quickstart.rst | 4 ++++ tests/test_web_cli.py | 32 +++++++++++++++++++++++++++++++- 4 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 CHANGES/9033.misc.rst diff --git a/CHANGES/9033.misc.rst b/CHANGES/9033.misc.rst new file mode 100644 index 00000000000..07a017ffdda --- /dev/null +++ b/CHANGES/9033.misc.rst @@ -0,0 +1 @@ +Changed web entry point to not listen on TCP when only a Unix path is passed -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web.py b/aiohttp/web.py index 1d18691f401..f975b665331 100644 --- a/aiohttp/web.py +++ b/aiohttp/web.py @@ -545,21 +545,21 @@ def main(argv: List[str]) -> None: arg_parser.add_argument( "-H", "--hostname", - help="TCP/IP hostname to serve on (default: %(default)r)", - default="localhost", + help="TCP/IP hostname to serve on (default: localhost)", + default=None, ) arg_parser.add_argument( "-P", "--port", help="TCP/IP port to serve on (default: %(default)r)", type=int, - default="8080", + default=8080, ) arg_parser.add_argument( "-U", "--path", - help="Unix file system path to serve on. Specifying a path will cause " - "hostname and port arguments to be ignored.", + help="Unix file system path to serve on. Can be combined with hostname " + "to serve on both Unix and TCP.", ) args, extra_argv = arg_parser.parse_known_args(argv) @@ -586,8 +586,14 @@ def main(argv: List[str]) -> None: logging.basicConfig(level=logging.DEBUG) + if args.path and args.hostname is None: + host = port = None + else: + host = args.hostname or "localhost" + port = args.port + app = func(extra_argv) - run_app(app, host=args.hostname, port=args.port, path=args.path) + run_app(app, host=host, port=port, path=args.path) arg_parser.exit(message="Stopped\n") diff --git a/docs/web_quickstart.rst b/docs/web_quickstart.rst index fcd2b686d06..2d8c3368b82 100644 --- a/docs/web_quickstart.rst +++ b/docs/web_quickstart.rst @@ -85,6 +85,10 @@ accepts a list of any non-parsed command-line arguments and returns an return app +.. note:: + For local development we typically recommend using + `aiohttp-devtools `_. + .. _aiohttp-web-handler: Handler diff --git a/tests/test_web_cli.py b/tests/test_web_cli.py index 381aaf6cd82..b320ed35667 100644 --- a/tests/test_web_cli.py +++ b/tests/test_web_cli.py @@ -1,3 +1,7 @@ +import sys +from typing import Any +from unittest import mock + import pytest from aiohttp import web @@ -80,7 +84,33 @@ def test_entry_func_non_existent_attribute(mocker) -> None: ) -def test_path_when_unsupported(mocker, monkeypatch) -> None: +@pytest.mark.skipif(sys.platform.startswith("win32"), reason="Windows not Unix") +def test_path_no_host(mocker: Any, monkeypatch: Any) -> None: + argv = "--path=test_path.sock alpha.beta:func".split() + mocker.patch("aiohttp.web.import_module") + + run_app = mocker.patch("aiohttp.web.run_app") + with pytest.raises(SystemExit): + web.main(argv) + + run_app.assert_called_with(mock.ANY, path="test_path.sock", host=None, port=None) + + +@pytest.mark.skipif(sys.platform.startswith("win32"), reason="Windows not Unix") +def test_path_and_host(mocker: Any, monkeypatch: Any) -> None: + argv = "--path=test_path.sock --host=localhost --port=8000 alpha.beta:func".split() + mocker.patch("aiohttp.web.import_module") + + run_app = mocker.patch("aiohttp.web.run_app") + with pytest.raises(SystemExit): + web.main(argv) + + run_app.assert_called_with( + mock.ANY, path="test_path.sock", host="localhost", port=8000 + ) + + +def test_path_when_unsupported(mocker: Any, monkeypatch: Any) -> None: argv = "--path=test_path.sock alpha.beta:func".split() mocker.patch("aiohttp.web.import_module") monkeypatch.delattr("socket.AF_UNIX", raising=False) From d8c040e72adfcb9e37c154f59e77ff485e75eee0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 14:18:44 -0400 Subject: [PATCH 260/296] [PR #9018/63813fe backport][3.10] Reject data after close message (#9235) Co-authored-by: pre-commit-ci[bot] Co-authored-by: Sam Bull --- CHANGES/9018.bugfix.rst | 1 + aiohttp/http_parser.py | 5 +++++ tests/test_http_parser.py | 11 ++++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 CHANGES/9018.bugfix.rst diff --git a/CHANGES/9018.bugfix.rst b/CHANGES/9018.bugfix.rst new file mode 100644 index 00000000000..2de6d142900 --- /dev/null +++ b/CHANGES/9018.bugfix.rst @@ -0,0 +1 @@ +Updated Python parser to reject messages after a close message, matching C parser behaviour -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/http_parser.py b/aiohttp/http_parser.py index f46cf833c03..686a2d02e28 100644 --- a/aiohttp/http_parser.py +++ b/aiohttp/http_parser.py @@ -317,6 +317,7 @@ def feed_data( start_pos = 0 loop = self.loop + should_close = False while start_pos < data_len: # read HTTP message (request/response line + headers), \r\n\r\n @@ -329,6 +330,9 @@ def feed_data( continue if pos >= start_pos: + if should_close: + raise BadHttpMessage("Data after `Connection: close`") + # line found line = data[start_pos:pos] if SEP == b"\n": # For lax response parsing @@ -438,6 +442,7 @@ def get_content_length() -> Optional[int]: payload = EMPTY_PAYLOAD messages.append((msg, payload)) + should_close = msg.should_close else: self._tail = data[start_pos:] data = EMPTY diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py index 78abe528cb0..09f4f0746a5 100644 --- a/tests/test_http_parser.py +++ b/tests/test_http_parser.py @@ -17,6 +17,7 @@ NO_EXTENSIONS, DeflateBuffer, HttpPayloadParser, + HttpRequestParser, HttpRequestParserPy, HttpResponseParserPy, HttpVersion, @@ -826,7 +827,15 @@ def test_http_request_bad_status_line_whitespace(parser: Any) -> None: parser.feed_data(text) -def test_http_request_upgrade(parser: Any) -> None: +def test_http_request_message_after_close(parser: HttpRequestParser) -> None: + text = b"GET / HTTP/1.1\r\nConnection: close\r\n\r\nInvalid\r\n\r\n" + with pytest.raises( + http_exceptions.BadHttpMessage, match="Data after `Connection: close`" + ): + parser.feed_data(text) + + +def test_http_request_upgrade(parser: HttpRequestParser) -> None: text = ( b"GET /test HTTP/1.1\r\n" b"connection: upgrade\r\n" From d2b52d7c32a744379438c466e68ad2cb471deeba Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 14:27:52 -0400 Subject: [PATCH 261/296] [PR #9018/63813fe backport][3.11] Reject data after close message (#9236) Co-authored-by: pre-commit-ci[bot] Co-authored-by: Sam Bull --- CHANGES/9018.bugfix.rst | 1 + aiohttp/http_parser.py | 5 +++++ tests/test_http_parser.py | 11 ++++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 CHANGES/9018.bugfix.rst diff --git a/CHANGES/9018.bugfix.rst b/CHANGES/9018.bugfix.rst new file mode 100644 index 00000000000..2de6d142900 --- /dev/null +++ b/CHANGES/9018.bugfix.rst @@ -0,0 +1 @@ +Updated Python parser to reject messages after a close message, matching C parser behaviour -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/http_parser.py b/aiohttp/http_parser.py index f46cf833c03..686a2d02e28 100644 --- a/aiohttp/http_parser.py +++ b/aiohttp/http_parser.py @@ -317,6 +317,7 @@ def feed_data( start_pos = 0 loop = self.loop + should_close = False while start_pos < data_len: # read HTTP message (request/response line + headers), \r\n\r\n @@ -329,6 +330,9 @@ def feed_data( continue if pos >= start_pos: + if should_close: + raise BadHttpMessage("Data after `Connection: close`") + # line found line = data[start_pos:pos] if SEP == b"\n": # For lax response parsing @@ -438,6 +442,7 @@ def get_content_length() -> Optional[int]: payload = EMPTY_PAYLOAD messages.append((msg, payload)) + should_close = msg.should_close else: self._tail = data[start_pos:] data = EMPTY diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py index 75276df1a07..edd676190f6 100644 --- a/tests/test_http_parser.py +++ b/tests/test_http_parser.py @@ -17,6 +17,7 @@ NO_EXTENSIONS, DeflateBuffer, HttpPayloadParser, + HttpRequestParser, HttpRequestParserPy, HttpResponseParserPy, HttpVersion, @@ -826,7 +827,15 @@ def test_http_request_bad_status_line_whitespace(parser: Any) -> None: parser.feed_data(text) -def test_http_request_upgrade(parser: Any) -> None: +def test_http_request_message_after_close(parser: HttpRequestParser) -> None: + text = b"GET / HTTP/1.1\r\nConnection: close\r\n\r\nInvalid\r\n\r\n" + with pytest.raises( + http_exceptions.BadHttpMessage, match="Data after `Connection: close`" + ): + parser.feed_data(text) + + +def test_http_request_upgrade(parser: HttpRequestParser) -> None: text = ( b"GET /test HTTP/1.1\r\n" b"connection: upgrade\r\n" From 6fbe6771b95eb54cef11eefe12b5d35eef31777e Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 18:28:25 +0000 Subject: [PATCH 262/296] [PR #9063/3c1ca455 backport][3.10] Fix If-None-Match not using weak comparison (#9237) Co-authored-by: Sam Bull Fixes #8462. --- CHANGES/9063.bugfix.rst | 1 + aiohttp/web_fileresponse.py | 17 +++++++++---- tests/test_web_sendfile_functional.py | 36 +++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 CHANGES/9063.bugfix.rst diff --git a/CHANGES/9063.bugfix.rst b/CHANGES/9063.bugfix.rst new file mode 100644 index 00000000000..e512677b9c8 --- /dev/null +++ b/CHANGES/9063.bugfix.rst @@ -0,0 +1 @@ +Fixed ``If-None-Match`` not using weak comparison -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_fileresponse.py b/aiohttp/web_fileresponse.py index 0c23e375d25..f0de75e9f1b 100644 --- a/aiohttp/web_fileresponse.py +++ b/aiohttp/web_fileresponse.py @@ -136,10 +136,12 @@ async def _sendfile( return writer @staticmethod - def _strong_etag_match(etag_value: str, etags: Tuple[ETag, ...]) -> bool: + def _etag_match(etag_value: str, etags: Tuple[ETag, ...], *, weak: bool) -> bool: if len(etags) == 1 and etags[0].value == ETAG_ANY: return True - return any(etag.value == etag_value for etag in etags if not etag.is_weak) + return any( + etag.value == etag_value for etag in etags if weak or not etag.is_weak + ) async def _not_modified( self, request: "BaseRequest", etag_value: str, last_modified: float @@ -208,9 +210,11 @@ async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter etag_value = f"{st.st_mtime_ns:x}-{st.st_size:x}" last_modified = st.st_mtime - # https://tools.ietf.org/html/rfc7232#section-6 + # https://www.rfc-editor.org/rfc/rfc9110#section-13.1.1-2 ifmatch = request.if_match - if ifmatch is not None and not self._strong_etag_match(etag_value, ifmatch): + if ifmatch is not None and not self._etag_match( + etag_value, ifmatch, weak=False + ): return await self._precondition_failed(request) unmodsince = request.if_unmodified_since @@ -221,8 +225,11 @@ async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter ): return await self._precondition_failed(request) + # https://www.rfc-editor.org/rfc/rfc9110#section-13.1.2-2 ifnonematch = request.if_none_match - if ifnonematch is not None and self._strong_etag_match(etag_value, ifnonematch): + if ifnonematch is not None and self._etag_match( + etag_value, ifnonematch, weak=True + ): return await self._not_modified(request, etag_value, last_modified) modsince = request.if_modified_since diff --git a/tests/test_web_sendfile_functional.py b/tests/test_web_sendfile_functional.py index c9189a21fb0..256cf4d243a 100644 --- a/tests/test_web_sendfile_functional.py +++ b/tests/test_web_sendfile_functional.py @@ -513,10 +513,9 @@ async def test_static_file_if_none_match( resp = await client.get("/") assert 200 == resp.status - original_etag = resp.headers.get("ETag") + original_etag = resp.headers["ETag"] assert resp.headers.get("Last-Modified") is not None - assert original_etag is not None resp.close() await resp.release() @@ -555,6 +554,39 @@ async def test_static_file_if_none_match_star( await client.close() +@pytest.mark.parametrize("if_modified_since", ("", "Fri, 31 Dec 9999 23:59:59 GMT")) +async def test_static_file_if_none_match_weak( + aiohttp_client: Any, + app_with_static_route: web.Application, + if_modified_since: str, +) -> None: + client = await aiohttp_client(app_with_static_route) + + resp = await client.get("/") + assert 200 == resp.status + original_etag = resp.headers["ETag"] + + assert resp.headers.get("Last-Modified") is not None + resp.close() + resp.release() + + weak_etag = f"W/{original_etag}" + + resp = await client.get( + "/", + headers={"If-None-Match": weak_etag, "If-Modified-Since": if_modified_since}, + ) + body = await resp.read() + assert 304 == resp.status + assert resp.headers.get("Content-Length") is None + assert resp.headers.get("ETag") == original_etag + assert b"" == body + resp.close() + resp.release() + + await client.close() + + @pytest.mark.skipif(not ssl, reason="ssl not supported") async def test_static_file_ssl( aiohttp_server, From 51870a9d1fd13cc9393e803795428bfba3999606 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 18:40:32 +0000 Subject: [PATCH 263/296] [PR #9063/3c1ca455 backport][3.11] Fix If-None-Match not using weak comparison (#9238) Co-authored-by: Sam Bull Fixes #8462. --- CHANGES/9063.bugfix.rst | 1 + aiohttp/web_fileresponse.py | 17 +++++++++---- tests/test_web_sendfile_functional.py | 36 +++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 CHANGES/9063.bugfix.rst diff --git a/CHANGES/9063.bugfix.rst b/CHANGES/9063.bugfix.rst new file mode 100644 index 00000000000..e512677b9c8 --- /dev/null +++ b/CHANGES/9063.bugfix.rst @@ -0,0 +1 @@ +Fixed ``If-None-Match`` not using weak comparison -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web_fileresponse.py b/aiohttp/web_fileresponse.py index 2c253e03b0a..e7951acea16 100644 --- a/aiohttp/web_fileresponse.py +++ b/aiohttp/web_fileresponse.py @@ -132,10 +132,12 @@ async def _sendfile( return writer @staticmethod - def _strong_etag_match(etag_value: str, etags: Tuple[ETag, ...]) -> bool: + def _etag_match(etag_value: str, etags: Tuple[ETag, ...], *, weak: bool) -> bool: if len(etags) == 1 and etags[0].value == ETAG_ANY: return True - return any(etag.value == etag_value for etag in etags if not etag.is_weak) + return any( + etag.value == etag_value for etag in etags if weak or not etag.is_weak + ) async def _not_modified( self, request: "BaseRequest", etag_value: str, last_modified: float @@ -204,9 +206,11 @@ async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter etag_value = f"{st.st_mtime_ns:x}-{st.st_size:x}" last_modified = st.st_mtime - # https://tools.ietf.org/html/rfc7232#section-6 + # https://www.rfc-editor.org/rfc/rfc9110#section-13.1.1-2 ifmatch = request.if_match - if ifmatch is not None and not self._strong_etag_match(etag_value, ifmatch): + if ifmatch is not None and not self._etag_match( + etag_value, ifmatch, weak=False + ): return await self._precondition_failed(request) unmodsince = request.if_unmodified_since @@ -217,8 +221,11 @@ async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter ): return await self._precondition_failed(request) + # https://www.rfc-editor.org/rfc/rfc9110#section-13.1.2-2 ifnonematch = request.if_none_match - if ifnonematch is not None and self._strong_etag_match(etag_value, ifnonematch): + if ifnonematch is not None and self._etag_match( + etag_value, ifnonematch, weak=True + ): return await self._not_modified(request, etag_value, last_modified) modsince = request.if_modified_since diff --git a/tests/test_web_sendfile_functional.py b/tests/test_web_sendfile_functional.py index c9189a21fb0..256cf4d243a 100644 --- a/tests/test_web_sendfile_functional.py +++ b/tests/test_web_sendfile_functional.py @@ -513,10 +513,9 @@ async def test_static_file_if_none_match( resp = await client.get("/") assert 200 == resp.status - original_etag = resp.headers.get("ETag") + original_etag = resp.headers["ETag"] assert resp.headers.get("Last-Modified") is not None - assert original_etag is not None resp.close() await resp.release() @@ -555,6 +554,39 @@ async def test_static_file_if_none_match_star( await client.close() +@pytest.mark.parametrize("if_modified_since", ("", "Fri, 31 Dec 9999 23:59:59 GMT")) +async def test_static_file_if_none_match_weak( + aiohttp_client: Any, + app_with_static_route: web.Application, + if_modified_since: str, +) -> None: + client = await aiohttp_client(app_with_static_route) + + resp = await client.get("/") + assert 200 == resp.status + original_etag = resp.headers["ETag"] + + assert resp.headers.get("Last-Modified") is not None + resp.close() + resp.release() + + weak_etag = f"W/{original_etag}" + + resp = await client.get( + "/", + headers={"If-None-Match": weak_etag, "If-Modified-Since": if_modified_since}, + ) + body = await resp.read() + assert 304 == resp.status + assert resp.headers.get("Content-Length") is None + assert resp.headers.get("ETag") == original_etag + assert b"" == body + resp.close() + resp.release() + + await client.close() + + @pytest.mark.skipif(not ssl, reason="ssl not supported") async def test_static_file_ssl( aiohttp_server, From 255ec0281a55992c5521f449786e0f9e491b3a73 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 21:19:45 +0000 Subject: [PATCH 264/296] [PR #9204/34a626da backport][3.10] Significantly speed up filter_cookies (#9243) Co-authored-by: J. Nick Koston --- CHANGES/9204.misc.rst | 1 + aiohttp/cookiejar.py | 53 +++++++++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 CHANGES/9204.misc.rst diff --git a/CHANGES/9204.misc.rst b/CHANGES/9204.misc.rst new file mode 100644 index 00000000000..da12a7df6f7 --- /dev/null +++ b/CHANGES/9204.misc.rst @@ -0,0 +1 @@ +Significantly speed up filtering cookies -- by :user:`bdraco`. diff --git a/aiohttp/cookiejar.py b/aiohttp/cookiejar.py index 0a6e35461f7..c78d5fa7e72 100644 --- a/aiohttp/cookiejar.py +++ b/aiohttp/cookiejar.py @@ -95,6 +95,9 @@ def __init__( self._cookies: DefaultDict[Tuple[str, str], SimpleCookie] = defaultdict( SimpleCookie ) + self._morsel_cache: DefaultDict[Tuple[str, str], Dict[str, Morsel[str]]] = ( + defaultdict(dict) + ) self._host_only_cookies: Set[Tuple[str, str]] = set() self._unsafe = unsafe self._quote_cookie = quote_cookie @@ -127,6 +130,7 @@ def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None: if predicate is None: self._expire_heap.clear() self._cookies.clear() + self._morsel_cache.clear() self._host_only_cookies.clear() self._expirations.clear() return @@ -208,6 +212,7 @@ def _delete_cookies(self, to_del: List[Tuple[str, str, str]]) -> None: for domain, path, name in to_del: self._host_only_cookies.discard((domain, name)) self._cookies[(domain, path)].pop(name, None) + self._morsel_cache[(domain, path)].pop(name, None) self._expirations.pop((domain, path, name), None) def _expire_cookie(self, when: float, domain: str, path: str, name: str) -> None: @@ -283,7 +288,12 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No else: cookie["expires"] = "" - self._cookies[(domain, path)][name] = cookie + key = (domain, path) + if self._cookies[key].get(name) != cookie: + # Don't blow away the cache if the same + # cookie gets set again + self._cookies[key][name] = cookie + self._morsel_cache[key].pop(name, None) self._do_expiration() @@ -328,30 +338,33 @@ def filter_cookies(self, request_url: URL = URL()) -> "BaseCookie[str]": # Create every combination of (domain, path) pairs. pairs = itertools.product(domains, paths) - # Point 2: https://www.rfc-editor.org/rfc/rfc6265.html#section-5.4 - cookies = itertools.chain.from_iterable( - self._cookies[p].values() for p in pairs - ) path_len = len(request_url.path) - for cookie in cookies: - name = cookie.key - domain = cookie["domain"] + # Point 2: https://www.rfc-editor.org/rfc/rfc6265.html#section-5.4 + for p in pairs: + for name, cookie in self._cookies[p].items(): + domain = cookie["domain"] - if (domain, name) in self._host_only_cookies and domain != hostname: - continue + if (domain, name) in self._host_only_cookies and domain != hostname: + continue - # Skip edge case when the cookie has a trailing slash but request doesn't. - if len(cookie["path"]) > path_len: - continue + # Skip edge case when the cookie has a trailing slash but request doesn't. + if len(cookie["path"]) > path_len: + continue - if is_not_secure and cookie["secure"]: - continue + if is_not_secure and cookie["secure"]: + continue + + # We already built the Morsel so reuse it here + if name in self._morsel_cache[p]: + filtered[name] = self._morsel_cache[p][name] + continue - # It's critical we use the Morsel so the coded_value - # (based on cookie version) is preserved - mrsl_val = cast("Morsel[str]", cookie.get(cookie.key, Morsel())) - mrsl_val.set(cookie.key, cookie.value, cookie.coded_value) - filtered[name] = mrsl_val + # It's critical we use the Morsel so the coded_value + # (based on cookie version) is preserved + mrsl_val = cast("Morsel[str]", cookie.get(cookie.key, Morsel())) + mrsl_val.set(cookie.key, cookie.value, cookie.coded_value) + self._morsel_cache[p][name] = mrsl_val + filtered[name] = mrsl_val return filtered From 643369bd304fc6528a11e4c7494f7a70540020e2 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 21:19:52 +0000 Subject: [PATCH 265/296] [PR #9204/34a626da backport][3.11] Significantly speed up filter_cookies (#9244) Co-authored-by: J. Nick Koston --- CHANGES/9204.misc.rst | 1 + aiohttp/cookiejar.py | 53 +++++++++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 CHANGES/9204.misc.rst diff --git a/CHANGES/9204.misc.rst b/CHANGES/9204.misc.rst new file mode 100644 index 00000000000..da12a7df6f7 --- /dev/null +++ b/CHANGES/9204.misc.rst @@ -0,0 +1 @@ +Significantly speed up filtering cookies -- by :user:`bdraco`. diff --git a/aiohttp/cookiejar.py b/aiohttp/cookiejar.py index 448ab7aa7e0..3ffa4198fc7 100644 --- a/aiohttp/cookiejar.py +++ b/aiohttp/cookiejar.py @@ -95,6 +95,9 @@ def __init__( self._cookies: DefaultDict[Tuple[str, str], SimpleCookie] = defaultdict( SimpleCookie ) + self._morsel_cache: DefaultDict[Tuple[str, str], Dict[str, Morsel[str]]] = ( + defaultdict(dict) + ) self._host_only_cookies: Set[Tuple[str, str]] = set() self._unsafe = unsafe self._quote_cookie = quote_cookie @@ -127,6 +130,7 @@ def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None: if predicate is None: self._expire_heap.clear() self._cookies.clear() + self._morsel_cache.clear() self._host_only_cookies.clear() self._expirations.clear() return @@ -208,6 +212,7 @@ def _delete_cookies(self, to_del: List[Tuple[str, str, str]]) -> None: for domain, path, name in to_del: self._host_only_cookies.discard((domain, name)) self._cookies[(domain, path)].pop(name, None) + self._morsel_cache[(domain, path)].pop(name, None) self._expirations.pop((domain, path, name), None) def _expire_cookie(self, when: float, domain: str, path: str, name: str) -> None: @@ -283,7 +288,12 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No else: cookie["expires"] = "" - self._cookies[(domain, path)][name] = cookie + key = (domain, path) + if self._cookies[key].get(name) != cookie: + # Don't blow away the cache if the same + # cookie gets set again + self._cookies[key][name] = cookie + self._morsel_cache[key].pop(name, None) self._do_expiration() @@ -328,30 +338,33 @@ def filter_cookies(self, request_url: URL = URL()) -> "BaseCookie[str]": # Create every combination of (domain, path) pairs. pairs = itertools.product(domains, paths) - # Point 2: https://www.rfc-editor.org/rfc/rfc6265.html#section-5.4 - cookies = itertools.chain.from_iterable( - self._cookies[p].values() for p in pairs - ) path_len = len(request_url.path) - for cookie in cookies: - name = cookie.key - domain = cookie["domain"] + # Point 2: https://www.rfc-editor.org/rfc/rfc6265.html#section-5.4 + for p in pairs: + for name, cookie in self._cookies[p].items(): + domain = cookie["domain"] - if (domain, name) in self._host_only_cookies and domain != hostname: - continue + if (domain, name) in self._host_only_cookies and domain != hostname: + continue - # Skip edge case when the cookie has a trailing slash but request doesn't. - if len(cookie["path"]) > path_len: - continue + # Skip edge case when the cookie has a trailing slash but request doesn't. + if len(cookie["path"]) > path_len: + continue - if is_not_secure and cookie["secure"]: - continue + if is_not_secure and cookie["secure"]: + continue + + # We already built the Morsel so reuse it here + if name in self._morsel_cache[p]: + filtered[name] = self._morsel_cache[p][name] + continue - # It's critical we use the Morsel so the coded_value - # (based on cookie version) is preserved - mrsl_val = cast("Morsel[str]", cookie.get(cookie.key, Morsel())) - mrsl_val.set(cookie.key, cookie.value, cookie.coded_value) - filtered[name] = mrsl_val + # It's critical we use the Morsel so the coded_value + # (based on cookie version) is preserved + mrsl_val = cast("Morsel[str]", cookie.get(cookie.key, Morsel())) + mrsl_val.set(cookie.key, cookie.value, cookie.coded_value) + self._morsel_cache[p][name] = mrsl_val + filtered[name] = mrsl_val return filtered From 8b5a90c407766615476d921f2e63d475b7f0e2cb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 17:15:54 -0500 Subject: [PATCH 266/296] [PR #9234/0246f2d backport][3.11] Remove unnecessary string operations in update_content_encoding (#9246) --- aiohttp/client_reqrep.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 3fe34e21968..10144f2a9c4 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -499,8 +499,7 @@ def update_content_encoding(self, data: Any) -> None: self.compress = None return - enc = self.headers.get(hdrs.CONTENT_ENCODING, "").lower() - if enc: + if self.headers.get(hdrs.CONTENT_ENCODING): if self.compress: raise ValueError( "compress can not be set if Content-Encoding header is set" From 595a01d99693e282be8cff12eabad6ff5e2824b5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 17:16:07 -0500 Subject: [PATCH 267/296] [PR #9234/0246f2d backport][3.10] Remove unnecessary string operations in update_content_encoding (#9247) --- aiohttp/client_reqrep.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 57f3323a60c..cfe44c3c563 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -508,8 +508,7 @@ def update_content_encoding(self, data: Any) -> None: self.compress = None return - enc = self.headers.get(hdrs.CONTENT_ENCODING, "").lower() - if enc: + if self.headers.get(hdrs.CONTENT_ENCODING): if self.compress: raise ValueError( "compress can not be set " "if Content-Encoding header is set" From b31b82de5ee25774cfa2d1dcd9c46bec09041dda Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 22 Sep 2024 23:18:39 +0100 Subject: [PATCH 268/296] Add Generic for TestClient.app (#8977) (#9245) Co-authored-by: J. Nick Koston (cherry picked from commit 6db2c747572b3e77a2d64e3f6c5a01ecae74fcff) --- CHANGES/8977.bugfix.rst | 1 + aiohttp/pytest_plugin.py | 41 +++++++++++++++++++++------ aiohttp/test_utils.py | 49 +++++++++++++++++++++++++++++---- tests/test_client_functional.py | 2 +- tests/test_test_utils.py | 49 +++++++++++++++++++++------------ 5 files changed, 108 insertions(+), 34 deletions(-) create mode 100644 CHANGES/8977.bugfix.rst diff --git a/CHANGES/8977.bugfix.rst b/CHANGES/8977.bugfix.rst new file mode 100644 index 00000000000..7d21fe0c3fa --- /dev/null +++ b/CHANGES/8977.bugfix.rst @@ -0,0 +1 @@ +Made ``TestClient.app`` a ``Generic`` so type checkers will know the correct type (avoiding unneeded ``client.app is not None`` checks) -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/pytest_plugin.py b/aiohttp/pytest_plugin.py index 55964ead041..6da4852ab46 100644 --- a/aiohttp/pytest_plugin.py +++ b/aiohttp/pytest_plugin.py @@ -12,6 +12,7 @@ Protocol, Type, Union, + overload, ) import pytest @@ -26,7 +27,7 @@ teardown_test_loop, unused_port as _unused_port, ) -from .web import Application +from .web import Application, BaseRequest, Request from .web_protocol import _RequestHandler try: @@ -36,13 +37,22 @@ class AiohttpClient(Protocol): - def __call__( + @overload + async def __call__( self, - __param: Union[Application, BaseTestServer], + __param: Application, *, server_kwargs: Optional[Dict[str, Any]] = None, **kwargs: Any - ) -> Awaitable[TestClient]: ... + ) -> TestClient[Request, Application]: ... + @overload + async def __call__( + self, + __param: BaseTestServer, + *, + server_kwargs: Optional[Dict[str, Any]] = None, + **kwargs: Any + ) -> TestClient[BaseRequest, None]: ... class AiohttpServer(Protocol): @@ -355,9 +365,7 @@ def raw_test_server( # type: ignore[no-untyped-def] # pragma: no cover @pytest.fixture -def aiohttp_client( - loop: asyncio.AbstractEventLoop, -) -> Iterator[AiohttpClient]: +def aiohttp_client(loop: asyncio.AbstractEventLoop) -> Iterator[AiohttpClient]: """Factory to create a TestClient instance. aiohttp_client(app, **kwargs) @@ -366,13 +374,28 @@ def aiohttp_client( """ clients = [] + @overload + async def go( + __param: Application, + *, + server_kwargs: Optional[Dict[str, Any]] = None, + **kwargs: Any + ) -> TestClient[Request, Application]: ... + + @overload + async def go( + __param: BaseTestServer, + *, + server_kwargs: Optional[Dict[str, Any]] = None, + **kwargs: Any + ) -> TestClient[BaseRequest, None]: ... + async def go( __param: Union[Application, BaseTestServer], *args: Any, server_kwargs: Optional[Dict[str, Any]] = None, **kwargs: Any - ) -> TestClient: - + ) -> TestClient[Any, Any]: if isinstance(__param, Callable) and not isinstance( # type: ignore[arg-type] __param, (Application, BaseTestServer) ): diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py index 5ab3381f9e6..a85662b9fb2 100644 --- a/aiohttp/test_utils.py +++ b/aiohttp/test_utils.py @@ -11,7 +11,19 @@ import warnings from abc import ABC, abstractmethod from types import TracebackType -from typing import TYPE_CHECKING, Any, Callable, Iterator, List, Optional, Type, cast +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Generic, + Iterator, + List, + Optional, + Type, + TypeVar, + cast, + overload, +) from unittest import IsolatedAsyncioTestCase, mock from aiosignal import Signal @@ -36,6 +48,7 @@ from .web import ( Application, AppRunner, + BaseRequest, BaseRunner, Request, Server, @@ -53,6 +66,14 @@ if sys.version_info >= (3, 11) and TYPE_CHECKING: from typing import Unpack +if sys.version_info >= (3, 11): + from typing import Self +else: + Self = Any + +_ApplicationNone = TypeVar("_ApplicationNone", Application, None) +_Request = TypeVar("_Request", bound=BaseRequest) + REUSE_ADDRESS = os.name == "posix" and sys.platform != "cygwin" @@ -249,7 +270,7 @@ async def _make_runner(self, debug: bool = True, **kwargs: Any) -> ServerRunner: return ServerRunner(srv, debug=debug, **kwargs) -class TestClient: +class TestClient(Generic[_Request, _ApplicationNone]): """ A test client implementation. @@ -259,6 +280,22 @@ class TestClient: __test__ = False + @overload + def __init__( + self: "TestClient[Request, Application]", + server: TestServer, + *, + cookie_jar: Optional[AbstractCookieJar] = None, + **kwargs: Any, + ) -> None: ... + @overload + def __init__( + self: "TestClient[_Request, None]", + server: BaseTestServer, + *, + cookie_jar: Optional[AbstractCookieJar] = None, + **kwargs: Any, + ) -> None: ... def __init__( self, server: BaseTestServer, @@ -296,8 +333,8 @@ def server(self) -> BaseTestServer: return self._server @property - def app(self) -> Optional[Application]: - return cast(Optional[Application], getattr(self._server, "app", None)) + def app(self) -> _ApplicationNone: + return getattr(self._server, "app", None) # type: ignore[return-value] @property def session(self) -> ClientSession: @@ -465,7 +502,7 @@ def __exit__( # __exit__ should exist in pair with __enter__ but never executed pass # pragma: no cover - async def __aenter__(self) -> "TestClient": + async def __aenter__(self) -> Self: await self.start_server() return self @@ -530,7 +567,7 @@ async def get_server(self, app: Application) -> TestServer: """Return a TestServer instance.""" return TestServer(app, loop=self.loop) - async def get_client(self, server: TestServer) -> TestClient: + async def get_client(self, server: TestServer) -> TestClient[Request, Application]: """Return a TestClient instance.""" return TestClient(server, loop=self.loop) diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 70c5bf16096..137164c7d0b 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -3699,7 +3699,7 @@ async def handler(request): await resp.write(b"1" * 1000) await asyncio.sleep(0.01) - async def request(client): + async def request(client: TestClient[web.Request, web.Application]) -> None: timeout = aiohttp.ClientTimeout(total=0.5) async with await client.get("/", timeout=timeout) as resp: with pytest.raises(asyncio.TimeoutError): diff --git a/tests/test_test_utils.py b/tests/test_test_utils.py index a9c5179aedc..70d74fb69f0 100644 --- a/tests/test_test_utils.py +++ b/tests/test_test_utils.py @@ -1,6 +1,8 @@ +import asyncio import gzip +import sys from socket import socket -from typing import Any +from typing import Any, Iterator from unittest import mock import pytest @@ -12,14 +14,19 @@ from aiohttp.test_utils import ( AioHTTPTestCase, RawTestServer as _RawTestServer, - TestClient as _TestClient, - TestServer as _TestServer, + TestClient, + TestServer, get_port_socket, loop_context, make_mocked_request, unittest_run_loop, ) +if sys.version_info >= (3, 11): + from typing import assert_type + +_TestClient = TestClient[web.Request, web.Application] + _hello_world_str = "Hello, world" _hello_world_bytes = _hello_world_str.encode("utf-8") _hello_world_gz = gzip.compress(_hello_world_bytes) @@ -67,9 +74,11 @@ def app(): @pytest.fixture -def test_client(loop, app) -> None: - async def make_client(): - return _TestClient(_TestServer(app, loop=loop), loop=loop) +def test_client( + loop: asyncio.AbstractEventLoop, app: web.Application +) -> Iterator[_TestClient]: + async def make_client() -> TestClient[web.Request, web.Application]: + return TestClient(TestServer(app)) client = loop.run_until_complete(make_client()) @@ -81,14 +90,14 @@ async def make_client(): def test_with_test_server_fails(loop) -> None: app = _create_example_app() with pytest.raises(TypeError): - with _TestServer(app, loop=loop): + with TestServer(app, loop=loop): pass async def test_with_client_fails(loop) -> None: app = _create_example_app() with pytest.raises(TypeError): - with _TestClient(_TestServer(app, loop=loop), loop=loop): + with _TestClient(TestServer(app, loop=loop), loop=loop): pass @@ -96,7 +105,7 @@ async def test_aiohttp_client_close_is_idempotent() -> None: # a test client, called multiple times, should # not attempt to close the server again. app = _create_example_app() - client = _TestClient(_TestServer(app)) + client = _TestClient(TestServer(app)) await client.close() await client.close() @@ -252,12 +261,14 @@ def test_make_mocked_request_transport() -> None: async def test_test_client_props(loop) -> None: app = _create_example_app() - client = _TestClient(_TestServer(app, host="127.0.0.1", loop=loop), loop=loop) + client = _TestClient(TestServer(app, host="127.0.0.1", loop=loop), loop=loop) assert client.host == "127.0.0.1" assert client.port is None async with client: assert isinstance(client.port, int) assert client.server is not None + if sys.version_info >= (3, 11): + assert_type(client.app, web.Application) assert client.app is not None assert client.port is None @@ -272,13 +283,15 @@ async def hello(request): async with client: assert isinstance(client.port, int) assert client.server is not None + if sys.version_info >= (3, 11): + assert_type(client.app, None) assert client.app is None assert client.port is None async def test_test_server_context_manager(loop) -> None: app = _create_example_app() - async with _TestServer(app, loop=loop) as server: + async with TestServer(app, loop=loop) as server: client = aiohttp.ClientSession(loop=loop) resp = await client.head(server.make_url("/")) assert resp.status == 200 @@ -288,7 +301,7 @@ async def test_test_server_context_manager(loop) -> None: def test_client_unsupported_arg() -> None: with pytest.raises(TypeError) as e: - _TestClient("string") + TestClient("string") # type: ignore[call-overload] assert ( str(e.value) == "server must be TestServer instance, found type: " @@ -297,7 +310,7 @@ def test_client_unsupported_arg() -> None: async def test_server_make_url_yarl_compatibility(loop) -> None: app = _create_example_app() - async with _TestServer(app, loop=loop) as server: + async with TestServer(app, loop=loop) as server: make_url = server.make_url assert make_url(URL("/foo")) == make_url("/foo") with pytest.raises(AssertionError): @@ -322,7 +335,7 @@ def test_noop(self) -> None: async def test_server_context_manager(app, loop) -> None: - async with _TestServer(app, loop=loop) as server: + async with TestServer(app, loop=loop) as server: async with aiohttp.ClientSession(loop=loop) as client: async with client.head(server.make_url("/")) as resp: assert resp.status == 200 @@ -332,7 +345,7 @@ async def test_server_context_manager(app, loop) -> None: "method", ["head", "get", "post", "options", "post", "put", "patch", "delete"] ) async def test_client_context_manager_response(method, app, loop) -> None: - async with _TestClient(_TestServer(app), loop=loop) as client: + async with _TestClient(TestServer(app), loop=loop) as client: async with getattr(client, method)("/") as resp: assert resp.status == 200 if method != "head": @@ -342,7 +355,7 @@ async def test_client_context_manager_response(method, app, loop) -> None: async def test_custom_port(loop, app, aiohttp_unused_port) -> None: port = aiohttp_unused_port() - client = _TestClient(_TestServer(app, loop=loop, port=port), loop=loop) + client = _TestClient(TestServer(app, loop=loop, port=port), loop=loop) await client.start_server() assert client.server.port == port @@ -355,7 +368,7 @@ async def test_custom_port(loop, app, aiohttp_unused_port) -> None: await client.close() -@pytest.mark.parametrize("test_server_cls", [_TestServer, _RawTestServer]) +@pytest.mark.parametrize("test_server_cls", [TestServer, _RawTestServer]) async def test_base_test_server_socket_factory( test_server_cls: type, app: Any, loop: Any ) -> None: @@ -379,7 +392,7 @@ def factory(*args, **kwargs) -> socket: ) async def test_test_server_hostnames(hostname, expected_host, loop) -> None: app = _create_example_app() - server = _TestServer(app, host=hostname, loop=loop) + server = TestServer(app, host=hostname, loop=loop) async with server: pass assert server.host == expected_host From 8c20e730b2a2f7a8cf9f0b46ef48c90ab60ffea4 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 22:22:51 +0000 Subject: [PATCH 269/296] [PR #9242/76c6010f backport][3.10] Remove duplicate checks in the StreamWriter (#9248) Co-authored-by: J. Nick Koston --- aiohttp/http_writer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index f54fa0f0774..dc07a358c70 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -71,8 +71,8 @@ def _write(self, chunk: bytes) -> None: size = len(chunk) self.buffer_size += size self.output_size += size - transport = self.transport - if not self._protocol.connected or transport is None or transport.is_closing(): + transport = self._protocol.transport + if transport is None or transport.is_closing(): raise ClientConnectionResetError("Cannot write to closing transport") transport.write(chunk) From ae4ba606bf6b968dd71d6b3b22c5001b1e5656fc Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 22:23:21 +0000 Subject: [PATCH 270/296] [PR #9242/76c6010f backport][3.11] Remove duplicate checks in the StreamWriter (#9249) Co-authored-by: J. Nick Koston --- aiohttp/http_writer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index f54fa0f0774..dc07a358c70 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -71,8 +71,8 @@ def _write(self, chunk: bytes) -> None: size = len(chunk) self.buffer_size += size self.output_size += size - transport = self.transport - if not self._protocol.connected or transport is None or transport.is_closing(): + transport = self._protocol.transport + if transport is None or transport.is_closing(): raise ClientConnectionResetError("Cannot write to closing transport") transport.write(chunk) From b823b4ecd452cd43317522fc30fa6f5742ff89f7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 18:24:26 -0500 Subject: [PATCH 271/296] [PR #9240/9604461 backport][3.11] Small cleanups to Application._handle (#9251) --- aiohttp/web_app.py | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index b59d0d1b0ff..c29f32df413 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -533,32 +533,30 @@ async def _handle(self, request: Request) -> StreamResponse: match_info.freeze() - resp = None request._match_info = match_info - expect = request.headers.get(hdrs.EXPECT) - if expect: + + if request.headers.get(hdrs.EXPECT): resp = await match_info.expect_handler(request) await request.writer.drain() + if resp is not None: + return resp + + handler = match_info.handler - if resp is None: - handler = match_info.handler - - if self._run_middlewares: - if not self._has_legacy_middlewares: - handler = _build_middlewares(handler, match_info.apps) - else: - for app in match_info.apps[::-1]: - for m, new_style in app._middlewares_handlers: # type: ignore[union-attr] - if new_style: - handler = update_wrapper( - partial(m, handler=handler), handler # type: ignore[misc] - ) - else: - handler = await m(app, handler) # type: ignore[arg-type,assignment] - - resp = await handler(request) - - return resp + if self._run_middlewares: + if not self._has_legacy_middlewares: + handler = _build_middlewares(handler, match_info.apps) + else: + for app in match_info.apps[::-1]: + for m, new_style in app._middlewares_handlers: # type: ignore[union-attr] + if new_style: + handler = update_wrapper( + partial(m, handler=handler), handler # type: ignore[misc] + ) + else: + handler = await m(app, handler) # type: ignore[arg-type,assignment] + + return await handler(request) def __call__(self) -> "Application": """gunicorn compatibility""" From 2abb604c01b8ca551e8fa69758094bebd39acb54 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 18:24:26 -0500 Subject: [PATCH 272/296] [PR #9240/9604461 backport][3.10] Small cleanups to Application._handle (#9250) --- aiohttp/web_app.py | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index c4199b12271..78b1a67bacc 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -533,32 +533,30 @@ async def _handle(self, request: Request) -> StreamResponse: match_info.freeze() - resp = None request._match_info = match_info - expect = request.headers.get(hdrs.EXPECT) - if expect: + + if request.headers.get(hdrs.EXPECT): resp = await match_info.expect_handler(request) await request.writer.drain() + if resp is not None: + return resp + + handler = match_info.handler - if resp is None: - handler = match_info.handler - - if self._run_middlewares: - if not self._has_legacy_middlewares: - handler = _build_middlewares(handler, match_info.apps) - else: - for app in match_info.apps[::-1]: - for m, new_style in app._middlewares_handlers: # type: ignore[union-attr] - if new_style: - handler = update_wrapper( - partial(m, handler=handler), handler # type: ignore[misc] - ) - else: - handler = await m(app, handler) # type: ignore[arg-type,assignment] - - resp = await handler(request) - - return resp + if self._run_middlewares: + if not self._has_legacy_middlewares: + handler = _build_middlewares(handler, match_info.apps) + else: + for app in match_info.apps[::-1]: + for m, new_style in app._middlewares_handlers: # type: ignore[union-attr] + if new_style: + handler = update_wrapper( + partial(m, handler=handler), handler # type: ignore[misc] + ) + else: + handler = await m(app, handler) # type: ignore[arg-type,assignment] + + return await handler(request) def __call__(self) -> "Application": """gunicorn compatibility""" From 80730b5eb73a5e550401a31f126425fd429e6494 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 19:00:28 -0500 Subject: [PATCH 273/296] [PR #9241/7e0ef07 backport][3.11] Small speed up to starting web requests (#9253) --- CHANGES/9241.misc.rst | 1 + aiohttp/web_protocol.py | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 120000 CHANGES/9241.misc.rst diff --git a/CHANGES/9241.misc.rst b/CHANGES/9241.misc.rst new file mode 120000 index 00000000000..d6a2f2aaaab --- /dev/null +++ b/CHANGES/9241.misc.rst @@ -0,0 +1 @@ +9174.misc.rst \ No newline at end of file diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index dd819de7236..8fa8535b93a 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -27,7 +27,7 @@ from .abc import AbstractAccessLogger, AbstractStreamWriter from .base_protocol import BaseProtocol -from .helpers import ceil_timeout, set_exception +from .helpers import ceil_timeout from .http import ( HttpProcessingError, HttpRequestParser, @@ -84,6 +84,9 @@ class PayloadAccessError(Exception): """Payload was accessed after response was sent.""" +_PAYLOAD_ACCESS_ERROR = PayloadAccessError() + + @attr.s(auto_attribs=True, frozen=True, slots=True) class _ErrInfo: status: int @@ -578,7 +581,7 @@ async def start(self) -> None: self.log_debug("Uncompleted request.") self.close() - set_exception(payload, PayloadAccessError()) + payload.set_exception(_PAYLOAD_ACCESS_ERROR) except asyncio.CancelledError: self.log_debug("Ignored premature client disconnection ") From 76284b5abbe1d78f1813d6568b0611fe4843173c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 19:00:34 -0500 Subject: [PATCH 274/296] [PR #9241/7e0ef07 backport][3.10] Small speed up to starting web requests (#9252) --- CHANGES/9241.misc.rst | 1 + aiohttp/web_protocol.py | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 120000 CHANGES/9241.misc.rst diff --git a/CHANGES/9241.misc.rst b/CHANGES/9241.misc.rst new file mode 120000 index 00000000000..d6a2f2aaaab --- /dev/null +++ b/CHANGES/9241.misc.rst @@ -0,0 +1 @@ +9174.misc.rst \ No newline at end of file diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index dd819de7236..8fa8535b93a 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -27,7 +27,7 @@ from .abc import AbstractAccessLogger, AbstractStreamWriter from .base_protocol import BaseProtocol -from .helpers import ceil_timeout, set_exception +from .helpers import ceil_timeout from .http import ( HttpProcessingError, HttpRequestParser, @@ -84,6 +84,9 @@ class PayloadAccessError(Exception): """Payload was accessed after response was sent.""" +_PAYLOAD_ACCESS_ERROR = PayloadAccessError() + + @attr.s(auto_attribs=True, frozen=True, slots=True) class _ErrInfo: status: int @@ -578,7 +581,7 @@ async def start(self) -> None: self.log_debug("Uncompleted request.") self.close() - set_exception(payload, PayloadAccessError()) + payload.set_exception(_PAYLOAD_ACCESS_ERROR) except asyncio.CancelledError: self.log_debug("Ignored premature client disconnection ") From 7ecc9c943104db7185fda7d51aa14749e4ba2992 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 23 Sep 2024 01:55:50 +0100 Subject: [PATCH 275/296] Stop raise_for_status() releasing when in a context (#9239) (#9255) (cherry picked from commit 63cfd5f96b3ce819c7feac67dfa4183ac4731acd) --- CHANGES/9239.bugfix.rst | 1 + aiohttp/client.py | 32 ++++++------------------- aiohttp/client_reqrep.py | 10 +++++++- aiohttp/client_ws.py | 14 ++++++++++- tests/test_client_functional.py | 41 +++++++++++++++++++++++++++++++-- 5 files changed, 69 insertions(+), 29 deletions(-) create mode 100644 CHANGES/9239.bugfix.rst diff --git a/CHANGES/9239.bugfix.rst b/CHANGES/9239.bugfix.rst new file mode 100644 index 00000000000..95b229742ce --- /dev/null +++ b/CHANGES/9239.bugfix.rst @@ -0,0 +1 @@ +Changed :py:meth:`ClientResponse.raise_for_status() ` to only release the connection when invoked outside an ``async with`` context -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/client.py b/aiohttp/client.py index 443335c6061..c893b06bb11 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -218,7 +218,7 @@ class ClientTimeout: # https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2 IDEMPOTENT_METHODS = frozenset({"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"}) -_RetType = TypeVar("_RetType") +_RetType = TypeVar("_RetType", ClientResponse, ClientWebSocketResponse) _CharsetResolver = Callable[[ClientResponse, bytes], str] @@ -1364,7 +1364,7 @@ class _BaseRequestContextManager(Coroutine[Any, Any, _RetType], Generic[_RetType __slots__ = ("_coro", "_resp") def __init__(self, coro: Coroutine["asyncio.Future[Any]", None, _RetType]) -> None: - self._coro = coro + self._coro: Coroutine["asyncio.Future[Any]", None, _RetType] = coro def send(self, arg: None) -> "asyncio.Future[Any]": return self._coro.send(arg) @@ -1383,12 +1383,8 @@ def __iter__(self) -> Generator[Any, None, _RetType]: return self.__await__() async def __aenter__(self) -> _RetType: - self._resp = await self._coro - return self._resp - - -class _RequestContextManager(_BaseRequestContextManager[ClientResponse]): - __slots__ = () + self._resp: _RetType = await self._coro + return await self._resp.__aenter__() async def __aexit__( self, @@ -1396,25 +1392,11 @@ async def __aexit__( exc: Optional[BaseException], tb: Optional[TracebackType], ) -> None: - # We're basing behavior on the exception as it can be caused by - # user code unrelated to the status of the connection. If you - # would like to close a connection you must do that - # explicitly. Otherwise connection error handling should kick in - # and close/recycle the connection as required. - self._resp.release() - await self._resp.wait_for_close() + await self._resp.__aexit__(exc_type, exc, tb) -class _WSRequestContextManager(_BaseRequestContextManager[ClientWebSocketResponse]): - __slots__ = () - - async def __aexit__( - self, - exc_type: Optional[Type[BaseException]], - exc: Optional[BaseException], - tb: Optional[TracebackType], - ) -> None: - await self._resp.close() +_RequestContextManager = _BaseRequestContextManager[ClientResponse] +_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse] class _SessionRequestContextManager: diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 10144f2a9c4..09b8659264e 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -805,6 +805,7 @@ class ClientResponse(HeadersMixin): # post-init stage allows to not change ctor signature _closed = True # to allow __del__ for non-initialized properly response _released = False + _in_context = False __writer = None def __init__( @@ -1094,7 +1095,12 @@ def raise_for_status(self) -> None: if not self.ok: # reason should always be not None for a started response assert self.reason is not None - self.release() + + # If we're in a context we can rely on __aexit__() to release as the + # exception propagates. + if not self._in_context: + self.release() + raise ClientResponseError( self.request_info, self.history, @@ -1221,6 +1227,7 @@ async def json( return loads(stripped.decode(encoding)) async def __aenter__(self) -> "ClientResponse": + self._in_context = True return self async def __aexit__( @@ -1229,6 +1236,7 @@ async def __aexit__( exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: + self._in_context = False # similar to _RequestContextManager, we do not need to check # for exceptions, response object can close connection # if state is broken diff --git a/aiohttp/client_ws.py b/aiohttp/client_ws.py index 6246234b8e0..58409ed71e5 100644 --- a/aiohttp/client_ws.py +++ b/aiohttp/client_ws.py @@ -2,7 +2,8 @@ import asyncio import sys -from typing import Any, Optional, cast +from types import TracebackType +from typing import Any, Optional, Type, cast import attr @@ -393,3 +394,14 @@ async def __anext__(self) -> WSMessage: if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED): raise StopAsyncIteration return msg + + async def __aenter__(self) -> "ClientWebSocketResponse": + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + await self.close() diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 137164c7d0b..d39addc29a1 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -23,6 +23,7 @@ from aiohttp import Fingerprint, ServerFingerprintMismatch, hdrs, web from aiohttp.abc import AbstractResolver from aiohttp.client_exceptions import ( + ClientResponseError, InvalidURL, InvalidUrlClientError, InvalidUrlRedirectClientError, @@ -3592,8 +3593,44 @@ async def handler(request): await resp.read() -async def test_read_from_closed_content(aiohttp_client) -> None: - async def handler(request): +async def test_read_after_catch_raise_for_status(aiohttp_client: AiohttpClient) -> None: + async def handler(request: web.Request) -> web.Response: + return web.Response(body=b"data", status=404) + + app = web.Application() + app.add_routes([web.get("/", handler)]) + + client = await aiohttp_client(app) + + async with client.get("/") as resp: + with pytest.raises(ClientResponseError, match="404"): + # Should not release response when in async with context. + resp.raise_for_status() + + result = await resp.read() + assert result == b"data" + + +async def test_read_after_raise_outside_context(aiohttp_client: AiohttpClient) -> None: + async def handler(request: web.Request) -> web.Response: + return web.Response(body=b"data", status=404) + + app = web.Application() + app.add_routes([web.get("/", handler)]) + + client = await aiohttp_client(app) + + resp = await client.get("/") + with pytest.raises(ClientResponseError, match="404"): + # No async with, so should release and therefore read() will fail. + resp.raise_for_status() + + with pytest.raises(aiohttp.ClientConnectionError, match=r"^Connection closed$"): + await resp.read() + + +async def test_read_from_closed_content(aiohttp_client: AiohttpClient) -> None: + async def handler(request: web.Request) -> web.Response: return web.Response(body=b"data") app = web.Application() From 5fe63257ad49813a753ba5f0591779eafe7c4e29 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 23 Sep 2024 01:56:24 +0100 Subject: [PATCH 276/296] Stop raise_for_status() releasing when in a context (#9239) (#9256) (cherry picked from commit 63cfd5f96b3ce819c7feac67dfa4183ac4731acd) --- CHANGES/9239.bugfix.rst | 1 + aiohttp/client.py | 32 ++++++------------------- aiohttp/client_reqrep.py | 10 +++++++- aiohttp/client_ws.py | 14 ++++++++++- tests/test_client_functional.py | 41 +++++++++++++++++++++++++++++++-- 5 files changed, 69 insertions(+), 29 deletions(-) create mode 100644 CHANGES/9239.bugfix.rst diff --git a/CHANGES/9239.bugfix.rst b/CHANGES/9239.bugfix.rst new file mode 100644 index 00000000000..95b229742ce --- /dev/null +++ b/CHANGES/9239.bugfix.rst @@ -0,0 +1 @@ +Changed :py:meth:`ClientResponse.raise_for_status() ` to only release the connection when invoked outside an ``async with`` context -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/client.py b/aiohttp/client.py index 61bea70aa9b..da89ee2a790 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -213,7 +213,7 @@ class ClientTimeout: # https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2 IDEMPOTENT_METHODS = frozenset({"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"}) -_RetType = TypeVar("_RetType") +_RetType = TypeVar("_RetType", ClientResponse, ClientWebSocketResponse) _CharsetResolver = Callable[[ClientResponse, bytes], str] @@ -1333,7 +1333,7 @@ class _BaseRequestContextManager(Coroutine[Any, Any, _RetType], Generic[_RetType __slots__ = ("_coro", "_resp") def __init__(self, coro: Coroutine["asyncio.Future[Any]", None, _RetType]) -> None: - self._coro = coro + self._coro: Coroutine["asyncio.Future[Any]", None, _RetType] = coro def send(self, arg: None) -> "asyncio.Future[Any]": return self._coro.send(arg) @@ -1352,12 +1352,8 @@ def __iter__(self) -> Generator[Any, None, _RetType]: return self.__await__() async def __aenter__(self) -> _RetType: - self._resp = await self._coro - return self._resp - - -class _RequestContextManager(_BaseRequestContextManager[ClientResponse]): - __slots__ = () + self._resp: _RetType = await self._coro + return await self._resp.__aenter__() async def __aexit__( self, @@ -1365,25 +1361,11 @@ async def __aexit__( exc: Optional[BaseException], tb: Optional[TracebackType], ) -> None: - # We're basing behavior on the exception as it can be caused by - # user code unrelated to the status of the connection. If you - # would like to close a connection you must do that - # explicitly. Otherwise connection error handling should kick in - # and close/recycle the connection as required. - self._resp.release() - await self._resp.wait_for_close() + await self._resp.__aexit__(exc_type, exc, tb) -class _WSRequestContextManager(_BaseRequestContextManager[ClientWebSocketResponse]): - __slots__ = () - - async def __aexit__( - self, - exc_type: Optional[Type[BaseException]], - exc: Optional[BaseException], - tb: Optional[TracebackType], - ) -> None: - await self._resp.close() +_RequestContextManager = _BaseRequestContextManager[ClientResponse] +_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse] class _SessionRequestContextManager: diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index cfe44c3c563..a80d28f7fd8 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -814,6 +814,7 @@ class ClientResponse(HeadersMixin): # post-init stage allows to not change ctor signature _closed = True # to allow __del__ for non-initialized properly response _released = False + _in_context = False __writer = None def __init__( @@ -1103,7 +1104,12 @@ def raise_for_status(self) -> None: if not self.ok: # reason should always be not None for a started response assert self.reason is not None - self.release() + + # If we're in a context we can rely on __aexit__() to release as the + # exception propagates. + if not self._in_context: + self.release() + raise ClientResponseError( self.request_info, self.history, @@ -1230,6 +1236,7 @@ async def json( return loads(stripped.decode(encoding)) async def __aenter__(self) -> "ClientResponse": + self._in_context = True return self async def __aexit__( @@ -1238,6 +1245,7 @@ async def __aexit__( exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: + self._in_context = False # similar to _RequestContextManager, we do not need to check # for exceptions, response object can close connection # if state is broken diff --git a/aiohttp/client_ws.py b/aiohttp/client_ws.py index 7b3a5bf952d..c6b5da5103b 100644 --- a/aiohttp/client_ws.py +++ b/aiohttp/client_ws.py @@ -2,7 +2,8 @@ import asyncio import sys -from typing import Any, Optional, cast +from types import TracebackType +from typing import Any, Optional, Type, cast from .client_exceptions import ClientError, ServerTimeoutError from .client_reqrep import ClientResponse @@ -384,3 +385,14 @@ async def __anext__(self) -> WSMessage: if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED): raise StopAsyncIteration return msg + + async def __aenter__(self) -> "ClientWebSocketResponse": + return self + + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + await self.close() diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index 082db6f3e9a..60af4930f14 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -23,6 +23,7 @@ from aiohttp import Fingerprint, ServerFingerprintMismatch, client_reqrep, hdrs, web from aiohttp.abc import AbstractResolver from aiohttp.client_exceptions import ( + ClientResponseError, InvalidURL, InvalidUrlClientError, InvalidUrlRedirectClientError, @@ -3474,8 +3475,44 @@ async def handler(request): await resp.read() -async def test_read_from_closed_content(aiohttp_client) -> None: - async def handler(request): +async def test_read_after_catch_raise_for_status(aiohttp_client: AiohttpClient) -> None: + async def handler(request: web.Request) -> web.Response: + return web.Response(body=b"data", status=404) + + app = web.Application() + app.add_routes([web.get("/", handler)]) + + client = await aiohttp_client(app) + + async with client.get("/") as resp: + with pytest.raises(ClientResponseError, match="404"): + # Should not release response when in async with context. + resp.raise_for_status() + + result = await resp.read() + assert result == b"data" + + +async def test_read_after_raise_outside_context(aiohttp_client: AiohttpClient) -> None: + async def handler(request: web.Request) -> web.Response: + return web.Response(body=b"data", status=404) + + app = web.Application() + app.add_routes([web.get("/", handler)]) + + client = await aiohttp_client(app) + + resp = await client.get("/") + with pytest.raises(ClientResponseError, match="404"): + # No async with, so should release and therefore read() will fail. + resp.raise_for_status() + + with pytest.raises(aiohttp.ClientConnectionError, match=r"^Connection closed$"): + await resp.read() + + +async def test_read_from_closed_content(aiohttp_client: AiohttpClient) -> None: + async def handler(request: web.Request) -> web.Response: return web.Response(body=b"data") app = web.Application() From 3f1a8b10328ac8e65e87af9e66e61dcbc960ccb3 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 23 Sep 2024 02:34:30 +0100 Subject: [PATCH 277/296] Fix cancellations being swallowed (#9030) (#9257) Co-authored-by: J. Nick Koston (cherry picked from commit 1a77ad933f07ab0e7ba0c16f7ca8f02fa8ab044e) --- CHANGES/9030.bugfix.rst | 1 + aiohttp/client_reqrep.py | 37 +++++++++++++++++++++++++-------- aiohttp/web_protocol.py | 38 ++++++++++++++++++++++++++-------- tests/test_client_request.py | 19 ++++++++++++++++- tests/test_web_functional.py | 40 ++++++++++++++++++++++++++++++++++-- 5 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 CHANGES/9030.bugfix.rst diff --git a/CHANGES/9030.bugfix.rst b/CHANGES/9030.bugfix.rst new file mode 100644 index 00000000000..2e9d48f5359 --- /dev/null +++ b/CHANGES/9030.bugfix.rst @@ -0,0 +1 @@ +Fixed (on Python 3.11+) some edge cases where a task cancellation may get incorrectly suppressed -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 09b8659264e..627966dbca6 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -625,11 +625,8 @@ async def write_bytes( """Support coroutines that yields bytes objects.""" # 100 response if self._continue is not None: - try: - await writer.drain() - await self._continue - except asyncio.CancelledError: - return + await writer.drain() + await self._continue protocol = conn.protocol assert protocol is not None @@ -658,6 +655,7 @@ async def write_bytes( except asyncio.CancelledError: # Body hasn't been fully sent, so connection can't be reused. conn.close() + raise except Exception as underlying_exc: set_exception( protocol, @@ -764,8 +762,15 @@ async def send(self, conn: "Connection") -> "ClientResponse": async def close(self) -> None: if self._writer is not None: - with contextlib.suppress(asyncio.CancelledError): + try: await self._writer + except asyncio.CancelledError: + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise def terminate(self) -> None: if self._writer is not None: @@ -1119,7 +1124,15 @@ def _release_connection(self) -> None: async def _wait_released(self) -> None: if self._writer is not None: - await self._writer + try: + await self._writer + except asyncio.CancelledError: + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise self._release_connection() def _cleanup_writer(self) -> None: @@ -1135,7 +1148,15 @@ def _notify_content(self) -> None: async def wait_for_close(self) -> None: if self._writer is not None: - await self._writer + try: + await self._writer + except asyncio.CancelledError: + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise self.release() async def read(self) -> bytes: diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index 8fa8535b93a..85eb70d5a0b 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -271,17 +271,32 @@ async def shutdown(self, timeout: Optional[float] = 15.0) -> None: # down while the handler is still processing a request # to avoid creating a future for every request. self._handler_waiter = self._loop.create_future() - with suppress(asyncio.CancelledError, asyncio.TimeoutError): + try: async with ceil_timeout(timeout): await self._handler_waiter + except (asyncio.CancelledError, asyncio.TimeoutError): + self._handler_waiter = None + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise # Then cancel handler and wait - with suppress(asyncio.CancelledError, asyncio.TimeoutError): + try: async with ceil_timeout(timeout): if self._current_request is not None: self._current_request._cancel(asyncio.CancelledError()) if self._task_handler is not None and not self._task_handler.done(): - await self._task_handler + await asyncio.shield(self._task_handler) + except (asyncio.CancelledError, asyncio.TimeoutError): + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise # force-close non-idle handler if self._task_handler is not None: @@ -517,8 +532,6 @@ async def start(self) -> None: # wait for next request self._waiter = loop.create_future() await self._waiter - except asyncio.CancelledError: - break finally: self._waiter = None @@ -545,7 +558,7 @@ async def start(self) -> None: task = loop.create_task(coro) try: resp, reset = await task - except (asyncio.CancelledError, ConnectionError): + except ConnectionError: self.log_debug("Ignored premature client disconnection") break @@ -569,12 +582,19 @@ async def start(self) -> None: now = loop.time() end_t = now + lingering_time - with suppress(asyncio.TimeoutError, asyncio.CancelledError): + try: while not payload.is_eof() and now < end_t: async with ceil_timeout(end_t - now): # read and ignore await payload.readany() now = loop.time() + except (asyncio.CancelledError, asyncio.TimeoutError): + if ( + sys.version_info >= (3, 11) + and (t := asyncio.current_task()) + and t.cancelling() + ): + raise # if payload still uncompleted if not payload.is_eof() and not self._force_close: @@ -584,8 +604,8 @@ async def start(self) -> None: payload.set_exception(_PAYLOAD_ACCESS_ERROR) except asyncio.CancelledError: - self.log_debug("Ignored premature client disconnection ") - break + self.log_debug("Ignored premature client disconnection") + raise except Exception as exc: self.log_exception("Unhandled exception", exc_info=exc) self.force_close() diff --git a/tests/test_client_request.py b/tests/test_client_request.py index 2d70ebdd4f2..f2eff019504 100644 --- a/tests/test_client_request.py +++ b/tests/test_client_request.py @@ -2,6 +2,7 @@ import hashlib import io import pathlib +import sys import urllib.parse import zlib from http.cookies import BaseCookie, Morsel, SimpleCookie @@ -1213,7 +1214,23 @@ async def test_oserror_on_write_bytes(loop, conn) -> None: await req.close() -async def test_terminate(loop, conn) -> None: +@pytest.mark.skipif(sys.version_info < (3, 11), reason="Needs Task.cancelling()") +async def test_cancel_close(loop: asyncio.AbstractEventLoop, conn: mock.Mock) -> None: + req = ClientRequest("get", URL("http://python.org"), loop=loop) + req._writer = asyncio.Future() # type: ignore[assignment] + + t = asyncio.create_task(req.close()) + + # Start waiting on _writer + await asyncio.sleep(0) + + t.cancel() + # Cancellation should not be suppressed. + with pytest.raises(asyncio.CancelledError): + await t + + +async def test_terminate(loop: asyncio.AbstractEventLoop, conn: mock.Mock) -> None: req = ClientRequest("get", URL("http://python.org"), loop=loop) async def _mock_write_bytes(*args, **kwargs): diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index 5b2e5fe9353..e46a23c5857 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -3,6 +3,7 @@ import json import pathlib import socket +import sys import zlib from typing import Any, NoReturn, Optional from unittest import mock @@ -22,6 +23,7 @@ web, ) from aiohttp.hdrs import CONTENT_LENGTH, CONTENT_TYPE, TRANSFER_ENCODING +from aiohttp.pytest_plugin import AiohttpClient from aiohttp.test_utils import make_mocked_coro from aiohttp.typedefs import Handler from aiohttp.web_protocol import RequestHandler @@ -187,8 +189,42 @@ async def handler(request): await resp.release() -async def test_post_form(aiohttp_client) -> None: - async def handler(request): +@pytest.mark.skipif(sys.version_info < (3, 11), reason="Needs Task.cancelling()") +async def test_cancel_shutdown(aiohttp_client: AiohttpClient) -> None: + async def handler(request: web.Request) -> web.Response: + t = asyncio.create_task(request.protocol.shutdown()) + # Ensure it's started waiting + await asyncio.sleep(0) + + t.cancel() + # Cancellation should not be suppressed + with pytest.raises(asyncio.CancelledError): + await t + + # Repeat for second waiter in shutdown() + with mock.patch.object(request.protocol, "_request_in_progress", False): + with mock.patch.object(request.protocol, "_current_request", None): + t = asyncio.create_task(request.protocol.shutdown()) + await asyncio.sleep(0) + + t.cancel() + with pytest.raises(asyncio.CancelledError): + await t + + return web.Response(body=b"OK") + + app = web.Application() + app.router.add_get("/", handler) + client = await aiohttp_client(app) + + async with client.get("/") as resp: + assert resp.status == 200 + txt = await resp.text() + assert txt == "OK" + + +async def test_post_form(aiohttp_client: AiohttpClient) -> None: + async def handler(request: web.Request) -> web.Response: data = await request.post() assert {"a": "1", "b": "2", "c": ""} == data return web.Response(body=b"OK") From 9876a61beb4413c2ca720754291b71a79d2fce21 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Mon, 23 Sep 2024 02:34:44 +0100 Subject: [PATCH 278/296] Fix cancellations being swallowed (#9030) (#9258) Co-authored-by: J. Nick Koston (cherry picked from commit 1a77ad933f07ab0e7ba0c16f7ca8f02fa8ab044e) --- CHANGES/9030.bugfix.rst | 1 + aiohttp/client_reqrep.py | 37 +++++++++++++++++++++++++-------- aiohttp/web_protocol.py | 38 ++++++++++++++++++++++++++-------- tests/test_client_request.py | 19 ++++++++++++++++- tests/test_web_functional.py | 40 ++++++++++++++++++++++++++++++++++-- 5 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 CHANGES/9030.bugfix.rst diff --git a/CHANGES/9030.bugfix.rst b/CHANGES/9030.bugfix.rst new file mode 100644 index 00000000000..2e9d48f5359 --- /dev/null +++ b/CHANGES/9030.bugfix.rst @@ -0,0 +1 @@ +Fixed (on Python 3.11+) some edge cases where a task cancellation may get incorrectly suppressed -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index a80d28f7fd8..aa8f54e67b8 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -634,11 +634,8 @@ async def write_bytes( """Support coroutines that yields bytes objects.""" # 100 response if self._continue is not None: - try: - await writer.drain() - await self._continue - except asyncio.CancelledError: - return + await writer.drain() + await self._continue protocol = conn.protocol assert protocol is not None @@ -667,6 +664,7 @@ async def write_bytes( except asyncio.CancelledError: # Body hasn't been fully sent, so connection can't be reused. conn.close() + raise except Exception as underlying_exc: set_exception( protocol, @@ -773,8 +771,15 @@ async def send(self, conn: "Connection") -> "ClientResponse": async def close(self) -> None: if self._writer is not None: - with contextlib.suppress(asyncio.CancelledError): + try: await self._writer + except asyncio.CancelledError: + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise def terminate(self) -> None: if self._writer is not None: @@ -1128,7 +1133,15 @@ def _release_connection(self) -> None: async def _wait_released(self) -> None: if self._writer is not None: - await self._writer + try: + await self._writer + except asyncio.CancelledError: + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise self._release_connection() def _cleanup_writer(self) -> None: @@ -1144,7 +1157,15 @@ def _notify_content(self) -> None: async def wait_for_close(self) -> None: if self._writer is not None: - await self._writer + try: + await self._writer + except asyncio.CancelledError: + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise self.release() async def read(self) -> bytes: diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index 8fa8535b93a..85eb70d5a0b 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -271,17 +271,32 @@ async def shutdown(self, timeout: Optional[float] = 15.0) -> None: # down while the handler is still processing a request # to avoid creating a future for every request. self._handler_waiter = self._loop.create_future() - with suppress(asyncio.CancelledError, asyncio.TimeoutError): + try: async with ceil_timeout(timeout): await self._handler_waiter + except (asyncio.CancelledError, asyncio.TimeoutError): + self._handler_waiter = None + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise # Then cancel handler and wait - with suppress(asyncio.CancelledError, asyncio.TimeoutError): + try: async with ceil_timeout(timeout): if self._current_request is not None: self._current_request._cancel(asyncio.CancelledError()) if self._task_handler is not None and not self._task_handler.done(): - await self._task_handler + await asyncio.shield(self._task_handler) + except (asyncio.CancelledError, asyncio.TimeoutError): + if ( + sys.version_info >= (3, 11) + and (task := asyncio.current_task()) + and task.cancelling() + ): + raise # force-close non-idle handler if self._task_handler is not None: @@ -517,8 +532,6 @@ async def start(self) -> None: # wait for next request self._waiter = loop.create_future() await self._waiter - except asyncio.CancelledError: - break finally: self._waiter = None @@ -545,7 +558,7 @@ async def start(self) -> None: task = loop.create_task(coro) try: resp, reset = await task - except (asyncio.CancelledError, ConnectionError): + except ConnectionError: self.log_debug("Ignored premature client disconnection") break @@ -569,12 +582,19 @@ async def start(self) -> None: now = loop.time() end_t = now + lingering_time - with suppress(asyncio.TimeoutError, asyncio.CancelledError): + try: while not payload.is_eof() and now < end_t: async with ceil_timeout(end_t - now): # read and ignore await payload.readany() now = loop.time() + except (asyncio.CancelledError, asyncio.TimeoutError): + if ( + sys.version_info >= (3, 11) + and (t := asyncio.current_task()) + and t.cancelling() + ): + raise # if payload still uncompleted if not payload.is_eof() and not self._force_close: @@ -584,8 +604,8 @@ async def start(self) -> None: payload.set_exception(_PAYLOAD_ACCESS_ERROR) except asyncio.CancelledError: - self.log_debug("Ignored premature client disconnection ") - break + self.log_debug("Ignored premature client disconnection") + raise except Exception as exc: self.log_exception("Unhandled exception", exc_info=exc) self.force_close() diff --git a/tests/test_client_request.py b/tests/test_client_request.py index 2d70ebdd4f2..f2eff019504 100644 --- a/tests/test_client_request.py +++ b/tests/test_client_request.py @@ -2,6 +2,7 @@ import hashlib import io import pathlib +import sys import urllib.parse import zlib from http.cookies import BaseCookie, Morsel, SimpleCookie @@ -1213,7 +1214,23 @@ async def test_oserror_on_write_bytes(loop, conn) -> None: await req.close() -async def test_terminate(loop, conn) -> None: +@pytest.mark.skipif(sys.version_info < (3, 11), reason="Needs Task.cancelling()") +async def test_cancel_close(loop: asyncio.AbstractEventLoop, conn: mock.Mock) -> None: + req = ClientRequest("get", URL("http://python.org"), loop=loop) + req._writer = asyncio.Future() # type: ignore[assignment] + + t = asyncio.create_task(req.close()) + + # Start waiting on _writer + await asyncio.sleep(0) + + t.cancel() + # Cancellation should not be suppressed. + with pytest.raises(asyncio.CancelledError): + await t + + +async def test_terminate(loop: asyncio.AbstractEventLoop, conn: mock.Mock) -> None: req = ClientRequest("get", URL("http://python.org"), loop=loop) async def _mock_write_bytes(*args, **kwargs): diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index 969153b1603..eadb43b1ecb 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -3,6 +3,7 @@ import json import pathlib import socket +import sys import zlib from typing import Any, NoReturn, Optional from unittest import mock @@ -22,6 +23,7 @@ web, ) from aiohttp.hdrs import CONTENT_LENGTH, CONTENT_TYPE, TRANSFER_ENCODING +from aiohttp.pytest_plugin import AiohttpClient from aiohttp.test_utils import make_mocked_coro from aiohttp.typedefs import Handler from aiohttp.web_protocol import RequestHandler @@ -187,8 +189,42 @@ async def handler(request): await resp.release() -async def test_post_form(aiohttp_client) -> None: - async def handler(request): +@pytest.mark.skipif(sys.version_info < (3, 11), reason="Needs Task.cancelling()") +async def test_cancel_shutdown(aiohttp_client: AiohttpClient) -> None: + async def handler(request: web.Request) -> web.Response: + t = asyncio.create_task(request.protocol.shutdown()) + # Ensure it's started waiting + await asyncio.sleep(0) + + t.cancel() + # Cancellation should not be suppressed + with pytest.raises(asyncio.CancelledError): + await t + + # Repeat for second waiter in shutdown() + with mock.patch.object(request.protocol, "_request_in_progress", False): + with mock.patch.object(request.protocol, "_current_request", None): + t = asyncio.create_task(request.protocol.shutdown()) + await asyncio.sleep(0) + + t.cancel() + with pytest.raises(asyncio.CancelledError): + await t + + return web.Response(body=b"OK") + + app = web.Application() + app.router.add_get("/", handler) + client = await aiohttp_client(app) + + async with client.get("/") as resp: + assert resp.status == 200 + txt = await resp.text() + assert txt == "OK" + + +async def test_post_form(aiohttp_client: AiohttpClient) -> None: + async def handler(request: web.Request) -> web.Response: data = await request.post() assert {"a": "1", "b": "2", "c": ""} == data return web.Response(body=b"OK") From 750e333c2f992d299f87a270252268b6a7e50a1c Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2024 21:28:44 -0500 Subject: [PATCH 279/296] [PR #9261/2383b9ca backport][3.10] Cleanup changelog messages (#9262) Co-authored-by: J. Nick Koston --- CHANGES/4650.bugfix | 2 +- CHANGES/8845.bugfix.rst | 2 +- CHANGES/9031.misc.rst | 2 +- CHANGES/9204.misc.rst | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES/4650.bugfix b/CHANGES/4650.bugfix index 5c9fc17ff60..e3e17b00ae8 100644 --- a/CHANGES/4650.bugfix +++ b/CHANGES/4650.bugfix @@ -1 +1 @@ -Implement binding to IPv6 addresses in the pytest server fixture. +Implemented binding to IPv6 addresses in the pytest server fixture. diff --git a/CHANGES/8845.bugfix.rst b/CHANGES/8845.bugfix.rst index ff0016ac14b..c37a0095ed3 100644 --- a/CHANGES/8845.bugfix.rst +++ b/CHANGES/8845.bugfix.rst @@ -1 +1 @@ -Changed behaviour when returning an invalid response to send a 500 response -- by :user:`Dreamsorcerer`. +Changed behavior when returning an invalid response to send a 500 response -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/9031.misc.rst b/CHANGES/9031.misc.rst index 1deab5230f7..1874a4deddd 100644 --- a/CHANGES/9031.misc.rst +++ b/CHANGES/9031.misc.rst @@ -1 +1 @@ -Tracing overhead is avoided in the http writer when there are no active traces -- by user:`bdraco`. +Avoided tracing overhead in the http writer when there are no active traces -- by user:`bdraco`. diff --git a/CHANGES/9204.misc.rst b/CHANGES/9204.misc.rst index da12a7df6f7..9f3196fa5be 100644 --- a/CHANGES/9204.misc.rst +++ b/CHANGES/9204.misc.rst @@ -1 +1 @@ -Significantly speed up filtering cookies -- by :user:`bdraco`. +Significantly sped up filtering cookies -- by :user:`bdraco`. From 3eb72824a7ddddb41e57a4275224df7b46b58134 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Sep 2024 21:33:43 -0500 Subject: [PATCH 280/296] Release 3.10.6rc0 --- CHANGES.rst | 449 ++++++++++++++++++++++++++++++++++++++++++++ aiohttp/__init__.py | 2 +- 2 files changed, 450 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 2bd19de71d6..9a944423642 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,455 @@ .. towncrier release notes start +3.10.6rc0 (2024-09-22) +====================== + +Bug fixes +--------- + +- Implemented binding to IPv6 addresses in the pytest server fixture. + + + *Related issues and pull requests on GitHub:* + :issue:`4650`. + + + +- Fixed StreamResponse.prepared to return True after EOF is sent -- by :user:`arthurdarcet`. + + + *Related issues and pull requests on GitHub:* + :issue:`5343`. + + + +- Fixed ``Response.text`` when body is a ``Payload`` -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`6485`. + + + +- Added support for URL credentials with empty (zero-length) username, e.g. ``https://:password@host`` -- by :user:`shuckc` + + + *Related issues and pull requests on GitHub:* + :issue:`6494`. + + + +- Fixed handling of some file-like objects (e.g. ``tarfile.extractfile()``) which raise ``AttributeError`` instead of ``OSError`` when ``fileno`` fails for streaming payload data -- by :user:`ReallyReivax`. + + + *Related issues and pull requests on GitHub:* + :issue:`6732`. + + + +- Stopped logging exceptions from ``web.run_app()`` that would be raised regardless -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`6807`. + + + +- Changed ``make_mocked_request()`` to use empty payload by default -- by :user:`rahulnht`. + + + *Related issues and pull requests on GitHub:* + :issue:`7167`. + + + +- Used more precise type for ``ClientResponseError.headers``, fixing some type errors when using them -- by :user:`Dreamorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8768`. + + + +- Fixed Python parser chunked handling with multiple Transfer-Encoding values -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8823`. + + + +- Changed behavior when returning an invalid response to send a 500 response -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8845`. + + + +- Stopped adding a default Content-Type header when response has no content -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8858`. + + + +- Fixed an unclosed transport ``ResourceWarning`` on web handlers -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8875`. + + + +- Fixed error handling after 100-continue so server sends 500 response instead of disconnecting -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8876`. + + + +- Fixed response reading from closed session to throw an error immediately instead of timing out -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8878`. + + + +- Fixed web router not matching pre-encoded URLs (requires yarl 1.9.6+) -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8898`. + + + +- Fixed ``CancelledError`` from one cleanup context stopping other contexts from completing -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8908`. + + + +- Fixed ``Site.name`` when host is an empty string -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8929`. + + + +- Fixed resolve_host() 'Task was destroyed but is pending' errors -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8967`. + + + +- Fixed changing scheme/host in ``Response.clone()`` for absolute URLs -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8990`. + + + +- Fixed client incorrectly reusing a connection when the previous message had not been fully sent -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8992`. + + + +- Fixed an error when trying to add a route for multiple methods with a path containing a regex pattern -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8998`. + + + +- Updated Python parser to reject messages after a close message, matching C parser behaviour -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`9018`. + + + +- Fixed creation of ``SSLContext`` inside of :py:class:`aiohttp.TCPConnector` with multiple event loops in different threads -- by :user:`bdraco`. + + + *Related issues and pull requests on GitHub:* + :issue:`9029`. + + + +- Fixed (on Python 3.11+) some edge cases where a task cancellation may get incorrectly suppressed -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`9030`. + + + +- Fixed the incorrect use of flags for ``getnameinfo()`` in the Resolver --by :user:`GitNMLee` + + Link-Local IPv6 addresses can now be handled by the Resolver correctly. + + + *Related issues and pull requests on GitHub:* + :issue:`9032`. + + + +- Fixed exception information getting lost on ``HttpProcessingError`` -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`9052`. + + + +- Fixed ``If-None-Match`` not using weak comparison -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`9063`. + + + +- Fixed compressed requests failing when no body was provided -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`9108`. + + + +- Added :exc:`aiohttp.ClientConnectionResetError`. Client code that previously threw :exc:`ConnectionResetError` + will now throw this -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`9137`. + + + +- Fixed race condition that could cause server to close connection incorrectly at keepalive timeout -- by :user:`Dreamosorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`9140`. + + + +- Fixed badly encoded charset crashing when getting response text instead of falling back to charset detector. + + + *Related issues and pull requests on GitHub:* + :issue:`9160`. + + + +- Rejected `\n` in `reason` values to avoid sending broken HTTP messages -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`9167`. + + + +- Changed :py:meth:`ClientResponse.raise_for_status() ` to only release the connection when invoked outside an ``async with`` context -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`9239`. + + + + +Features +-------- + +- Improved type on ``params`` to match the underlying type allowed by ``yarl`` -- by :user:`lpetre`. + + + *Related issues and pull requests on GitHub:* + :issue:`8564`. + + + + +Removals and backward incompatible breaking changes +--------------------------------------------------- + +- Improved middleware performance -- by :user:`bdraco`. + + The ``set_current_app`` method was removed from ``UrlMappingMatchInfo`` because it is no longer used, and it was unlikely external caller would ever use it. + + + *Related issues and pull requests on GitHub:* + :issue:`9200`. + + + + +Improved documentation +---------------------- + +- Clarified that ``GracefulExit`` needs to be handled in ``AppRunner`` and ``ServerRunner`` when using ``handle_signals=True``. -- by :user:`Daste745` + + + *Related issues and pull requests on GitHub:* + :issue:`4414`. + + + +- Clarified that auth parameter in ClientSession will persist and be included with any request to any origin, even during redirects to different origins. -- by :user:`MaximZemskov`. + + + *Related issues and pull requests on GitHub:* + :issue:`6764`. + + + +- Clarified which timeout exceptions happen on which timeouts -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8968`. + + + +- Updated ``ClientSession`` parameters to match current code -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`8991`. + + + + +Packaging updates and notes for downstreams +------------------------------------------- + +- Fixed ``test_client_session_timeout_zero`` to not require internet access -- by :user:`Dreamsorcerer`. + + + *Related issues and pull requests on GitHub:* + :issue:`9004`. + + + + +Miscellaneous internal changes +------------------------------ + +- Improved performance of making requests when there are no auto headers to skip -- by :user:`bdraco`. + + + *Related issues and pull requests on GitHub:* + :issue:`8847`. + + + +- Exported ``aiohttp.TraceRequestHeadersSentParams`` -- by :user:`Hadock-is-ok`. + + + *Related issues and pull requests on GitHub:* + :issue:`8947`. + + + +- Avoided tracing overhead in the http writer when there are no active traces -- by user:`bdraco`. + + + *Related issues and pull requests on GitHub:* + :issue:`9031`. + + + +- Improved performance of reify Cython implementation -- by :user:`bdraco`. + + + *Related issues and pull requests on GitHub:* + :issue:`9054`. + + + +- Use :meth:`URL.extend_query() ` to extend query params (requires yarl 1.11.0+) -- by :user:`bdraco`. + + If yarl is older than 1.11.0, the previous slower hand rolled version will be used. + + + *Related issues and pull requests on GitHub:* + :issue:`9068`. + + + +- Improved performance of checking if a host is an IP Address -- by :user:`bdraco`. + + + *Related issues and pull requests on GitHub:* + :issue:`9095`. + + + +- Significantly improved performance of middlewares -- by :user:`bdraco`. + + The construction of the middleware wrappers is now cached and is built once per handler instead of on every request. + + + *Related issues and pull requests on GitHub:* + :issue:`9158`, :issue:`9170`. + + + +- Improved performance of web requests -- by :user:`bdraco`. + + + *Related issues and pull requests on GitHub:* + :issue:`9168`, :issue:`9169`, :issue:`9172`, :issue:`9174`, :issue:`9175`, :issue:`9241`. + + + +- Improved performance of starting web requests when there is no response prepare hook -- by :user:`bdraco`. + + + *Related issues and pull requests on GitHub:* + :issue:`9173`. + + + +- Significantly improved performance of expiring cookies -- by :user:`bdraco`. + + Expiring cookies has been redesigned to use :mod:`heapq` instead of a linear search, to better scale. + + + *Related issues and pull requests on GitHub:* + :issue:`9203`. + + + +- Significantly sped up filtering cookies -- by :user:`bdraco`. + + + *Related issues and pull requests on GitHub:* + :issue:`9204`. + + + + +---- + + 3.10.5 (2024-08-19) ========================= diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py index 63367052646..111ce98440c 100644 --- a/aiohttp/__init__.py +++ b/aiohttp/__init__.py @@ -1,4 +1,4 @@ -__version__ = "3.10.6.dev0" +__version__ = "3.10.6rc0" from typing import TYPE_CHECKING, Tuple From bf1490951b6c26d71e2febd5e2b58f3e871fabd5 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 03:31:00 +0000 Subject: [PATCH 281/296] [PR #9261/2383b9ca backport][3.11] Cleanup changelog messages (#9263) Co-authored-by: J. Nick Koston --- CHANGES/4650.bugfix | 2 +- CHANGES/8845.bugfix.rst | 2 +- CHANGES/9031.misc.rst | 2 +- CHANGES/9204.misc.rst | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES/4650.bugfix b/CHANGES/4650.bugfix index 5c9fc17ff60..e3e17b00ae8 100644 --- a/CHANGES/4650.bugfix +++ b/CHANGES/4650.bugfix @@ -1 +1 @@ -Implement binding to IPv6 addresses in the pytest server fixture. +Implemented binding to IPv6 addresses in the pytest server fixture. diff --git a/CHANGES/8845.bugfix.rst b/CHANGES/8845.bugfix.rst index ff0016ac14b..c37a0095ed3 100644 --- a/CHANGES/8845.bugfix.rst +++ b/CHANGES/8845.bugfix.rst @@ -1 +1 @@ -Changed behaviour when returning an invalid response to send a 500 response -- by :user:`Dreamsorcerer`. +Changed behavior when returning an invalid response to send a 500 response -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/9031.misc.rst b/CHANGES/9031.misc.rst index 1deab5230f7..1874a4deddd 100644 --- a/CHANGES/9031.misc.rst +++ b/CHANGES/9031.misc.rst @@ -1 +1 @@ -Tracing overhead is avoided in the http writer when there are no active traces -- by user:`bdraco`. +Avoided tracing overhead in the http writer when there are no active traces -- by user:`bdraco`. diff --git a/CHANGES/9204.misc.rst b/CHANGES/9204.misc.rst index da12a7df6f7..9f3196fa5be 100644 --- a/CHANGES/9204.misc.rst +++ b/CHANGES/9204.misc.rst @@ -1 +1 @@ -Significantly speed up filtering cookies -- by :user:`bdraco`. +Significantly sped up filtering cookies -- by :user:`bdraco`. From 84a69ce291ff1652ce1b2474566cd44182a467c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:51:49 +0000 Subject: [PATCH 282/296] Bump proxy-py from 2.4.7 to 2.4.8 (#9264) Bumps [proxy-py](https://github.com/abhinavsingh/proxy.py) from 2.4.7 to 2.4.8.
Commits
  • 4884255 Bump actions/download-artifact from 3 to 4 in /.github/workflows in the githu...
  • ebf3599 Bump pip group for benchmark comparisions to fix security scanner reports (#1...
  • c07436c Bump workflows to use Ubuntu 24.04 (#1478)
  • f5f18e4 TLS Intercept conditionally (#1476)
  • a51ddaa Add clarity on how HttpParser class works (#1466)
  • 05ac288 Add support for Upgrade: Derp custom protocol (#1463)
  • 447b68e Add pip and Selenium Base to projects using proxy.py
  • See full diff in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=proxy-py&package-manager=pip&previous-version=2.4.7&new-version=2.4.8)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index d1069b4f590..6ce841dc2c9 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -146,7 +146,7 @@ pluggy==1.5.0 # via pytest pre-commit==3.5.0 # via -r requirements/lint.in -proxy-py==2.4.7 +proxy-py==2.4.8 # via -r requirements/test.in pycares==4.4.0 # via aiodns diff --git a/requirements/dev.txt b/requirements/dev.txt index 46be3754db8..e4e6c6ac172 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -143,7 +143,7 @@ pluggy==1.5.0 # via pytest pre-commit==3.5.0 # via -r requirements/lint.in -proxy-py==2.4.7 +proxy-py==2.4.8 # via -r requirements/test.in pycares==4.4.0 # via aiodns diff --git a/requirements/test.txt b/requirements/test.txt index bf1be8a9e33..a479b9a5e38 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -71,7 +71,7 @@ packaging==24.1 # pytest pluggy==1.5.0 # via pytest -proxy-py==2.4.7 +proxy-py==2.4.8 # via -r requirements/test.in pycares==4.4.0 # via aiodns From c5a3d812e519f2d94425f50b41cfd92eb8341867 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 Sep 2024 17:07:23 -0500 Subject: [PATCH 283/296] [PR #9267/947b9c4 backport][3.10] Fix double unquoting in url dispatcher (#9269) Co-authored-by: Sam Bull --- CHANGES/9267.breaking.rst | 1 + CHANGES/9267.bugfix.rst | 1 + aiohttp/web_urldispatcher.py | 24 ++++++++++++------------ requirements/base.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/runtime-deps.in | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test.txt | 2 +- setup.cfg | 2 +- tests/test_urldispatch.py | 17 ++++++++++++++--- tests/test_web_urldispatcher.py | 18 +++++++++++++++++- 12 files changed, 52 insertions(+), 23 deletions(-) create mode 100644 CHANGES/9267.breaking.rst create mode 120000 CHANGES/9267.bugfix.rst diff --git a/CHANGES/9267.breaking.rst b/CHANGES/9267.breaking.rst new file mode 100644 index 00000000000..82fec1d21b4 --- /dev/null +++ b/CHANGES/9267.breaking.rst @@ -0,0 +1 @@ +Increased minimum yarl version to 1.12.0 -- by :user:`bdraco`. diff --git a/CHANGES/9267.bugfix.rst b/CHANGES/9267.bugfix.rst new file mode 120000 index 00000000000..2a85c7ec63c --- /dev/null +++ b/CHANGES/9267.bugfix.rst @@ -0,0 +1 @@ +8898.bugfix.rst \ No newline at end of file diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 0f6d1b2bcd6..89abdc43fa6 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -375,7 +375,7 @@ def register_route(self, route: "ResourceRoute") -> None: async def resolve(self, request: Request) -> _Resolve: allowed_methods: Set[str] = set() - match_dict = self._match(request.rel_url.path) + match_dict = self._match(request.rel_url.path_safe) if match_dict is None: return None, allowed_methods @@ -425,8 +425,7 @@ def _match(self, path: str) -> Optional[Dict[str, str]]: # string comparison is about 10 times faster than regexp matching if self._path == path: return {} - else: - return None + return None def raw_match(self, path: str) -> bool: return self._path == path @@ -497,10 +496,9 @@ def _match(self, path: str) -> Optional[Dict[str, str]]: match = self._pattern.fullmatch(path) if match is None: return None - else: - return { - key: _unquote_path(value) for key, value in match.groupdict().items() - } + return { + key: _unquote_path_safe(value) for key, value in match.groupdict().items() + } def raw_match(self, path: str) -> bool: return self._orig_path == path @@ -645,7 +643,7 @@ def set_options_route(self, handler: Handler) -> None: ) async def resolve(self, request: Request) -> _Resolve: - path = request.rel_url.path + path = request.rel_url.path_safe method = request.method allowed_methods = set(self._routes) if not path.startswith(self._prefix2) and path != self._prefix: @@ -654,7 +652,7 @@ async def resolve(self, request: Request) -> _Resolve: if method not in allowed_methods: return None, allowed_methods - match_dict = {"filename": _unquote_path(path[len(self._prefix) + 1 :])} + match_dict = {"filename": _unquote_path_safe(path[len(self._prefix) + 1 :])} return (UrlMappingMatchInfo(match_dict, self._routes[method]), allowed_methods) def __len__(self) -> int: @@ -1035,7 +1033,7 @@ async def resolve(self, request: Request) -> UrlMappingMatchInfo: # candidates for a given url part because there are multiple resources # registered for the same canonical path, we resolve them in a linear # fashion to ensure registration order is respected. - url_part = request.rel_url.path + url_part = request.rel_url.path_safe while url_part: for candidate in resource_index.get(url_part, ()): match_dict, allowed = await candidate.resolve(request) @@ -1286,8 +1284,10 @@ def _quote_path(value: str) -> str: return URL.build(path=value, encoded=False).raw_path -def _unquote_path(value: str) -> str: - return URL.build(path=value, encoded=True).path.replace("%2F", "/") +def _unquote_path_safe(value: str) -> str: + if "%" not in value: + return value + return value.replace("%2F", "/").replace("%25", "%") def _requote_path(value: str) -> str: diff --git a/requirements/base.txt b/requirements/base.txt index 6604aa0f6c5..4dd5dc1f05b 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -38,5 +38,5 @@ pycparser==2.21 # via cffi uvloop==0.19.0 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in -yarl==1.11.0 +yarl==1.12.0 # via -r requirements/runtime-deps.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index f3109af5fb7..59f7a130b75 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -274,7 +274,7 @@ webcolors==1.11.1 # via blockdiag wheel==0.37.0 # via pip-tools -yarl==1.11.0 +yarl==1.12.0 # via -r requirements/runtime-deps.in zipp==3.17.0 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 54c0157b01a..d6ee12fe9d4 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -261,7 +261,7 @@ webcolors==1.13 # via blockdiag wheel==0.41.0 # via pip-tools -yarl==1.11.0 +yarl==1.12.0 # via -r requirements/runtime-deps.in zipp==3.17.0 # via diff --git a/requirements/runtime-deps.in b/requirements/runtime-deps.in index 2299584a463..9a199453d55 100644 --- a/requirements/runtime-deps.in +++ b/requirements/runtime-deps.in @@ -9,4 +9,4 @@ Brotli; platform_python_implementation == 'CPython' brotlicffi; platform_python_implementation != 'CPython' frozenlist >= 1.1.1 multidict >=4.5, < 7.0 -yarl >= 1.0, < 2.0 +yarl >= 1.12.0, < 2.0 diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index 279a9525fc5..b7a3828955e 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -32,5 +32,5 @@ pycares==4.3.0 # via aiodns pycparser==2.21 # via cffi -yarl==1.11.0 +yarl==1.12.0 # via -r requirements/runtime-deps.in diff --git a/requirements/test.txt b/requirements/test.txt index ad9ec0ace39..a42a5c0dbb3 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -125,5 +125,5 @@ uvloop==0.19.0 ; platform_system != "Windows" and implementation_name == "cpytho # via -r requirements/base.in wait-for-it==2.2.2 # via -r requirements/test.in -yarl==1.11.0 +yarl==1.12.0 # via -r requirements/runtime-deps.in diff --git a/setup.cfg b/setup.cfg index bd93b00cb2f..a3edc74fc8c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -55,7 +55,7 @@ install_requires = attrs >= 17.3.0 frozenlist >= 1.1.1 multidict >=4.5, < 7.0 - yarl >= 1.0, < 2.0 + yarl >= 1.12.0, < 2.0 [options.exclude_package_data] * = diff --git a/tests/test_urldispatch.py b/tests/test_urldispatch.py index f06f73edc21..de6815589b0 100644 --- a/tests/test_urldispatch.py +++ b/tests/test_urldispatch.py @@ -1,7 +1,7 @@ import pathlib import re from collections.abc import Container, Iterable, Mapping, MutableMapping, Sized -from urllib.parse import unquote +from urllib.parse import quote, unquote import pytest from re_assert import Matches @@ -457,7 +457,7 @@ def test_add_static_quoting(router) -> None: ) assert router["static"] is resource url = resource.url_for(filename="/1 2/файл%2F.txt") - assert url.path == "/пре %2Fфикс/1 2/файл%2F.txt" + assert url.path == "/пре /фикс/1 2/файл%2F.txt" assert str(url) == ( "/%D0%BF%D1%80%D0%B5%20%2F%D1%84%D0%B8%D0%BA%D1%81" "/1%202/%D1%84%D0%B0%D0%B9%D0%BB%252F.txt" @@ -630,7 +630,7 @@ def test_route_dynamic_quoting(router) -> None: route = router.add_route("GET", r"/пре %2Fфикс/{arg}", handler) url = route.url_for(arg="1 2/текст%2F") - assert url.path == "/пре %2Fфикс/1 2/текст%2F" + assert url.path == "/пре /фикс/1 2/текст%2F" assert str(url) == ( "/%D0%BF%D1%80%D0%B5%20%2F%D1%84%D0%B8%D0%BA%D1%81" "/1%202/%D1%82%D0%B5%D0%BA%D1%81%D1%82%252F" @@ -742,6 +742,17 @@ async def test_dynamic_match_unquoted_path(router) -> None: assert match_info == {"path": "path", "subpath": unquote(resource_id)} +async def test_dynamic_match_double_quoted_path(router: web.UrlDispatcher) -> None: + """Verify that double-quoted path is unquoted only once.""" + handler = make_handler() + router.add_route("GET", "/{path}/{subpath}", handler) + resource_id = quote("my/path|with!some%strange$characters", safe="") + double_quoted_resource_id = quote(resource_id, safe="") + req = make_mocked_request("GET", f"/path/{double_quoted_resource_id}") + match_info = await router.resolve(req) + assert match_info == {"path": "path", "subpath": resource_id} + + def test_add_route_not_started_with_slash(router) -> None: with pytest.raises(ValueError): handler = make_handler() diff --git a/tests/test_web_urldispatcher.py b/tests/test_web_urldispatcher.py index 7991cfe821e..eca365d2a25 100644 --- a/tests/test_web_urldispatcher.py +++ b/tests/test_web_urldispatcher.py @@ -5,7 +5,7 @@ import socket import sys from stat import S_IFIFO, S_IMODE -from typing import Any, Generator, Optional +from typing import Any, Generator, NoReturn, Optional import pytest import yarl @@ -885,6 +885,22 @@ async def handler(request: web.Request) -> web.Response: assert resp.status == expected_http_resp_status +async def test_decoded_raw_match_regex(aiohttp_client: AiohttpClient) -> None: + """Verify that raw_match only matches decoded url.""" + app = web.Application() + + async def handler(request: web.Request) -> NoReturn: + assert False + + app.router.add_get("/467%2C802%2C24834%2C24952%2C25362%2C40574/hello", handler) + client = await aiohttp_client(app) + + async with client.get( + yarl.URL("/467%2C802%2C24834%2C24952%2C25362%2C40574/hello", encoded=True) + ) as resp: + assert resp.status == 404 # should only match decoded url + + async def test_order_is_preserved(aiohttp_client: AiohttpClient) -> None: """Test route order is preserved. From be348691da356ea45b88f2074831a22168c41ee6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 Sep 2024 17:11:55 -0500 Subject: [PATCH 284/296] Release 3.10.6rc1 --- CHANGES.rst | 18 ++++++++++++++++++ aiohttp/__init__.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9a944423642..36f05700df3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,24 @@ .. towncrier release notes start +3.10.6rc1 (2024-09-22) +====================== + +Removals and backward incompatible breaking changes +--------------------------------------------------- + +- Increased minimum yarl version to 1.12.0 -- by :user:`bdraco`. + + + *Related issues and pull requests on GitHub:* + :issue:`9267`. + + + + +---- + + 3.10.6rc0 (2024-09-22) ====================== diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py index 111ce98440c..25d7f39ffe5 100644 --- a/aiohttp/__init__.py +++ b/aiohttp/__init__.py @@ -1,4 +1,4 @@ -__version__ = "3.10.6rc0" +__version__ = "3.10.6rc1" from typing import TYPE_CHECKING, Tuple From 22a9243a32d7dc16149a0ca74170acc7ab6a7372 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 Sep 2024 17:14:47 -0500 Subject: [PATCH 285/296] [PR #9267/947b9c4 backport][3.11] Fix double unquoting in url dispatcher (#9270) Co-authored-by: Sam Bull --- CHANGES/9267.breaking.rst | 1 + CHANGES/9267.bugfix.rst | 1 + aiohttp/web_urldispatcher.py | 24 ++++++++++++------------ requirements/base.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/runtime-deps.in | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test.txt | 2 +- setup.cfg | 2 +- tests/test_urldispatch.py | 17 ++++++++++++++--- tests/test_web_urldispatcher.py | 18 +++++++++++++++++- 12 files changed, 52 insertions(+), 23 deletions(-) create mode 100644 CHANGES/9267.breaking.rst create mode 120000 CHANGES/9267.bugfix.rst diff --git a/CHANGES/9267.breaking.rst b/CHANGES/9267.breaking.rst new file mode 100644 index 00000000000..82fec1d21b4 --- /dev/null +++ b/CHANGES/9267.breaking.rst @@ -0,0 +1 @@ +Increased minimum yarl version to 1.12.0 -- by :user:`bdraco`. diff --git a/CHANGES/9267.bugfix.rst b/CHANGES/9267.bugfix.rst new file mode 120000 index 00000000000..2a85c7ec63c --- /dev/null +++ b/CHANGES/9267.bugfix.rst @@ -0,0 +1 @@ +8898.bugfix.rst \ No newline at end of file diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 9c07f4ee9ad..8c1eef9094a 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -375,7 +375,7 @@ def register_route(self, route: "ResourceRoute") -> None: async def resolve(self, request: Request) -> _Resolve: allowed_methods: Set[str] = set() - match_dict = self._match(request.rel_url.path) + match_dict = self._match(request.rel_url.path_safe) if match_dict is None: return None, allowed_methods @@ -425,8 +425,7 @@ def _match(self, path: str) -> Optional[Dict[str, str]]: # string comparison is about 10 times faster than regexp matching if self._path == path: return {} - else: - return None + return None def raw_match(self, path: str) -> bool: return self._path == path @@ -497,10 +496,9 @@ def _match(self, path: str) -> Optional[Dict[str, str]]: match = self._pattern.fullmatch(path) if match is None: return None - else: - return { - key: _unquote_path(value) for key, value in match.groupdict().items() - } + return { + key: _unquote_path_safe(value) for key, value in match.groupdict().items() + } def raw_match(self, path: str) -> bool: return self._orig_path == path @@ -645,7 +643,7 @@ def set_options_route(self, handler: Handler) -> None: ) async def resolve(self, request: Request) -> _Resolve: - path = request.rel_url.path + path = request.rel_url.path_safe method = request.method allowed_methods = set(self._routes) if not path.startswith(self._prefix2) and path != self._prefix: @@ -654,7 +652,7 @@ async def resolve(self, request: Request) -> _Resolve: if method not in allowed_methods: return None, allowed_methods - match_dict = {"filename": _unquote_path(path[len(self._prefix) + 1 :])} + match_dict = {"filename": _unquote_path_safe(path[len(self._prefix) + 1 :])} return (UrlMappingMatchInfo(match_dict, self._routes[method]), allowed_methods) def __len__(self) -> int: @@ -1035,7 +1033,7 @@ async def resolve(self, request: Request) -> UrlMappingMatchInfo: # candidates for a given url part because there are multiple resources # registered for the same canonical path, we resolve them in a linear # fashion to ensure registration order is respected. - url_part = request.rel_url.path + url_part = request.rel_url.path_safe while url_part: for candidate in resource_index.get(url_part, ()): match_dict, allowed = await candidate.resolve(request) @@ -1286,8 +1284,10 @@ def _quote_path(value: str) -> str: return URL.build(path=value, encoded=False).raw_path -def _unquote_path(value: str) -> str: - return URL.build(path=value, encoded=True).path.replace("%2F", "/") +def _unquote_path_safe(value: str) -> str: + if "%" not in value: + return value + return value.replace("%2F", "/").replace("%25", "%") def _requote_path(value: str) -> str: diff --git a/requirements/base.txt b/requirements/base.txt index d947f437f98..1eb566b4627 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -40,5 +40,5 @@ typing-extensions==4.12.2 # via multidict uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in -yarl==1.11.1 +yarl==1.12.0 # via -r requirements/runtime-deps.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 6ce841dc2c9..4f90ea80755 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -287,7 +287,7 @@ webcolors==24.8.0 # via blockdiag wheel==0.44.0 # via pip-tools -yarl==1.11.1 +yarl==1.12.0 # via -r requirements/runtime-deps.in zipp==3.20.2 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index e4e6c6ac172..611f438c8b4 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -279,7 +279,7 @@ webcolors==24.8.0 # via blockdiag wheel==0.44.0 # via pip-tools -yarl==1.11.1 +yarl==1.12.0 # via -r requirements/runtime-deps.in zipp==3.20.2 # via diff --git a/requirements/runtime-deps.in b/requirements/runtime-deps.in index 1b440bc7c68..9a199453d55 100644 --- a/requirements/runtime-deps.in +++ b/requirements/runtime-deps.in @@ -9,4 +9,4 @@ Brotli; platform_python_implementation == 'CPython' brotlicffi; platform_python_implementation != 'CPython' frozenlist >= 1.1.1 multidict >=4.5, < 7.0 -yarl >= 1.11.0, < 2.0 +yarl >= 1.12.0, < 2.0 diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index eea3d44a539..87d55bfa80c 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -34,5 +34,5 @@ pycparser==2.22 # via cffi typing-extensions==4.12.2 # via multidict -yarl==1.11.1 +yarl==1.12.0 # via -r requirements/runtime-deps.in diff --git a/requirements/test.txt b/requirements/test.txt index a479b9a5e38..c2b9653fd4a 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -137,5 +137,5 @@ uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpytho # via -r requirements/base.in wait-for-it==2.2.2 # via -r requirements/test.in -yarl==1.11.1 +yarl==1.12.0 # via -r requirements/runtime-deps.in diff --git a/setup.cfg b/setup.cfg index c5258115f11..91f4385aedc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -54,7 +54,7 @@ install_requires = attrs >= 17.3.0 frozenlist >= 1.1.1 multidict >=4.5, < 7.0 - yarl >= 1.11.0, < 2.0 + yarl >= 1.12.0, < 2.0 [options.exclude_package_data] * = diff --git a/tests/test_urldispatch.py b/tests/test_urldispatch.py index d0efa91593e..8c3eaed13b7 100644 --- a/tests/test_urldispatch.py +++ b/tests/test_urldispatch.py @@ -1,7 +1,7 @@ import pathlib import re from collections.abc import Container, Iterable, Mapping, MutableMapping, Sized -from urllib.parse import unquote +from urllib.parse import quote, unquote import pytest from re_assert import Matches @@ -457,7 +457,7 @@ def test_add_static_quoting(router) -> None: ) assert router["static"] is resource url = resource.url_for(filename="/1 2/файл%2F.txt") - assert url.path == "/пре %2Fфикс/1 2/файл%2F.txt" + assert url.path == "/пре /фикс/1 2/файл%2F.txt" assert str(url) == ( "/%D0%BF%D1%80%D0%B5%20%2F%D1%84%D0%B8%D0%BA%D1%81" "/1%202/%D1%84%D0%B0%D0%B9%D0%BB%252F.txt" @@ -630,7 +630,7 @@ def test_route_dynamic_quoting(router) -> None: route = router.add_route("GET", r"/пре %2Fфикс/{arg}", handler) url = route.url_for(arg="1 2/текст%2F") - assert url.path == "/пре %2Fфикс/1 2/текст%2F" + assert url.path == "/пре /фикс/1 2/текст%2F" assert str(url) == ( "/%D0%BF%D1%80%D0%B5%20%2F%D1%84%D0%B8%D0%BA%D1%81" "/1%202/%D1%82%D0%B5%D0%BA%D1%81%D1%82%252F" @@ -742,6 +742,17 @@ async def test_dynamic_match_unquoted_path(router) -> None: assert match_info == {"path": "path", "subpath": unquote(resource_id)} +async def test_dynamic_match_double_quoted_path(router: web.UrlDispatcher) -> None: + """Verify that double-quoted path is unquoted only once.""" + handler = make_handler() + router.add_route("GET", "/{path}/{subpath}", handler) + resource_id = quote("my/path|with!some%strange$characters", safe="") + double_quoted_resource_id = quote(resource_id, safe="") + req = make_mocked_request("GET", f"/path/{double_quoted_resource_id}") + match_info = await router.resolve(req) + assert match_info == {"path": "path", "subpath": resource_id} + + def test_add_route_not_started_with_slash(router) -> None: with pytest.raises(ValueError): handler = make_handler() diff --git a/tests/test_web_urldispatcher.py b/tests/test_web_urldispatcher.py index 7991cfe821e..eca365d2a25 100644 --- a/tests/test_web_urldispatcher.py +++ b/tests/test_web_urldispatcher.py @@ -5,7 +5,7 @@ import socket import sys from stat import S_IFIFO, S_IMODE -from typing import Any, Generator, Optional +from typing import Any, Generator, NoReturn, Optional import pytest import yarl @@ -885,6 +885,22 @@ async def handler(request: web.Request) -> web.Response: assert resp.status == expected_http_resp_status +async def test_decoded_raw_match_regex(aiohttp_client: AiohttpClient) -> None: + """Verify that raw_match only matches decoded url.""" + app = web.Application() + + async def handler(request: web.Request) -> NoReturn: + assert False + + app.router.add_get("/467%2C802%2C24834%2C24952%2C25362%2C40574/hello", handler) + client = await aiohttp_client(app) + + async with client.get( + yarl.URL("/467%2C802%2C24834%2C24952%2C25362%2C40574/hello", encoded=True) + ) as resp: + assert resp.status == 404 # should only match decoded url + + async def test_order_is_preserved(aiohttp_client: AiohttpClient) -> None: """Test route order is preserved. From 361db7c64bb69c91fc5dee7869e5c64ee254573e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 23 Sep 2024 20:23:31 -0500 Subject: [PATCH 286/296] Release 3.10.6rc2 --- CHANGES.rst | 9 +++++++++ aiohttp/__init__.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 36f05700df3..cff8a8a0e2e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,15 @@ .. towncrier release notes start +3.10.6rc2 (2024-09-23) +====================== + +No significant changes. + + +---- + + 3.10.6rc1 (2024-09-22) ====================== diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py index 25d7f39ffe5..02fde739bdb 100644 --- a/aiohttp/__init__.py +++ b/aiohttp/__init__.py @@ -1,4 +1,4 @@ -__version__ = "3.10.6rc1" +__version__ = "3.10.6rc2" from typing import TYPE_CHECKING, Tuple From eb938088cb88dc6169f3688ef03cb1034066d2e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:58:18 +0000 Subject: [PATCH 287/296] Bump yarl from 1.12.0 to 1.12.1 (#9274) Bumps [yarl](https://github.com/aio-libs/yarl) from 1.12.0 to 1.12.1.
Release notes

Sourced from yarl's releases.

1.12.1

No significant changes.

This release was created because the signatures failed to upload for 1.12.0


Changelog

Sourced from yarl's changelog.

1.12.1

(2024-09-23)

No significant changes.


Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=yarl&package-manager=pip&previous-version=1.12.0&new-version=1.12.1)](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 merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@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.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements/base.txt b/requirements/base.txt index 1eb566b4627..c508dd9665e 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -40,5 +40,5 @@ typing-extensions==4.12.2 # via multidict uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in -yarl==1.12.0 +yarl==1.12.1 # via -r requirements/runtime-deps.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 4f90ea80755..7af5c4eea6c 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -287,7 +287,7 @@ webcolors==24.8.0 # via blockdiag wheel==0.44.0 # via pip-tools -yarl==1.12.0 +yarl==1.12.1 # via -r requirements/runtime-deps.in zipp==3.20.2 # via diff --git a/requirements/dev.txt b/requirements/dev.txt index 611f438c8b4..13a28f247ae 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -279,7 +279,7 @@ webcolors==24.8.0 # via blockdiag wheel==0.44.0 # via pip-tools -yarl==1.12.0 +yarl==1.12.1 # via -r requirements/runtime-deps.in zipp==3.20.2 # via diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index 87d55bfa80c..9a28aaf8c00 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -34,5 +34,5 @@ pycparser==2.22 # via cffi typing-extensions==4.12.2 # via multidict -yarl==1.12.0 +yarl==1.12.1 # via -r requirements/runtime-deps.in diff --git a/requirements/test.txt b/requirements/test.txt index c2b9653fd4a..15f15d16bc5 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -137,5 +137,5 @@ uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpytho # via -r requirements/base.in wait-for-it==2.2.2 # via -r requirements/test.in -yarl==1.12.0 +yarl==1.12.1 # via -r requirements/runtime-deps.in From cf1cad30bd586b1e376e5733a4cbd72bb40019a5 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 14:18:31 +0100 Subject: [PATCH 288/296] [PR #9276/ccbd2c5e backport][3.11] Bump uvloop (#9277) **This is a backport of PR #9276 as merged into master (ccbd2c5e3364dbf0425f097ae911ab84f44758dc).** Co-authored-by: Sam Bull --- .github/workflows/ci-cd.yml | 4 ++-- requirements/base.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/lint.txt | 2 +- requirements/test.txt | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 54ceb6b74cd..0c32a97f647 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -125,7 +125,7 @@ jobs: needs: gen_llhttp strategy: matrix: - pyver: [3.9, '3.10', '3.11', '3.12'] + pyver: [3.9, '3.10', '3.11', '3.12', '3.13'] no-extensions: ['', 'Y'] os: [ubuntu, macos, windows] experimental: [false] @@ -140,7 +140,7 @@ jobs: os: ubuntu experimental: false - os: ubuntu - pyver: "3.13" + pyver: "3.14" experimental: true no-extensions: 'Y' fail-fast: true diff --git a/requirements/base.txt b/requirements/base.txt index c508dd9665e..c4c189c3e4e 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -38,7 +38,7 @@ pycparser==2.22 # via cffi typing-extensions==4.12.2 # via multidict -uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" +uvloop==0.21.0b1 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in yarl==1.12.1 # via -r requirements/runtime-deps.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 7af5c4eea6c..fdf1e30f283 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -275,7 +275,7 @@ uritemplate==4.1.1 # via gidgethub urllib3==2.2.3 # via requests -uvloop==0.20.0 ; platform_system != "Windows" +uvloop==0.21.0b1 ; platform_system != "Windows" # via # -r requirements/base.in # -r requirements/lint.in diff --git a/requirements/dev.txt b/requirements/dev.txt index 13a28f247ae..81b1c1f6988 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -267,7 +267,7 @@ uritemplate==4.1.1 # via gidgethub urllib3==2.2.3 # via requests -uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" +uvloop==0.21.0b1 ; platform_system != "Windows" and implementation_name == "cpython" # via # -r requirements/base.in # -r requirements/lint.in diff --git a/requirements/lint.txt b/requirements/lint.txt index f5bdccfd695..299b2b09f4a 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -117,7 +117,7 @@ typing-extensions==4.12.2 # typer urllib3==2.2.3 # via requests -uvloop==0.20.0 ; platform_system != "Windows" +uvloop==0.21.0b1 ; platform_system != "Windows" # via -r requirements/lint.in virtualenv==20.26.5 # via pre-commit diff --git a/requirements/test.txt b/requirements/test.txt index 15f15d16bc5..2280e990965 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -133,7 +133,7 @@ typing-extensions==4.12.2 # typer urllib3==2.2.3 # via requests -uvloop==0.20.0 ; platform_system != "Windows" and implementation_name == "cpython" +uvloop==0.21.0b1 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in wait-for-it==2.2.2 # via -r requirements/test.in From 91c3162a285ffeff25887c8654ab6bfb801724c8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 24 Sep 2024 09:17:52 -0500 Subject: [PATCH 289/296] [3.10] Bump pydantic and deps for python 3.13 compat (#9279) --- requirements/constraints.txt | 6 +++--- requirements/dev.txt | 6 +++--- requirements/lint.txt | 6 +++--- requirements/test.txt | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 59f7a130b75..9badce63e06 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -18,7 +18,7 @@ aiosignal==1.3.1 # via -r requirements/runtime-deps.in alabaster==0.7.12 # via sphinx -annotated-types==0.5.0 +annotated-types==0.7.0 # via pydantic async-timeout==4.0.3 ; python_version < "3.11" # via @@ -148,9 +148,9 @@ pycares==4.3.0 # via aiodns pycparser==2.21 # via cffi -pydantic==2.2.0 +pydantic==2.9.2 # via python-on-whales -pydantic-core==2.6.0 +pydantic-core==2.23.4 # via pydantic pyenchant==3.2.2 # via sphinxcontrib-spelling diff --git a/requirements/dev.txt b/requirements/dev.txt index d6ee12fe9d4..1f42ee2d4bc 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -18,7 +18,7 @@ aiosignal==1.3.1 # via -r requirements/runtime-deps.in alabaster==0.7.13 # via sphinx -annotated-types==0.5.0 +annotated-types==0.7.0 # via pydantic async-timeout==4.0.3 ; python_version < "3.11" # via @@ -143,9 +143,9 @@ pycares==4.3.0 # via aiodns pycparser==2.21 # via cffi -pydantic==2.2.0 +pydantic==2.9.2 # via python-on-whales -pydantic-core==2.6.0 +pydantic-core==2.23.4 # via pydantic pygments==2.15.1 # via sphinx diff --git a/requirements/lint.txt b/requirements/lint.txt index 7ca49ba88d7..e1c8ca0e234 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -8,7 +8,7 @@ aiodns==3.2.0 # via -r requirements/lint.in aioredis==2.0.1 # via -r requirements/lint.in -annotated-types==0.6.0 +annotated-types==0.7.0 # via pydantic async-timeout==4.0.3 # via aioredis @@ -62,9 +62,9 @@ pycares==4.4.0 # via aiodns pycparser==2.22 # via cffi -pydantic==2.7.1 +pydantic==2.9.2 # via python-on-whales -pydantic-core==2.18.2 +pydantic-core==2.23.4 # via pydantic pygments==2.17.2 # via rich diff --git a/requirements/test.txt b/requirements/test.txt index a42a5c0dbb3..83592f6ef70 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -10,7 +10,7 @@ aiohappyeyeballs==2.3.4 # via -r requirements/runtime-deps.in aiosignal==1.3.1 # via -r requirements/runtime-deps.in -annotated-types==0.5.0 +annotated-types==0.7.0 # via pydantic async-timeout==4.0.3 ; python_version < "3.11" # via -r requirements/runtime-deps.in @@ -73,9 +73,9 @@ pycares==4.3.0 # via aiodns pycparser==2.21 # via cffi -pydantic==2.2.0 +pydantic==2.9.2 # via python-on-whales -pydantic-core==2.6.0 +pydantic-core==2.23.4 # via pydantic pytest==8.3.2 # via From 84c25b78a8a7b67b4915d5dd0e77112b623f043f Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:18:58 +0100 Subject: [PATCH 290/296] [PR #8748/2816002b backport][3.10] Add Python 3.13 classifier (#9280) **This is a backport of PR #8748 as merged into master (2816002b0b67bd517ec841afa65f70402997507d).** Co-authored-by: J. Nick Koston --- CHANGES/8748.feature.rst | 1 + setup.cfg | 1 + 2 files changed, 2 insertions(+) create mode 100644 CHANGES/8748.feature.rst diff --git a/CHANGES/8748.feature.rst b/CHANGES/8748.feature.rst new file mode 100644 index 00000000000..7794d16e4dc --- /dev/null +++ b/CHANGES/8748.feature.rst @@ -0,0 +1 @@ +Declared Python 3.13 supported -- by :user:`bdraco`. diff --git a/setup.cfg b/setup.cfg index a3edc74fc8c..d998e736b45 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,6 +38,7 @@ classifiers = Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 + Programming Language :: Python :: 3.13 Topic :: Internet :: WWW/HTTP From 1928fea21d6bc77199b2b312ff2771f00c3ee2a9 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 14:34:05 +0000 Subject: [PATCH 291/296] [PR #8748/2816002b backport][3.11] Add Python 3.13 classifier (#9281) Co-authored-by: J. Nick Koston --- CHANGES/8748.feature.rst | 1 + setup.cfg | 1 + 2 files changed, 2 insertions(+) create mode 100644 CHANGES/8748.feature.rst diff --git a/CHANGES/8748.feature.rst b/CHANGES/8748.feature.rst new file mode 100644 index 00000000000..7794d16e4dc --- /dev/null +++ b/CHANGES/8748.feature.rst @@ -0,0 +1 @@ +Declared Python 3.13 supported -- by :user:`bdraco`. diff --git a/setup.cfg b/setup.cfg index 91f4385aedc..8168dac4408 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,6 +37,7 @@ classifiers = Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 + Programming Language :: Python :: 3.13 Topic :: Internet :: WWW/HTTP From 3ba15873997ec8dadb1f0cf7aaf9ba2d81bf70ec Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 24 Sep 2024 09:54:14 -0500 Subject: [PATCH 292/296] [3.10] Bump typing-extensions to 4.12.2 (#9282) --- requirements/constraints.txt | 2 +- requirements/cython.txt | 2 +- requirements/dev.txt | 2 +- requirements/lint.txt | 2 +- requirements/test.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 9badce63e06..d08dae712ab 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -250,7 +250,7 @@ trustme==1.1.0 ; platform_machine != "i686" # via -r requirements/test.in typer==0.6.1 # via python-on-whales -typing-extensions==4.11.0 +typing-extensions==4.12.2 # via # aioredis # annotated-types diff --git a/requirements/cython.txt b/requirements/cython.txt index 72b9a67af98..053c390dd02 100644 --- a/requirements/cython.txt +++ b/requirements/cython.txt @@ -8,5 +8,5 @@ cython==3.0.10 # via -r requirements/cython.in multidict==6.0.5 # via -r requirements/multidict.in -typing-extensions==4.11.0 +typing-extensions==4.12.2 # via -r requirements/typing-extensions.in diff --git a/requirements/dev.txt b/requirements/dev.txt index 1f42ee2d4bc..f34ba86729f 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -236,7 +236,7 @@ trustme==1.1.0 ; platform_machine != "i686" # via -r requirements/test.in typer==0.9.0 # via python-on-whales -typing-extensions==4.11.0 +typing-extensions==4.12.2 # via # aioredis # annotated-types diff --git a/requirements/lint.txt b/requirements/lint.txt index e1c8ca0e234..57f4824ceac 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -95,7 +95,7 @@ trustme==1.1.0 # via -r requirements/lint.in typer==0.12.3 # via python-on-whales -typing-extensions==4.11.0 +typing-extensions==4.12.2 # via # aioredis # annotated-types diff --git a/requirements/test.txt b/requirements/test.txt index 83592f6ef70..2685f179b00 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -111,7 +111,7 @@ trustme==1.1.0 ; platform_machine != "i686" # via -r requirements/test.in typer==0.9.0 # via python-on-whales -typing-extensions==4.11.0 +typing-extensions==4.12.2 # via # annotated-types # mypy From 6ff4ac97e6b0e98bdf761bf7620258968ecd6fa1 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 24 Sep 2024 16:48:58 +0100 Subject: [PATCH 293/296] Bump uvloop (#9276) (#9278) (cherry picked from commit ccbd2c5e3364dbf0425f097ae911ab84f44758dc) --- .github/workflows/ci-cd.yml | 4 ++-- requirements/base.txt | 2 +- requirements/constraints.txt | 4 ++-- requirements/dev.txt | 4 ++-- requirements/lint.txt | 2 +- requirements/test.txt | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 17632dba6e6..4d77978d1e3 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -125,7 +125,7 @@ jobs: needs: gen_llhttp strategy: matrix: - pyver: [3.8, 3.9, '3.10', '3.11', '3.12'] + pyver: [3.8, 3.9, '3.10', '3.11', '3.12', '3.13'] no-extensions: ['', 'Y'] os: [ubuntu, macos, windows] experimental: [false] @@ -142,7 +142,7 @@ jobs: os: ubuntu experimental: false - os: ubuntu - pyver: "3.13" + pyver: "3.14" experimental: true no-extensions: 'Y' fail-fast: true diff --git a/requirements/base.txt b/requirements/base.txt index 4dd5dc1f05b..6359f4d60c7 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -36,7 +36,7 @@ pycares==4.3.0 # via aiodns pycparser==2.21 # via cffi -uvloop==0.19.0 ; platform_system != "Windows" and implementation_name == "cpython" +uvloop==0.21.0b1 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in yarl==1.12.0 # via -r requirements/runtime-deps.in diff --git a/requirements/constraints.txt b/requirements/constraints.txt index d08dae712ab..485480490a6 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -188,7 +188,7 @@ pyyaml==6.0.1 # via pre-commit re-assert==1.1.0 # via -r requirements/test.in -regex==2021.11.10 +regex==2024.9.11 # via re-assert requests==2.31.0 # via @@ -262,7 +262,7 @@ uritemplate==4.1.1 # via gidgethub urllib3==1.26.7 # via requests -uvloop==0.19.0 ; platform_system != "Windows" +uvloop==0.21.0b1 ; platform_system != "Windows" # via # -r requirements/base.in # -r requirements/lint.in diff --git a/requirements/dev.txt b/requirements/dev.txt index f34ba86729f..a7e4fd44ecb 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -179,7 +179,7 @@ pyyaml==6.0.1 # via pre-commit re-assert==1.1.0 # via -r requirements/test.in -regex==2023.6.3 +regex==2024.9.11 # via re-assert requests==2.31.0 # via @@ -249,7 +249,7 @@ uritemplate==4.1.1 # via gidgethub urllib3==2.0.4 # via requests -uvloop==0.19.0 ; platform_system != "Windows" and implementation_name == "cpython" +uvloop==0.21.0b1 ; platform_system != "Windows" and implementation_name == "cpython" # via # -r requirements/base.in # -r requirements/lint.in diff --git a/requirements/lint.txt b/requirements/lint.txt index 57f4824ceac..5f1b068cb1c 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -107,7 +107,7 @@ typing-extensions==4.12.2 # typer urllib3==2.2.1 # via requests -uvloop==0.19.0 ; platform_system != "Windows" +uvloop==0.21.0b1 ; platform_system != "Windows" # via -r requirements/lint.in virtualenv==20.24.2 # via pre-commit diff --git a/requirements/test.txt b/requirements/test.txt index 2685f179b00..9900af5bd19 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -92,7 +92,7 @@ python-on-whales==0.72.0 # via -r requirements/test.in re-assert==1.1.0 # via -r requirements/test.in -regex==2023.6.3 +regex==2024.9.11 # via re-assert requests==2.31.0 # via python-on-whales @@ -121,7 +121,7 @@ typing-extensions==4.12.2 # typer urllib3==2.0.4 # via requests -uvloop==0.19.0 ; platform_system != "Windows" and implementation_name == "cpython" +uvloop==0.21.0b1 ; platform_system != "Windows" and implementation_name == "cpython" # via -r requirements/base.in wait-for-it==2.2.2 # via -r requirements/test.in From 81bb9cb46c644ed6e0803af75fed1ac37017ab2c Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 19:09:39 +0100 Subject: [PATCH 294/296] [PR #9284/a4f9eca4 backport][3.10] Bump aiohttp-theme (#9286) **This is a backport of PR #9284 as merged into master (a4f9eca487d09cf4b960aa27b4ea5e8129e29697).** Co-authored-by: Sam Bull --- .readthedocs.yml | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/doc-spelling.txt | 2 +- requirements/doc.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 7e9c11160e5..1b66ee7c0e4 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -11,7 +11,7 @@ submodules: recursive: true build: - os: ubuntu-22.04 + os: ubuntu-24.04 tools: python: "3.11" diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 485480490a6..b9a6c80e86f 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -10,7 +10,7 @@ aiodns==3.2.0 ; sys_platform == "linux" or sys_platform == "darwin" # -r requirements/runtime-deps.in aiohappyeyeballs==2.3.4 # via -r requirements/runtime-deps.in -aiohttp-theme==0.1.6 +aiohttp-theme==0.1.7 # via -r requirements/doc.in aioredis==2.0.1 # via -r requirements/lint.in diff --git a/requirements/dev.txt b/requirements/dev.txt index a7e4fd44ecb..92ec10aa4c1 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -10,7 +10,7 @@ aiodns==3.2.0 ; sys_platform == "linux" or sys_platform == "darwin" # -r requirements/runtime-deps.in aiohappyeyeballs==2.3.4 # via -r requirements/runtime-deps.in -aiohttp-theme==0.1.6 +aiohttp-theme==0.1.7 # via -r requirements/doc.in aioredis==2.0.1 # via -r requirements/lint.in diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 9ee15189662..5b8419574af 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -4,7 +4,7 @@ # # pip-compile --allow-unsafe --output-file=requirements/doc-spelling.txt --resolver=backtracking --strip-extras requirements/doc-spelling.in # -aiohttp-theme==0.1.6 +aiohttp-theme==0.1.7 # via -r requirements/doc.in alabaster==0.7.13 # via sphinx diff --git a/requirements/doc.txt b/requirements/doc.txt index d9e7fb0ad7f..5a27f896ad9 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -4,7 +4,7 @@ # # pip-compile --allow-unsafe --output-file=requirements/doc.txt --resolver=backtracking --strip-extras requirements/doc.in # -aiohttp-theme==0.1.6 +aiohttp-theme==0.1.7 # via -r requirements/doc.in alabaster==0.7.13 # via sphinx From 238991c9f5aadbfe7b743d66e560b3e2c9e027ca Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 19:17:09 +0100 Subject: [PATCH 295/296] [PR #9284/a4f9eca4 backport][3.11] Bump aiohttp-theme (#9287) **This is a backport of PR #9284 as merged into master (a4f9eca487d09cf4b960aa27b4ea5e8129e29697).** Co-authored-by: Sam Bull --- .readthedocs.yml | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/doc-spelling.txt | 2 +- requirements/doc.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 7e9c11160e5..1b66ee7c0e4 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -11,7 +11,7 @@ submodules: recursive: true build: - os: ubuntu-22.04 + os: ubuntu-24.04 tools: python: "3.11" diff --git a/requirements/constraints.txt b/requirements/constraints.txt index fdf1e30f283..64ab607cf31 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -10,7 +10,7 @@ aiodns==3.2.0 ; sys_platform == "linux" or sys_platform == "darwin" # -r requirements/runtime-deps.in aiohappyeyeballs==2.4.0 # via -r requirements/runtime-deps.in -aiohttp-theme==0.1.6 +aiohttp-theme==0.1.7 # via -r requirements/doc.in aioredis==2.0.1 # via -r requirements/lint.in diff --git a/requirements/dev.txt b/requirements/dev.txt index 81b1c1f6988..e170cac365f 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -10,7 +10,7 @@ aiodns==3.2.0 ; sys_platform == "linux" or sys_platform == "darwin" # -r requirements/runtime-deps.in aiohappyeyeballs==2.4.0 # via -r requirements/runtime-deps.in -aiohttp-theme==0.1.6 +aiohttp-theme==0.1.7 # via -r requirements/doc.in aioredis==2.0.1 # via -r requirements/lint.in diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 57cfa253fe7..fae36c2f105 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -4,7 +4,7 @@ # # pip-compile --allow-unsafe --output-file=requirements/doc-spelling.txt --resolver=backtracking --strip-extras requirements/doc-spelling.in # -aiohttp-theme==0.1.6 +aiohttp-theme==0.1.7 # via -r requirements/doc.in alabaster==0.7.13 # via sphinx diff --git a/requirements/doc.txt b/requirements/doc.txt index 49c8f3864ac..e696c59b1a6 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -4,7 +4,7 @@ # # pip-compile --allow-unsafe --output-file=requirements/doc.txt --resolver=backtracking --strip-extras requirements/doc.in # -aiohttp-theme==0.1.6 +aiohttp-theme==0.1.7 # via -r requirements/doc.in alabaster==0.7.13 # via sphinx From 2272c2b952e87048922cb69ce6daa3a7340677c1 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 24 Sep 2024 19:40:11 +0100 Subject: [PATCH 296/296] Release v3.10.6 (#9288) --- CHANGES.rst | 169 +++++++++++++++++-------------------- CHANGES/4414.doc | 1 - CHANGES/4650.bugfix | 1 - CHANGES/5343.bugfix | 1 - CHANGES/6485.bugfix.rst | 1 - CHANGES/6494.bugfix.rst | 1 - CHANGES/6732.bugfix | 1 - CHANGES/6764.doc.rst | 1 - CHANGES/6807.bugfix.rst | 1 - CHANGES/7167.bugfix.rst | 1 - CHANGES/8564.feature.rst | 1 - CHANGES/8748.feature.rst | 1 - CHANGES/8768.bugfix.rst | 1 - CHANGES/8823.bugfix.rst | 1 - CHANGES/8845.bugfix.rst | 1 - CHANGES/8847.misc.rst | 1 - CHANGES/8858.bugfix.rst | 1 - CHANGES/8875.bugfix.rst | 1 - CHANGES/8876.bugfix.rst | 1 - CHANGES/8878.bugfix.rst | 1 - CHANGES/8898.bugfix.rst | 1 - CHANGES/8908.bugfix.rst | 1 - CHANGES/8929.bugfix.rst | 1 - CHANGES/8947.misc.rst | 1 - CHANGES/8967.bugfix.rst | 1 - CHANGES/8968.doc.rst | 1 - CHANGES/8990.bugfix.rst | 1 - CHANGES/8991.doc.rst | 1 - CHANGES/8992.bugfix.rst | 1 - CHANGES/8998.bugfix.rst | 1 - CHANGES/9004.packaging.rst | 1 - CHANGES/9018.bugfix.rst | 1 - CHANGES/9029.bugfix.rst | 1 - CHANGES/9030.bugfix.rst | 1 - CHANGES/9031.misc.rst | 1 - CHANGES/9032.bugfix.rst | 3 - CHANGES/9052.bugfix.rst | 1 - CHANGES/9054.misc.rst | 1 - CHANGES/9063.bugfix.rst | 1 - CHANGES/9068.misc.rst | 3 - CHANGES/9095.misc.rst | 1 - CHANGES/9108.bugfix.rst | 1 - CHANGES/9137.bugfix.rst | 2 - CHANGES/9140.bugfix.rst | 1 - CHANGES/9158.misc.rst | 3 - CHANGES/9160.bugfix | 1 - CHANGES/9167.bugfix.rst | 1 - CHANGES/9168.misc.rst | 1 - CHANGES/9169.misc.rst | 1 - CHANGES/9170.misc.rst | 1 - CHANGES/9172.misc.rst | 1 - CHANGES/9173.misc.rst | 1 - CHANGES/9174.misc.rst | 1 - CHANGES/9175.misc.rst | 1 - CHANGES/9200.breaking.rst | 3 - CHANGES/9203.misc.rst | 3 - CHANGES/9204.misc.rst | 1 - CHANGES/9239.bugfix.rst | 1 - CHANGES/9241.misc.rst | 1 - CHANGES/9267.breaking.rst | 1 - CHANGES/9267.bugfix.rst | 1 - aiohttp/__init__.py | 2 +- 62 files changed, 80 insertions(+), 162 deletions(-) delete mode 100644 CHANGES/4414.doc delete mode 100644 CHANGES/4650.bugfix delete mode 100644 CHANGES/5343.bugfix delete mode 100644 CHANGES/6485.bugfix.rst delete mode 100644 CHANGES/6494.bugfix.rst delete mode 100644 CHANGES/6732.bugfix delete mode 100644 CHANGES/6764.doc.rst delete mode 100644 CHANGES/6807.bugfix.rst delete mode 100644 CHANGES/7167.bugfix.rst delete mode 100644 CHANGES/8564.feature.rst delete mode 100644 CHANGES/8748.feature.rst delete mode 100644 CHANGES/8768.bugfix.rst delete mode 100644 CHANGES/8823.bugfix.rst delete mode 100644 CHANGES/8845.bugfix.rst delete mode 100644 CHANGES/8847.misc.rst delete mode 100644 CHANGES/8858.bugfix.rst delete mode 100644 CHANGES/8875.bugfix.rst delete mode 100644 CHANGES/8876.bugfix.rst delete mode 100644 CHANGES/8878.bugfix.rst delete mode 100644 CHANGES/8898.bugfix.rst delete mode 100644 CHANGES/8908.bugfix.rst delete mode 100644 CHANGES/8929.bugfix.rst delete mode 100644 CHANGES/8947.misc.rst delete mode 100644 CHANGES/8967.bugfix.rst delete mode 100644 CHANGES/8968.doc.rst delete mode 100644 CHANGES/8990.bugfix.rst delete mode 100644 CHANGES/8991.doc.rst delete mode 100644 CHANGES/8992.bugfix.rst delete mode 100644 CHANGES/8998.bugfix.rst delete mode 100644 CHANGES/9004.packaging.rst delete mode 100644 CHANGES/9018.bugfix.rst delete mode 100644 CHANGES/9029.bugfix.rst delete mode 100644 CHANGES/9030.bugfix.rst delete mode 100644 CHANGES/9031.misc.rst delete mode 100644 CHANGES/9032.bugfix.rst delete mode 100644 CHANGES/9052.bugfix.rst delete mode 100644 CHANGES/9054.misc.rst delete mode 100644 CHANGES/9063.bugfix.rst delete mode 100644 CHANGES/9068.misc.rst delete mode 100644 CHANGES/9095.misc.rst delete mode 100644 CHANGES/9108.bugfix.rst delete mode 100644 CHANGES/9137.bugfix.rst delete mode 100644 CHANGES/9140.bugfix.rst delete mode 100644 CHANGES/9158.misc.rst delete mode 100644 CHANGES/9160.bugfix delete mode 100644 CHANGES/9167.bugfix.rst delete mode 120000 CHANGES/9168.misc.rst delete mode 120000 CHANGES/9169.misc.rst delete mode 120000 CHANGES/9170.misc.rst delete mode 120000 CHANGES/9172.misc.rst delete mode 100644 CHANGES/9173.misc.rst delete mode 100644 CHANGES/9174.misc.rst delete mode 120000 CHANGES/9175.misc.rst delete mode 100644 CHANGES/9200.breaking.rst delete mode 100644 CHANGES/9203.misc.rst delete mode 100644 CHANGES/9204.misc.rst delete mode 100644 CHANGES/9239.bugfix.rst delete mode 120000 CHANGES/9241.misc.rst delete mode 100644 CHANGES/9267.breaking.rst delete mode 120000 CHANGES/9267.bugfix.rst diff --git a/CHANGES.rst b/CHANGES.rst index cff8a8a0e2e..f6010b9840a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,100 +10,90 @@ .. towncrier release notes start -3.10.6rc2 (2024-09-23) -====================== +3.10.6 (2024-09-24) +=================== -No significant changes. +Bug fixes +--------- +- Added :exc:`aiohttp.ClientConnectionResetError`. Client code that previously threw :exc:`ConnectionResetError` + will now throw this -- by :user:`Dreamsorcerer`. ----- + *Related issues and pull requests on GitHub:* + :issue:`9137`. -3.10.6rc1 (2024-09-22) -====================== -Removals and backward incompatible breaking changes ---------------------------------------------------- -- Increased minimum yarl version to 1.12.0 -- by :user:`bdraco`. +- Fixed an unclosed transport ``ResourceWarning`` on web handlers -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`9267`. - - - - ----- + :issue:`8875`. -3.10.6rc0 (2024-09-22) -====================== -Bug fixes ---------- - -- Implemented binding to IPv6 addresses in the pytest server fixture. +- Fixed resolve_host() 'Task was destroyed but is pending' errors -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`4650`. + :issue:`8967`. -- Fixed StreamResponse.prepared to return True after EOF is sent -- by :user:`arthurdarcet`. +- Fixed handling of some file-like objects (e.g. ``tarfile.extractfile()``) which raise ``AttributeError`` instead of ``OSError`` when ``fileno`` fails for streaming payload data -- by :user:`ReallyReivax`. *Related issues and pull requests on GitHub:* - :issue:`5343`. + :issue:`6732`. -- Fixed ``Response.text`` when body is a ``Payload`` -- by :user:`Dreamsorcerer`. +- Fixed web router not matching pre-encoded URLs (requires yarl 1.9.6+) -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`6485`. + :issue:`8898`, :issue:`9267`. -- Added support for URL credentials with empty (zero-length) username, e.g. ``https://:password@host`` -- by :user:`shuckc` +- Fixed an error when trying to add a route for multiple methods with a path containing a regex pattern -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`6494`. + :issue:`8998`. -- Fixed handling of some file-like objects (e.g. ``tarfile.extractfile()``) which raise ``AttributeError`` instead of ``OSError`` when ``fileno`` fails for streaming payload data -- by :user:`ReallyReivax`. +- Fixed ``Response.text`` when body is a ``Payload`` -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`6732`. + :issue:`6485`. -- Stopped logging exceptions from ``web.run_app()`` that would be raised regardless -- by :user:`Dreamsorcerer`. +- Fixed compressed requests failing when no body was provided -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`6807`. + :issue:`9108`. -- Changed ``make_mocked_request()`` to use empty payload by default -- by :user:`rahulnht`. +- Fixed client incorrectly reusing a connection when the previous message had not been fully sent -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`7167`. + :issue:`8992`. -- Used more precise type for ``ClientResponseError.headers``, fixing some type errors when using them -- by :user:`Dreamorcerer`. +- Fixed race condition that could cause server to close connection incorrectly at keepalive timeout -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`8768`. + :issue:`9140`. @@ -115,11 +105,11 @@ Bug fixes -- Changed behavior when returning an invalid response to send a 500 response -- by :user:`Dreamsorcerer`. +- Fixed error handling after 100-continue so server sends 500 response instead of disconnecting -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`8845`. + :issue:`8876`. @@ -131,158 +121,141 @@ Bug fixes -- Fixed an unclosed transport ``ResourceWarning`` on web handlers -- by :user:`Dreamsorcerer`. - - - *Related issues and pull requests on GitHub:* - :issue:`8875`. - - - -- Fixed error handling after 100-continue so server sends 500 response instead of disconnecting -- by :user:`Dreamsorcerer`. +- Added support for URL credentials with empty (zero-length) username, e.g. ``https://:password@host`` -- by :user:`shuckc` *Related issues and pull requests on GitHub:* - :issue:`8876`. + :issue:`6494`. -- Fixed response reading from closed session to throw an error immediately instead of timing out -- by :user:`Dreamsorcerer`. +- Stopped logging exceptions from ``web.run_app()`` that would be raised regardless -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`8878`. + :issue:`6807`. -- Fixed web router not matching pre-encoded URLs (requires yarl 1.9.6+) -- by :user:`Dreamsorcerer`. +- Implemented binding to IPv6 addresses in the pytest server fixture. *Related issues and pull requests on GitHub:* - :issue:`8898`. - - - -- Fixed ``CancelledError`` from one cleanup context stopping other contexts from completing -- by :user:`Dreamsorcerer`. + :issue:`4650`. - *Related issues and pull requests on GitHub:* - :issue:`8908`. - +- Fixed the incorrect use of flags for ``getnameinfo()`` in the Resolver --by :user:`GitNMLee` -- Fixed ``Site.name`` when host is an empty string -- by :user:`Dreamsorcerer`. + Link-Local IPv6 addresses can now be handled by the Resolver correctly. *Related issues and pull requests on GitHub:* - :issue:`8929`. + :issue:`9032`. -- Fixed resolve_host() 'Task was destroyed but is pending' errors -- by :user:`Dreamsorcerer`. +- Fixed StreamResponse.prepared to return True after EOF is sent -- by :user:`arthurdarcet`. *Related issues and pull requests on GitHub:* - :issue:`8967`. + :issue:`5343`. -- Fixed changing scheme/host in ``Response.clone()`` for absolute URLs -- by :user:`Dreamsorcerer`. +- Changed ``make_mocked_request()`` to use empty payload by default -- by :user:`rahulnht`. *Related issues and pull requests on GitHub:* - :issue:`8990`. + :issue:`7167`. -- Fixed client incorrectly reusing a connection when the previous message had not been fully sent -- by :user:`Dreamsorcerer`. +- Used more precise type for ``ClientResponseError.headers``, fixing some type errors when using them -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`8992`. + :issue:`8768`. -- Fixed an error when trying to add a route for multiple methods with a path containing a regex pattern -- by :user:`Dreamsorcerer`. +- Changed behavior when returning an invalid response to send a 500 response -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`8998`. + :issue:`8845`. -- Updated Python parser to reject messages after a close message, matching C parser behaviour -- by :user:`Dreamsorcerer`. +- Fixed response reading from closed session to throw an error immediately instead of timing out -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`9018`. + :issue:`8878`. -- Fixed creation of ``SSLContext`` inside of :py:class:`aiohttp.TCPConnector` with multiple event loops in different threads -- by :user:`bdraco`. +- Fixed ``CancelledError`` from one cleanup context stopping other contexts from completing -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`9029`. + :issue:`8908`. -- Fixed (on Python 3.11+) some edge cases where a task cancellation may get incorrectly suppressed -- by :user:`Dreamsorcerer`. +- Fixed changing scheme/host in ``Response.clone()`` for absolute URLs -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`9030`. - + :issue:`8990`. -- Fixed the incorrect use of flags for ``getnameinfo()`` in the Resolver --by :user:`GitNMLee` - Link-Local IPv6 addresses can now be handled by the Resolver correctly. +- Fixed ``Site.name`` when host is an empty string -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`9032`. + :issue:`8929`. -- Fixed exception information getting lost on ``HttpProcessingError`` -- by :user:`Dreamsorcerer`. +- Updated Python parser to reject messages after a close message, matching C parser behaviour -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`9052`. + :issue:`9018`. -- Fixed ``If-None-Match`` not using weak comparison -- by :user:`Dreamsorcerer`. +- Fixed creation of ``SSLContext`` inside of :py:class:`aiohttp.TCPConnector` with multiple event loops in different threads -- by :user:`bdraco`. *Related issues and pull requests on GitHub:* - :issue:`9063`. + :issue:`9029`. -- Fixed compressed requests failing when no body was provided -- by :user:`Dreamsorcerer`. +- Fixed (on Python 3.11+) some edge cases where a task cancellation may get incorrectly suppressed -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`9108`. + :issue:`9030`. -- Added :exc:`aiohttp.ClientConnectionResetError`. Client code that previously threw :exc:`ConnectionResetError` - will now throw this -- by :user:`Dreamsorcerer`. +- Fixed exception information getting lost on ``HttpProcessingError`` -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`9137`. + :issue:`9052`. -- Fixed race condition that could cause server to close connection incorrectly at keepalive timeout -- by :user:`Dreamosorcerer`. +- Fixed ``If-None-Match`` not using weak comparison -- by :user:`Dreamsorcerer`. *Related issues and pull requests on GitHub:* - :issue:`9140`. + :issue:`9063`. @@ -322,6 +295,14 @@ Features +- Declared Python 3.13 supported -- by :user:`bdraco`. + + + *Related issues and pull requests on GitHub:* + :issue:`8748`. + + + Removals and backward incompatible breaking changes --------------------------------------------------- @@ -336,6 +317,14 @@ Removals and backward incompatible breaking changes +- Increased minimum yarl version to 1.12.0 -- by :user:`bdraco`. + + + *Related issues and pull requests on GitHub:* + :issue:`9267`. + + + Improved documentation ---------------------- diff --git a/CHANGES/4414.doc b/CHANGES/4414.doc deleted file mode 100644 index b4be46afee8..00000000000 --- a/CHANGES/4414.doc +++ /dev/null @@ -1 +0,0 @@ -Clarified that ``GracefulExit`` needs to be handled in ``AppRunner`` and ``ServerRunner`` when using ``handle_signals=True``. -- by :user:`Daste745` diff --git a/CHANGES/4650.bugfix b/CHANGES/4650.bugfix deleted file mode 100644 index e3e17b00ae8..00000000000 --- a/CHANGES/4650.bugfix +++ /dev/null @@ -1 +0,0 @@ -Implemented binding to IPv6 addresses in the pytest server fixture. diff --git a/CHANGES/5343.bugfix b/CHANGES/5343.bugfix deleted file mode 100644 index 4e33071ea94..00000000000 --- a/CHANGES/5343.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fixed StreamResponse.prepared to return True after EOF is sent -- by :user:`arthurdarcet`. diff --git a/CHANGES/6485.bugfix.rst b/CHANGES/6485.bugfix.rst deleted file mode 100644 index b1d912f1579..00000000000 --- a/CHANGES/6485.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed ``Response.text`` when body is a ``Payload`` -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/6494.bugfix.rst b/CHANGES/6494.bugfix.rst deleted file mode 100644 index 3827644f0d1..00000000000 --- a/CHANGES/6494.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Added support for URL credentials with empty (zero-length) username, e.g. ``https://:password@host`` -- by :user:`shuckc` diff --git a/CHANGES/6732.bugfix b/CHANGES/6732.bugfix deleted file mode 100644 index a460d7cd695..00000000000 --- a/CHANGES/6732.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fixed handling of some file-like objects (e.g. ``tarfile.extractfile()``) which raise ``AttributeError`` instead of ``OSError`` when ``fileno`` fails for streaming payload data -- by :user:`ReallyReivax`. diff --git a/CHANGES/6764.doc.rst b/CHANGES/6764.doc.rst deleted file mode 100644 index dea2019fc76..00000000000 --- a/CHANGES/6764.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Clarified that auth parameter in ClientSession will persist and be included with any request to any origin, even during redirects to different origins. -- by :user:`MaximZemskov`. diff --git a/CHANGES/6807.bugfix.rst b/CHANGES/6807.bugfix.rst deleted file mode 100644 index 4eb07b9e0da..00000000000 --- a/CHANGES/6807.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Stopped logging exceptions from ``web.run_app()`` that would be raised regardless -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/7167.bugfix.rst b/CHANGES/7167.bugfix.rst deleted file mode 100644 index 766f1438b66..00000000000 --- a/CHANGES/7167.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Changed ``make_mocked_request()`` to use empty payload by default -- by :user:`rahulnht`. diff --git a/CHANGES/8564.feature.rst b/CHANGES/8564.feature.rst deleted file mode 100644 index 1eac9d12217..00000000000 --- a/CHANGES/8564.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Improved type on ``params`` to match the underlying type allowed by ``yarl`` -- by :user:`lpetre`. diff --git a/CHANGES/8748.feature.rst b/CHANGES/8748.feature.rst deleted file mode 100644 index 7794d16e4dc..00000000000 --- a/CHANGES/8748.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Declared Python 3.13 supported -- by :user:`bdraco`. diff --git a/CHANGES/8768.bugfix.rst b/CHANGES/8768.bugfix.rst deleted file mode 100644 index 18512163572..00000000000 --- a/CHANGES/8768.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Used more precise type for ``ClientResponseError.headers``, fixing some type errors when using them -- by :user:`Dreamorcerer`. diff --git a/CHANGES/8823.bugfix.rst b/CHANGES/8823.bugfix.rst deleted file mode 100644 index ea18e65fd4a..00000000000 --- a/CHANGES/8823.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed Python parser chunked handling with multiple Transfer-Encoding values -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/8845.bugfix.rst b/CHANGES/8845.bugfix.rst deleted file mode 100644 index c37a0095ed3..00000000000 --- a/CHANGES/8845.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Changed behavior when returning an invalid response to send a 500 response -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/8847.misc.rst b/CHANGES/8847.misc.rst deleted file mode 100644 index 58f61d48420..00000000000 --- a/CHANGES/8847.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Improved performance of making requests when there are no auto headers to skip -- by :user:`bdraco`. diff --git a/CHANGES/8858.bugfix.rst b/CHANGES/8858.bugfix.rst deleted file mode 100644 index e4efa91a2fd..00000000000 --- a/CHANGES/8858.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Stopped adding a default Content-Type header when response has no content -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/8875.bugfix.rst b/CHANGES/8875.bugfix.rst deleted file mode 100644 index fa33df05ae2..00000000000 --- a/CHANGES/8875.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed an unclosed transport ``ResourceWarning`` on web handlers -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/8876.bugfix.rst b/CHANGES/8876.bugfix.rst deleted file mode 100644 index 539eeb4c7d3..00000000000 --- a/CHANGES/8876.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed error handling after 100-continue so server sends 500 response instead of disconnecting -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/8878.bugfix.rst b/CHANGES/8878.bugfix.rst deleted file mode 100644 index df53dea3c35..00000000000 --- a/CHANGES/8878.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed response reading from closed session to throw an error immediately instead of timing out -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/8898.bugfix.rst b/CHANGES/8898.bugfix.rst deleted file mode 100644 index 0de6646c8cb..00000000000 --- a/CHANGES/8898.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed web router not matching pre-encoded URLs (requires yarl 1.9.6+) -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/8908.bugfix.rst b/CHANGES/8908.bugfix.rst deleted file mode 100644 index 0eb450431db..00000000000 --- a/CHANGES/8908.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed ``CancelledError`` from one cleanup context stopping other contexts from completing -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/8929.bugfix.rst b/CHANGES/8929.bugfix.rst deleted file mode 100644 index 229d5abd0e7..00000000000 --- a/CHANGES/8929.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed ``Site.name`` when host is an empty string -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/8947.misc.rst b/CHANGES/8947.misc.rst deleted file mode 100644 index 277ba915c50..00000000000 --- a/CHANGES/8947.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Exported ``aiohttp.TraceRequestHeadersSentParams`` -- by :user:`Hadock-is-ok`. diff --git a/CHANGES/8967.bugfix.rst b/CHANGES/8967.bugfix.rst deleted file mode 100644 index 1046f36bd8b..00000000000 --- a/CHANGES/8967.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed resolve_host() 'Task was destroyed but is pending' errors -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/8968.doc.rst b/CHANGES/8968.doc.rst deleted file mode 100644 index 3420794586f..00000000000 --- a/CHANGES/8968.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Clarified which timeout exceptions happen on which timeouts -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/8990.bugfix.rst b/CHANGES/8990.bugfix.rst deleted file mode 100644 index 9a9783103fd..00000000000 --- a/CHANGES/8990.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed changing scheme/host in ``Response.clone()`` for absolute URLs -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/8991.doc.rst b/CHANGES/8991.doc.rst deleted file mode 100644 index c29850c4f3c..00000000000 --- a/CHANGES/8991.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Updated ``ClientSession`` parameters to match current code -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/8992.bugfix.rst b/CHANGES/8992.bugfix.rst deleted file mode 100644 index bc41d5feb81..00000000000 --- a/CHANGES/8992.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed client incorrectly reusing a connection when the previous message had not been fully sent -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/8998.bugfix.rst b/CHANGES/8998.bugfix.rst deleted file mode 100644 index 1b6b189e7ea..00000000000 --- a/CHANGES/8998.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed an error when trying to add a route for multiple methods with a path containing a regex pattern -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/9004.packaging.rst b/CHANGES/9004.packaging.rst deleted file mode 100644 index f6b0f8ff2a3..00000000000 --- a/CHANGES/9004.packaging.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed ``test_client_session_timeout_zero`` to not require internet access -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/9018.bugfix.rst b/CHANGES/9018.bugfix.rst deleted file mode 100644 index 2de6d142900..00000000000 --- a/CHANGES/9018.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Updated Python parser to reject messages after a close message, matching C parser behaviour -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/9029.bugfix.rst b/CHANGES/9029.bugfix.rst deleted file mode 100644 index 7ca956e3832..00000000000 --- a/CHANGES/9029.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed creation of ``SSLContext`` inside of :py:class:`aiohttp.TCPConnector` with multiple event loops in different threads -- by :user:`bdraco`. diff --git a/CHANGES/9030.bugfix.rst b/CHANGES/9030.bugfix.rst deleted file mode 100644 index 2e9d48f5359..00000000000 --- a/CHANGES/9030.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed (on Python 3.11+) some edge cases where a task cancellation may get incorrectly suppressed -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/9031.misc.rst b/CHANGES/9031.misc.rst deleted file mode 100644 index 1874a4deddd..00000000000 --- a/CHANGES/9031.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Avoided tracing overhead in the http writer when there are no active traces -- by user:`bdraco`. diff --git a/CHANGES/9032.bugfix.rst b/CHANGES/9032.bugfix.rst deleted file mode 100644 index 8c8d81f6319..00000000000 --- a/CHANGES/9032.bugfix.rst +++ /dev/null @@ -1,3 +0,0 @@ -Fixed the incorrect use of flags for ``getnameinfo()`` in the Resolver --by :user:`GitNMLee` - -Link-Local IPv6 addresses can now be handled by the Resolver correctly. diff --git a/CHANGES/9052.bugfix.rst b/CHANGES/9052.bugfix.rst deleted file mode 100644 index 913288d3368..00000000000 --- a/CHANGES/9052.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed exception information getting lost on ``HttpProcessingError`` -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/9054.misc.rst b/CHANGES/9054.misc.rst deleted file mode 100644 index ddc71f453e5..00000000000 --- a/CHANGES/9054.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Improved performance of reify Cython implementation -- by :user:`bdraco`. diff --git a/CHANGES/9063.bugfix.rst b/CHANGES/9063.bugfix.rst deleted file mode 100644 index e512677b9c8..00000000000 --- a/CHANGES/9063.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed ``If-None-Match`` not using weak comparison -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/9068.misc.rst b/CHANGES/9068.misc.rst deleted file mode 100644 index 7ce5ec5c839..00000000000 --- a/CHANGES/9068.misc.rst +++ /dev/null @@ -1,3 +0,0 @@ -Use :meth:`URL.extend_query() ` to extend query params (requires yarl 1.11.0+) -- by :user:`bdraco`. - -If yarl is older than 1.11.0, the previous slower hand rolled version will be used. diff --git a/CHANGES/9095.misc.rst b/CHANGES/9095.misc.rst deleted file mode 100644 index f4a06cb09d6..00000000000 --- a/CHANGES/9095.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Improved performance of checking if a host is an IP Address -- by :user:`bdraco`. diff --git a/CHANGES/9108.bugfix.rst b/CHANGES/9108.bugfix.rst deleted file mode 100644 index 8be000575e8..00000000000 --- a/CHANGES/9108.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed compressed requests failing when no body was provided -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/9137.bugfix.rst b/CHANGES/9137.bugfix.rst deleted file mode 100644 index d99802095bd..00000000000 --- a/CHANGES/9137.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added :exc:`aiohttp.ClientConnectionResetError`. Client code that previously threw :exc:`ConnectionResetError` -will now throw this -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/9140.bugfix.rst b/CHANGES/9140.bugfix.rst deleted file mode 100644 index c9b8f7bf4ea..00000000000 --- a/CHANGES/9140.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed race condition that could cause server to close connection incorrectly at keepalive timeout -- by :user:`Dreamosorcerer`. diff --git a/CHANGES/9158.misc.rst b/CHANGES/9158.misc.rst deleted file mode 100644 index 8d87623c056..00000000000 --- a/CHANGES/9158.misc.rst +++ /dev/null @@ -1,3 +0,0 @@ -Significantly improved performance of middlewares -- by :user:`bdraco`. - -The construction of the middleware wrappers is now cached and is built once per handler instead of on every request. diff --git a/CHANGES/9160.bugfix b/CHANGES/9160.bugfix deleted file mode 100644 index 253cfd07d50..00000000000 --- a/CHANGES/9160.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fixed badly encoded charset crashing when getting response text instead of falling back to charset detector. diff --git a/CHANGES/9167.bugfix.rst b/CHANGES/9167.bugfix.rst deleted file mode 100644 index 4c33c8ad355..00000000000 --- a/CHANGES/9167.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Rejected `\n` in `reason` values to avoid sending broken HTTP messages -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/9168.misc.rst b/CHANGES/9168.misc.rst deleted file mode 120000 index d6a2f2aaaab..00000000000 --- a/CHANGES/9168.misc.rst +++ /dev/null @@ -1 +0,0 @@ -9174.misc.rst \ No newline at end of file diff --git a/CHANGES/9169.misc.rst b/CHANGES/9169.misc.rst deleted file mode 120000 index d6a2f2aaaab..00000000000 --- a/CHANGES/9169.misc.rst +++ /dev/null @@ -1 +0,0 @@ -9174.misc.rst \ No newline at end of file diff --git a/CHANGES/9170.misc.rst b/CHANGES/9170.misc.rst deleted file mode 120000 index e41cbad0125..00000000000 --- a/CHANGES/9170.misc.rst +++ /dev/null @@ -1 +0,0 @@ -9158.misc.rst \ No newline at end of file diff --git a/CHANGES/9172.misc.rst b/CHANGES/9172.misc.rst deleted file mode 120000 index d6a2f2aaaab..00000000000 --- a/CHANGES/9172.misc.rst +++ /dev/null @@ -1 +0,0 @@ -9174.misc.rst \ No newline at end of file diff --git a/CHANGES/9173.misc.rst b/CHANGES/9173.misc.rst deleted file mode 100644 index 6fcc098747f..00000000000 --- a/CHANGES/9173.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Improved performance of starting web requests when there is no response prepare hook -- by :user:`bdraco`. diff --git a/CHANGES/9174.misc.rst b/CHANGES/9174.misc.rst deleted file mode 100644 index 13dc00ec1de..00000000000 --- a/CHANGES/9174.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Improved performance of web requests -- by :user:`bdraco`. diff --git a/CHANGES/9175.misc.rst b/CHANGES/9175.misc.rst deleted file mode 120000 index d6a2f2aaaab..00000000000 --- a/CHANGES/9175.misc.rst +++ /dev/null @@ -1 +0,0 @@ -9174.misc.rst \ No newline at end of file diff --git a/CHANGES/9200.breaking.rst b/CHANGES/9200.breaking.rst deleted file mode 100644 index 0282e165c41..00000000000 --- a/CHANGES/9200.breaking.rst +++ /dev/null @@ -1,3 +0,0 @@ -Improved middleware performance -- by :user:`bdraco`. - -The ``set_current_app`` method was removed from ``UrlMappingMatchInfo`` because it is no longer used, and it was unlikely external caller would ever use it. diff --git a/CHANGES/9203.misc.rst b/CHANGES/9203.misc.rst deleted file mode 100644 index 766fdc01a57..00000000000 --- a/CHANGES/9203.misc.rst +++ /dev/null @@ -1,3 +0,0 @@ -Significantly improved performance of expiring cookies -- by :user:`bdraco`. - -Expiring cookies has been redesigned to use :mod:`heapq` instead of a linear search, to better scale. diff --git a/CHANGES/9204.misc.rst b/CHANGES/9204.misc.rst deleted file mode 100644 index 9f3196fa5be..00000000000 --- a/CHANGES/9204.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Significantly sped up filtering cookies -- by :user:`bdraco`. diff --git a/CHANGES/9239.bugfix.rst b/CHANGES/9239.bugfix.rst deleted file mode 100644 index 95b229742ce..00000000000 --- a/CHANGES/9239.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Changed :py:meth:`ClientResponse.raise_for_status() ` to only release the connection when invoked outside an ``async with`` context -- by :user:`Dreamsorcerer`. diff --git a/CHANGES/9241.misc.rst b/CHANGES/9241.misc.rst deleted file mode 120000 index d6a2f2aaaab..00000000000 --- a/CHANGES/9241.misc.rst +++ /dev/null @@ -1 +0,0 @@ -9174.misc.rst \ No newline at end of file diff --git a/CHANGES/9267.breaking.rst b/CHANGES/9267.breaking.rst deleted file mode 100644 index 82fec1d21b4..00000000000 --- a/CHANGES/9267.breaking.rst +++ /dev/null @@ -1 +0,0 @@ -Increased minimum yarl version to 1.12.0 -- by :user:`bdraco`. diff --git a/CHANGES/9267.bugfix.rst b/CHANGES/9267.bugfix.rst deleted file mode 120000 index 2a85c7ec63c..00000000000 --- a/CHANGES/9267.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -8898.bugfix.rst \ No newline at end of file diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py index 02fde739bdb..8830d340940 100644 --- a/aiohttp/__init__.py +++ b/aiohttp/__init__.py @@ -1,4 +1,4 @@ -__version__ = "3.10.6rc2" +__version__ = "3.10.6" from typing import TYPE_CHECKING, Tuple