From 302511f9bd41e736abe36ea84d6b25b3db82bc68 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 18:14:54 +0000 Subject: [PATCH 1/4] Bump idna from 3.11 to 3.15 (#12524) Bumps [idna](https://github.com/kjd/idna) from 3.11 to 3.15.
Changelog

Sourced from idna's changelog.

3.15 (2026-05-12)

Thanks to Felix Yan, Stan Ulbrych, and metsw24-max for contributions to this release.

3.14 (2026-05-10)

Thanks to Stan Ulbrych for reporting the issue.

3.13 (2026-04-22)

3.12 (2026-04-21)

Thanks to Rodrigo Nogueira for contributions to this release.

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=idna&package-manager=pip&previous-version=3.11&new-version=3.15)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/base-ft.txt | 2 +- requirements/base.txt | 2 +- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/doc-spelling.txt | 2 +- requirements/doc.txt | 2 +- requirements/lint.txt | 2 +- requirements/runtime-deps.txt | 2 +- requirements/test-common.txt | 2 +- requirements/test-ft.txt | 2 +- requirements/test.txt | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/requirements/base-ft.txt b/requirements/base-ft.txt index 2834ebfb7ca..c11d33a7bda 100644 --- a/requirements/base-ft.txt +++ b/requirements/base-ft.txt @@ -24,7 +24,7 @@ frozenlist==1.8.0 # aiosignal gunicorn==26.0.0 # via -r requirements/base-ft.in -idna==3.11 +idna==3.15 # via yarl multidict==6.7.1 # via diff --git a/requirements/base.txt b/requirements/base.txt index 7f639a1a99b..c19ff93a897 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -24,7 +24,7 @@ frozenlist==1.8.0 # aiosignal gunicorn==26.0.0 # via -r requirements/base.in -idna==3.11 +idna==3.15 # via yarl multidict==6.7.1 # via diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 52b2de05f78..eeb1f668c0a 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -100,7 +100,7 @@ gunicorn==26.0.0 # via -r requirements/base.in identify==2.6.19 # via pre-commit -idna==3.11 +idna==3.15 # via # requests # trustme diff --git a/requirements/dev.txt b/requirements/dev.txt index 0b8a30b6097..55dc2cadea1 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -98,7 +98,7 @@ gunicorn==26.0.0 # via -r requirements/base.in identify==2.6.19 # via pre-commit -idna==3.11 +idna==3.15 # via # requests # trustme diff --git a/requirements/doc-spelling.txt b/requirements/doc-spelling.txt index 1df62c2252f..20aa6b758be 100644 --- a/requirements/doc-spelling.txt +++ b/requirements/doc-spelling.txt @@ -18,7 +18,7 @@ click==8.3.3 # via towncrier docutils==0.21.2 # via sphinx -idna==3.11 +idna==3.15 # via requests imagesize==2.0.0 # via sphinx diff --git a/requirements/doc.txt b/requirements/doc.txt index 0b958126b20..22d2bc43694 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -18,7 +18,7 @@ click==8.3.3 # via towncrier docutils==0.21.2 # via sphinx -idna==3.11 +idna==3.15 # via requests imagesize==2.0.0 # via sphinx diff --git a/requirements/lint.txt b/requirements/lint.txt index 680c2e9100f..398c4d3d86f 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -56,7 +56,7 @@ frozenlist==1.8.0 # aiosignal identify==2.6.19 # via pre-commit -idna==3.11 +idna==3.15 # via # trustme # yarl diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt index 8d0850c963f..07175fbdba3 100644 --- a/requirements/runtime-deps.txt +++ b/requirements/runtime-deps.txt @@ -22,7 +22,7 @@ frozenlist==1.8.0 # via # -r requirements/runtime-deps.in # aiosignal -idna==3.11 +idna==3.15 # via yarl multidict==6.7.1 # via diff --git a/requirements/test-common.txt b/requirements/test-common.txt index ee8b0d93214..9f7c156906e 100644 --- a/requirements/test-common.txt +++ b/requirements/test-common.txt @@ -44,7 +44,7 @@ frozenlist==1.8.0 # via # aiohttp # aiosignal -idna==3.11 +idna==3.15 # via # trustme # yarl diff --git a/requirements/test-ft.txt b/requirements/test-ft.txt index cb301668dc4..ab23e38d9f7 100644 --- a/requirements/test-ft.txt +++ b/requirements/test-ft.txt @@ -61,7 +61,7 @@ frozenlist==1.8.0 # aiosignal gunicorn==26.0.0 # via -r requirements/base-ft.in -idna==3.11 +idna==3.15 # via # trustme # yarl diff --git a/requirements/test.txt b/requirements/test.txt index 3b59afa8db4..0282fb3804c 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -61,7 +61,7 @@ frozenlist==1.8.0 # aiosignal gunicorn==26.0.0 # via -r requirements/base.in -idna==3.11 +idna==3.15 # via # trustme # yarl From 5d738e24532daf9371616cf9485a6cba8d3ec5a2 Mon Sep 17 00:00:00 2001 From: nightcityblade Date: Thu, 14 May 2026 02:49:47 +0800 Subject: [PATCH 2/4] Skip IPv6-only tests when IPv6 is unavailable (#12531) --- CHANGES/9891.contrib.rst | 2 ++ tests/test_connector.py | 12 +++++++++++- tests/test_test_utils.py | 18 +++++++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 CHANGES/9891.contrib.rst diff --git a/CHANGES/9891.contrib.rst b/CHANGES/9891.contrib.rst new file mode 100644 index 00000000000..907c7f357f5 --- /dev/null +++ b/CHANGES/9891.contrib.rst @@ -0,0 +1,2 @@ +Skipped IPv6-only connector and test utility tests when the runtime does not support +IPv6 -- by :user:`nightcityblade`. diff --git a/tests/test_connector.py b/tests/test_connector.py index 87d71b999f8..ad672fe75f2 100644 --- a/tests/test_connector.py +++ b/tests/test_connector.py @@ -53,6 +53,14 @@ else: _RequestMaker = Any +HAS_IPV6: bool = socket.has_ipv6 +if HAS_IPV6: # pragma: no branch + try: + with socket.socket(socket.AF_INET6, socket.SOCK_STREAM): + pass + except OSError: # pragma: no cover + HAS_IPV6 = False + @pytest.fixture def key() -> ConnectionKey: @@ -887,6 +895,7 @@ def get_extra_info(param: str) -> object: await conn.close() +@pytest.mark.skipif(not HAS_IPV6, reason="IPv6 is not available") @pytest.mark.parametrize( ("happy_eyeballs_delay"), [0.1, 0.25, None], @@ -979,7 +988,8 @@ async def create_connection( await conn.close() -async def test_tcp_connector_interleave(make_client_request: _RequestMaker) -> None: +@pytest.mark.skipif(not HAS_IPV6, reason="IPv6 is not available") +async def test_tcp_connector_interleave(make_client_request: _RequestMaker) -> None: # type: ignore[misc] loop = asyncio.get_running_loop() conn = aiohttp.TCPConnector(interleave=2) diff --git a/tests/test_test_utils.py b/tests/test_test_utils.py index 10e20d0b58a..ef9acf9af22 100644 --- a/tests/test_test_utils.py +++ b/tests/test_test_utils.py @@ -26,6 +26,14 @@ if sys.version_info >= (3, 11): from typing import assert_type +HAS_IPV6: bool = socket.has_ipv6 +if HAS_IPV6: # pragma: no branch + try: + with socket.socket(socket.AF_INET6, socket.SOCK_STREAM): + pass + except OSError: # pragma: no cover + HAS_IPV6 = False + _TestClient = TestClient[web.Request, web.Application] _hello_world_str = "Hello, world" @@ -368,7 +376,15 @@ async def test_custom_port( @pytest.mark.parametrize( ("hostname", "expected_host"), - [("127.0.0.1", "127.0.0.1"), ("localhost", "127.0.0.1"), ("::1", "::1")], + [ + ("127.0.0.1", "127.0.0.1"), + ("localhost", "127.0.0.1"), + pytest.param( + "::1", + "::1", + marks=pytest.mark.skipif(not HAS_IPV6, reason="IPv6 is not available"), + ), + ], ) async def test_test_server_hostnames(hostname: str, expected_host: str) -> None: app = _create_example_app() From ae48ab3fbc989418badf625aa3082d1f16ab16fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= Date: Wed, 13 May 2026 21:53:39 +0300 Subject: [PATCH 3/4] Replaced `get_event_loop()` with `get_running_loop()` (#12425) --- CHANGES/12425.misc.rst | 2 + CONTRIBUTORS.txt | 1 + aiohttp/web_app.py | 2 +- aiohttp/web_fileresponse.py | 2 +- aiohttp/web_runner.py | 12 ++-- aiohttp/worker.py | 3 - tests/test_base_protocol.py | 38 ++++++------ tests/test_client_functional.py | 12 ++-- tests/test_client_request.py | 2 +- tests/test_client_ws_functional.py | 12 ++-- tests/test_connector.py | 2 +- tests/test_flowcontrol_streams.py | 2 +- tests/test_multipart.py | 4 +- tests/test_run_app.py | 6 +- tests/test_streams.py | 98 +++++++++++++++--------------- tests/test_web_app.py | 2 +- tests/test_web_functional.py | 4 +- tests/test_web_request.py | 24 +++++--- tests/test_web_runner.py | 2 +- 19 files changed, 118 insertions(+), 112 deletions(-) create mode 100644 CHANGES/12425.misc.rst diff --git a/CHANGES/12425.misc.rst b/CHANGES/12425.misc.rst new file mode 100644 index 00000000000..68beff45ac5 --- /dev/null +++ b/CHANGES/12425.misc.rst @@ -0,0 +1,2 @@ +Replaced uses of the ``get_event_loop()`` function with ``get_running_loop()`` +-- by :user:`agronholm`. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index f843e10cc98..84e58805f29 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -15,6 +15,7 @@ Alec Hanefeld Alejandro Gómez Aleksandr Danshyn Aleksey Kutepov +Alex Grönholm Alex Hayes Alex Key Alex Khomchenko diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index 095d5e806cd..1ff6e48bac2 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -254,7 +254,7 @@ def debug(self) -> bool: DeprecationWarning, stacklevel=2, ) - return asyncio.get_event_loop().get_debug() + return asyncio.get_running_loop().get_debug() def _reg_subapp_signals(self, subapp: "Application") -> None: def reg_handler(signame: str) -> None: diff --git a/aiohttp/web_fileresponse.py b/aiohttp/web_fileresponse.py index 1b4495ea4e1..d45afc0dd7d 100644 --- a/aiohttp/web_fileresponse.py +++ b/aiohttp/web_fileresponse.py @@ -103,7 +103,7 @@ async def _sendfile_fallback( # controlled by the constructor's chunk_size argument. chunk_size = self._chunk_size - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() chunk = await loop.run_in_executor( None, self._seek_and_read, fobj, offset, min(chunk_size, count) ) diff --git a/aiohttp/web_runner.py b/aiohttp/web_runner.py index 3d2277570aa..82c3bd277f8 100644 --- a/aiohttp/web_runner.py +++ b/aiohttp/web_runner.py @@ -127,7 +127,7 @@ def name(self) -> str: async def start(self) -> None: await super().start() - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() server = self._runner.server assert server is not None self._server = await loop.create_server( @@ -170,7 +170,7 @@ def name(self) -> str: async def start(self) -> None: await super().start() - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() server = self._runner.server assert server is not None self._server = await loop.create_unix_server( @@ -185,7 +185,7 @@ class NamedPipeSite(BaseSite): __slots__ = ("_path",) def __init__(self, runner: "BaseRunner[Any]", path: str) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() if not isinstance( loop, asyncio.ProactorEventLoop # type: ignore[attr-defined] ): @@ -201,7 +201,7 @@ def name(self) -> str: async def start(self) -> None: await super().start() - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() server = self._runner.server assert server is not None _server = await loop.start_serving_pipe( # type: ignore[attr-defined] @@ -241,7 +241,7 @@ def name(self) -> str: async def start(self) -> None: await super().start() - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() server = self._runner.server assert server is not None self._server = await loop.create_server( @@ -286,7 +286,7 @@ def sites(self) -> set[BaseSite]: return set(self._sites) async def setup(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() if self._handle_signals: try: diff --git a/aiohttp/worker.py b/aiohttp/worker.py index 88c67016c54..25dfd284c6b 100644 --- a/aiohttp/worker.py +++ b/aiohttp/worker.py @@ -238,9 +238,6 @@ class GunicornUVLoopWebWorker(GunicornWebWorker): def init_process(self) -> None: import uvloop - # Setup uvloop policy, so that every - # asyncio.get_event_loop() will create an instance - # of uvloop event loop. asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) super().init_process() diff --git a/tests/test_base_protocol.py b/tests/test_base_protocol.py index 234e9927c02..2fe3325cac7 100644 --- a/tests/test_base_protocol.py +++ b/tests/test_base_protocol.py @@ -9,14 +9,14 @@ async def test_loop() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() asyncio.set_event_loop(None) pr = BaseProtocol(loop) assert pr._loop is loop async def test_pause_writing() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop) assert not pr._paused assert pr.writing_paused is False @@ -26,7 +26,7 @@ async def test_pause_writing() -> None: async def test_pause_reading_no_transport() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() parser = mock.create_autospec(HttpParser, spec_set=True, instance=True) pr = BaseProtocol(loop, parser=parser) pr.pause_reading() @@ -34,7 +34,7 @@ async def test_pause_reading_no_transport() -> None: async def test_pause_reading_stub_transport() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() parser = mock.create_autospec(HttpParser, spec_set=True, instance=True) pr = BaseProtocol(loop, parser=parser) tr = asyncio.Transport() @@ -46,7 +46,7 @@ async def test_pause_reading_stub_transport() -> None: async def test_resume_reading_stub_transport() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() parser = mock.create_autospec(HttpParser, spec_set=True, instance=True) pr = BaseProtocol(loop, parser=parser) tr = asyncio.Transport() @@ -57,7 +57,7 @@ async def test_resume_reading_stub_transport() -> None: async def test_resume_writing_no_waiters() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop=loop) pr.pause_writing() assert pr._paused @@ -66,7 +66,7 @@ async def test_resume_writing_no_waiters() -> None: async def test_resume_writing_waiter_done() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop=loop) waiter = mock.Mock(done=mock.Mock(return_value=True)) pr._drain_waiter = waiter @@ -77,7 +77,7 @@ async def test_resume_writing_waiter_done() -> None: async def test_connection_made() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop=loop) tr = mock.Mock() assert pr.transport is None @@ -86,7 +86,7 @@ async def test_connection_made() -> None: async def test_connection_lost_not_paused() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop=loop) tr = mock.Mock() pr.connection_made(tr) @@ -97,7 +97,7 @@ async def test_connection_lost_not_paused() -> None: async def test_connection_lost_paused_without_waiter() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop=loop) tr = mock.Mock() pr.connection_made(tr) @@ -109,7 +109,7 @@ async def test_connection_lost_paused_without_waiter() -> None: async def test_connection_lost_waiter_done() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop=loop) pr._paused = True waiter = mock.Mock(done=mock.Mock(return_value=True)) @@ -120,7 +120,7 @@ async def test_connection_lost_waiter_done() -> None: async def test_drain_lost() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop=loop) tr = mock.Mock() pr.connection_made(tr) @@ -130,7 +130,7 @@ async def test_drain_lost() -> None: async def test_drain_not_paused() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop=loop) tr = mock.Mock() pr.connection_made(tr) @@ -140,7 +140,7 @@ async def test_drain_not_paused() -> None: async def test_resume_drain_waited() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop=loop) tr = mock.Mock() pr.connection_made(tr) @@ -156,7 +156,7 @@ async def test_resume_drain_waited() -> None: async def test_lost_drain_waited_ok() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop=loop) tr = mock.Mock() pr.connection_made(tr) @@ -172,7 +172,7 @@ async def test_lost_drain_waited_ok() -> None: async def test_lost_drain_waited_exception() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop=loop) tr = mock.Mock() pr.connection_made(tr) @@ -191,7 +191,7 @@ async def test_lost_drain_waited_exception() -> None: async def test_lost_drain_cancelled() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop=loop) tr = mock.Mock() pr.connection_made(tr) @@ -215,7 +215,7 @@ async def wait() -> None: async def test_resume_drain_cancelled() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop=loop) tr = mock.Mock() pr.connection_made(tr) @@ -239,7 +239,7 @@ async def wait() -> None: async def test_parallel_drain_race_condition() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() pr = BaseProtocol(loop=loop) tr = mock.Mock() pr.connection_made(tr) diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py index c98f3b2a168..b6efc7e07c9 100644 --- a/tests/test_client_functional.py +++ b/tests/test_client_functional.py @@ -1218,7 +1218,7 @@ async def handler(request: web.Request) -> web.Response: async def test_timeout_on_reading_data( aiohttp_client: AiohttpClient, mocker: MockerFixture ) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() fut = loop.create_future() @@ -1281,7 +1281,7 @@ async def handler(request: web.Request) -> NoReturn: async def test_readline_error_on_conn_close(aiohttp_client: AiohttpClient) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() async def handler(request: web.Request) -> NoReturn: resp = web.StreamResponse() @@ -4000,7 +4000,7 @@ async def handler(request: web.Request) -> web.Response: async def test_server_close_keepalive_connection(unused_tcp_port: int) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() class Proto(asyncio.Protocol): def connection_made(self, transport: asyncio.BaseTransport) -> None: @@ -4041,7 +4041,7 @@ def connection_lost(self, exc: BaseException | None) -> None: async def test_handle_keepalive_on_closed_connection(unused_tcp_port: int) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() class Proto(asyncio.Protocol): def connection_made(self, transport: asyncio.BaseTransport) -> None: @@ -4092,7 +4092,7 @@ async def handler(request: web.Request) -> NoReturn: def exception_handler(loop: object, context: object) -> None: """Skip log messages about destroyed but pending tasks""" - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() loop.set_exception_handler(exception_handler) app = web.Application() @@ -4112,7 +4112,7 @@ def exception_handler(loop: object, context: object) -> None: async def test_await_after_cancelling(aiohttp_client: AiohttpClient) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() async def handler(request: web.Request) -> web.Response: return web.Response() diff --git a/tests/test_client_request.py b/tests/test_client_request.py index e028433d61d..bc72704ff28 100644 --- a/tests/test_client_request.py +++ b/tests/test_client_request.py @@ -2063,7 +2063,7 @@ async def test_expect100_with_body_becomes_empty( # Create a request req = make_client_request( - "POST", URL("http://test.example.com/"), loop=asyncio.get_event_loop() + "POST", URL("http://test.example.com/"), loop=asyncio.get_running_loop() ) req._body = mock.Mock() # Start with a body diff --git a/tests/test_client_ws_functional.py b/tests/test_client_ws_functional.py index 8c054ceb690..411f310d5ed 100644 --- a/tests/test_client_ws_functional.py +++ b/tests/test_client_ws_functional.py @@ -288,7 +288,7 @@ async def handler(request: web.Request) -> web.WebSocketResponse: async def test_ping_pong(aiohttp_client: AiohttpClient) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() closed = loop.create_future() async def handler(request: web.Request) -> web.WebSocketResponse: @@ -324,7 +324,7 @@ async def handler(request: web.Request) -> web.WebSocketResponse: async def test_ping_pong_manual(aiohttp_client: AiohttpClient) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() closed = loop.create_future() async def handler(request: web.Request) -> web.WebSocketResponse: @@ -483,7 +483,7 @@ async def handler(request: web.Request) -> web.WebSocketResponse: async def test_close_from_server(aiohttp_client: AiohttpClient) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() closed = loop.create_future() async def handler(request: web.Request) -> web.WebSocketResponse: @@ -515,7 +515,7 @@ async def handler(request: web.Request) -> web.WebSocketResponse: async def test_close_manual(aiohttp_client: AiohttpClient) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() closed = loop.create_future() async def handler(request: web.Request) -> web.WebSocketResponse: @@ -609,7 +609,7 @@ async def handler(request: web.Request) -> NoReturn: async def test_close_cancel(aiohttp_client: AiohttpClient) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() async def handler(request: web.Request) -> NoReturn: ws = web.WebSocketResponse() @@ -1260,7 +1260,7 @@ async def handler(request: web.Request) -> web.WebSocketResponse: async def test_closed_async_for(aiohttp_client: AiohttpClient) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() closed = loop.create_future() async def handler(request: web.Request) -> web.WebSocketResponse: diff --git a/tests/test_connector.py b/tests/test_connector.py index ad672fe75f2..1019437bd0d 100644 --- a/tests/test_connector.py +++ b/tests/test_connector.py @@ -2049,7 +2049,7 @@ async def test_cleanup_close_ssl_transport( # type: ignore[misc] testset[ssl_key] = deque([(proto, 10)]) loop = mock.Mock() - new_time = asyncio.get_event_loop().time() + 300 + new_time = asyncio.get_running_loop().time() + 300 loop.time.return_value = new_time conn = aiohttp.BaseConnector(enable_cleanup_closed=True) conn._loop = loop diff --git a/tests/test_flowcontrol_streams.py b/tests/test_flowcontrol_streams.py index 6580f462946..5e5388094c4 100644 --- a/tests/test_flowcontrol_streams.py +++ b/tests/test_flowcontrol_streams.py @@ -117,7 +117,7 @@ async def test_resumed_on_eof(self, stream: streams.StreamReader) -> None: async def test_stream_reader_eof_when_full() -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() parser = mock.create_autospec(HttpParser, spec_set=True, instance=True) protocol = BaseProtocol(loop=loop, parser=parser) protocol.transport = asyncio.Transport() diff --git a/tests/test_multipart.py b/tests/test_multipart.py index aec0bcae922..604c3f9cf29 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -677,7 +677,7 @@ async def test_reading_long_part(self) -> None: size = 2 * DEFAULT_CHUNK_SIZE protocol = mock.Mock(_reading_paused=False) stream = StreamReader( - protocol, DEFAULT_CHUNK_SIZE, loop=asyncio.get_event_loop() + protocol, DEFAULT_CHUNK_SIZE, loop=asyncio.get_running_loop() ) stream.feed_data(b"0" * size + b"\r\n--:--") stream.feed_eof() @@ -1731,7 +1731,7 @@ async def test_body_part_reader_payload_as_bytes() -> None: # Create a mock BodyPartReader headers = HeadersDictProxy(CIMultiDict({CONTENT_TYPE: "text/plain"})) protocol = mock.Mock(_reading_paused=False) - stream = StreamReader(protocol, 2**16, loop=asyncio.get_event_loop()) + stream = StreamReader(protocol, 2**16, loop=asyncio.get_running_loop()) body_part = BodyPartReader(BOUNDARY, headers, stream) # Create the payload diff --git a/tests/test_run_app.py b/tests/test_run_app.py index 130ee8862dd..68131a49eb1 100644 --- a/tests/test_run_app.py +++ b/tests/test_run_app.py @@ -888,7 +888,7 @@ def test_run_app_cancels_all_pending_tasks( async def on_startup(app: web.Application) -> None: nonlocal task - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() task = loop.create_task(asyncio.sleep(1000)) app.on_startup.append(on_startup) @@ -909,7 +909,7 @@ async def coro() -> int: async def on_startup(app: web.Application) -> None: nonlocal task - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() task = loop.create_task(coro()) app.on_startup.append(on_startup) @@ -935,7 +935,7 @@ async def fail() -> None: async def on_startup(app: web.Application) -> None: nonlocal task - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() task = loop.create_task(fail()) await asyncio.sleep(0.01) diff --git a/tests/test_streams.py b/tests/test_streams.py index 2f1e105c289..e11218680d4 100644 --- a/tests/test_streams.py +++ b/tests/test_streams.py @@ -28,7 +28,7 @@ def chunkify(seq: Sequence[_T], n: int) -> Iterator[Sequence[_T]]: async def create_stream() -> streams.StreamReader: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() protocol = mock.Mock(_reading_paused=False) stream = streams.StreamReader(protocol, DEFAULT_CHUNK_SIZE, loop=loop) stream.feed_data(DATA) @@ -77,11 +77,11 @@ class TestStreamReader: DATA: bytes = b"line1\nline2\nline3\n" def _make_one(self, limit: int = DEFAULT_CHUNK_SIZE) -> streams.StreamReader: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() return streams.StreamReader(mock.Mock(_reading_paused=False), limit, loop=loop) async def test_create_waiter(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = self._make_one() stream._waiter = loop.create_future # type: ignore[assignment] with pytest.raises(RuntimeError): @@ -103,7 +103,7 @@ async def test_at_eof(self) -> None: assert stream.at_eof() async def test_wait_eof(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = self._make_one() wait_task = loop.create_task(stream.wait_eof()) @@ -118,7 +118,7 @@ async def cb() -> None: await t async def test_wait_eof_eof(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = self._make_one() stream.feed_eof() @@ -155,7 +155,7 @@ async def test_read_zero(self) -> None: assert self.DATA == data async def test_read(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() # Read bytes. stream = self._make_one() read_task = loop.create_task(stream.read(30)) @@ -207,7 +207,7 @@ async def test_read_up_to(self) -> None: assert b"e2" == data async def test_read_eof(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() # Read bytes, stop at eof. stream = self._make_one() read_task = loop.create_task(stream.read(1024)) @@ -244,7 +244,7 @@ async def test_read_eof_unread_data_no_warning(self) -> None: assert not internal_logger.warning.called async def test_read_until_eof(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() # Read all bytes until eof. stream = self._make_one() read_task = loop.create_task(stream.read(-1)) @@ -274,7 +274,7 @@ async def test_read_exception(self) -> None: await stream.read(2) async def test_readline(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() # Read one line. 'readline' will need to wait for the data # to come from 'cb' stream = self._make_one() @@ -311,7 +311,7 @@ async def test_readline_limit_with_existing_data(self) -> None: assert b"line2\n" == data async def test_readline_limit(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() # Read one line. StreamReaders are fed with data after # their 'readline' methods are called. stream = self._make_one(limit=4) @@ -384,7 +384,7 @@ async def test_readline_exception(self) -> None: @pytest.mark.parametrize("separator", [b"*", b"**"]) async def test_readuntil(self, separator: bytes) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() # Read one chunk. 'readuntil' will need to wait for the data # to come from 'cb' stream = self._make_one() @@ -423,7 +423,7 @@ async def test_readuntil_limit_with_existing_data(self, separator: bytes) -> Non @pytest.mark.parametrize("separator", [b"$", b"$$"]) async def test_readuntil_limit(self, separator: bytes) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() # Read one chunk. StreamReaders are fed with data after # their 'readuntil' methods are called. stream = self._make_one(limit=4) @@ -525,7 +525,7 @@ async def test_readexactly_zero_or_less(self) -> None: assert self.DATA == data async def test_readexactly(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() # Read exact number of bytes. stream = self._make_one() @@ -547,7 +547,7 @@ def cb() -> None: assert self.DATA == data async def test_readexactly_eof(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() # Read exact number of bytes (eof). stream = self._make_one() n = 2 * len(self.DATA) @@ -650,7 +650,7 @@ async def test_exception(self) -> None: assert stream.exception() is exc async def test_exception_waiter(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = self._make_one() async def set_err() -> None: @@ -664,7 +664,7 @@ async def set_err() -> None: t1.result() async def test_exception_cancel(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = self._make_one() async def read_a_line() -> None: @@ -680,7 +680,7 @@ async def read_a_line() -> None: assert stream._waiter is None async def test_readany_eof(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = self._make_one() read_task = loop.create_task(stream.readany()) loop.call_soon(stream.feed_data, b"chunk1\n") @@ -692,7 +692,7 @@ async def test_readany_eof(self) -> None: assert b"" == data async def test_readany_empty_eof(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = self._make_one() stream.feed_eof() read_task = loop.create_task(stream.readany()) @@ -742,7 +742,7 @@ async def test_read_nowait_exception(self) -> None: stream.read_nowait() async def test_read_nowait_waiter(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = self._make_one() stream.feed_data(b"line\n") stream._waiter = loop.create_future() @@ -751,7 +751,7 @@ async def test_read_nowait_waiter(self) -> None: stream.read_nowait() async def test_readchunk(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = self._make_one() def cb() -> None: @@ -774,7 +774,7 @@ def cb() -> None: assert not end_of_chunk async def test_readchunk_wait_eof(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = self._make_one() async def cb() -> None: @@ -827,7 +827,7 @@ async def test_readany_chunk_end_race(self) -> None: data = await stream.readany() assert data == b"part1" - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() task = loop.create_task(stream.readany()) # Give a chance for task to create waiter and start waiting for it. @@ -994,7 +994,7 @@ async def test_read_empty_chunks(self) -> None: async def test_readchunk_separate_http_chunk_tail(self) -> None: # Test that stream.readchunk returns (b'', True) when end of # http chunk received after body - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = self._make_one() stream.begin_http_chunk_receiving() @@ -1080,7 +1080,7 @@ async def test___repr__exception(self) -> None: assert "" == repr(stream) async def test___repr__waiter(self) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = self._make_one() stream._waiter = loop.create_future() assert repr(stream).startswith(" None: assert buffer._eof async def test_read(self, buffer: streams.DataQueue[bytes]) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() item = b"" def cb() -> None: @@ -1174,7 +1174,7 @@ def cb() -> None: assert item is data async def test_read_eof(self, buffer: streams.DataQueue[bytes]) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() def cb() -> None: buffer.feed_eof() @@ -1185,7 +1185,7 @@ def cb() -> None: await buffer.read() async def test_read_cancelled(self, buffer: streams.DataQueue[bytes]) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() read_task = loop.create_task(buffer.read()) await asyncio.sleep(0) waiter = buffer._waiter @@ -1242,7 +1242,7 @@ async def test_read_exception_with_data( async def test_read_exception_on_wait( self, buffer: streams.DataQueue[bytes] ) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() read_task = loop.create_task(buffer.read()) await asyncio.sleep(0) assert asyncio.isfuture(buffer._waiter) @@ -1261,7 +1261,7 @@ def test_exception(self, buffer: streams.DataQueue[bytes]) -> None: assert buffer.exception() is exc async def test_exception_waiter(self, buffer: streams.DataQueue[bytes]) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() async def set_err() -> None: buffer.set_exception(ValueError()) @@ -1276,7 +1276,7 @@ async def set_err() -> None: async def test_feed_data_waiters(protocol: BaseProtocol) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() reader = streams.StreamReader(protocol, DEFAULT_CHUNK_SIZE, loop=loop) waiter = reader._waiter = loop.create_future() eof_waiter = reader._eof_waiter = loop.create_future() @@ -1293,7 +1293,7 @@ async def test_feed_data_waiters(protocol: BaseProtocol) -> None: async def test_feed_data_completed_waiters(protocol: BaseProtocol) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() reader = streams.StreamReader(protocol, 2**16, loop=loop) waiter = reader._waiter = loop.create_future() @@ -1304,7 +1304,7 @@ async def test_feed_data_completed_waiters(protocol: BaseProtocol) -> None: async def test_feed_eof_waiters(protocol: BaseProtocol) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() reader = streams.StreamReader(protocol, DEFAULT_CHUNK_SIZE, loop=loop) waiter = reader._waiter = loop.create_future() eof_waiter = reader._eof_waiter = loop.create_future() @@ -1319,7 +1319,7 @@ async def test_feed_eof_waiters(protocol: BaseProtocol) -> None: async def test_feed_eof_cancelled(protocol: BaseProtocol) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() reader = streams.StreamReader(protocol, 2**16, loop=loop) waiter = reader._waiter = loop.create_future() eof_waiter = reader._eof_waiter = loop.create_future() @@ -1336,7 +1336,7 @@ async def test_feed_eof_cancelled(protocol: BaseProtocol) -> None: async def test_on_eof(protocol: BaseProtocol) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() reader = streams.StreamReader(protocol, DEFAULT_CHUNK_SIZE, loop=loop) on_eof = mock.Mock() @@ -1357,7 +1357,7 @@ async def test_on_eof_empty_reader() -> None: async def test_on_eof_exc_in_callback(protocol: BaseProtocol) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() reader = streams.StreamReader(protocol, DEFAULT_CHUNK_SIZE, loop=loop) on_eof = mock.Mock() @@ -1381,7 +1381,7 @@ async def test_on_eof_exc_in_callback_empty_stream_reader() -> None: async def test_on_eof_eof_is_set(protocol: BaseProtocol) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() reader = streams.StreamReader(protocol, 2**16, loop=loop) reader.feed_eof() @@ -1392,7 +1392,7 @@ async def test_on_eof_eof_is_set(protocol: BaseProtocol) -> None: async def test_on_eof_eof_is_set_exception(protocol: BaseProtocol) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() reader = streams.StreamReader(protocol, DEFAULT_CHUNK_SIZE, loop=loop) reader.feed_eof() @@ -1405,7 +1405,7 @@ async def test_on_eof_eof_is_set_exception(protocol: BaseProtocol) -> None: async def test_set_exception(protocol: BaseProtocol) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() reader = streams.StreamReader(protocol, 2**16, loop=loop) waiter = reader._waiter = loop.create_future() eof_waiter = reader._eof_waiter = loop.create_future() @@ -1420,7 +1420,7 @@ async def test_set_exception(protocol: BaseProtocol) -> None: async def test_set_exception_cancelled(protocol: BaseProtocol) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() reader = streams.StreamReader(protocol, 2**16, loop=loop) waiter = reader._waiter = loop.create_future() eof_waiter = reader._eof_waiter = loop.create_future() @@ -1438,7 +1438,7 @@ async def test_set_exception_cancelled(protocol: BaseProtocol) -> None: async def test_set_exception_eof_callbacks(protocol: BaseProtocol) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() reader = streams.StreamReader(protocol, DEFAULT_CHUNK_SIZE, loop=loop) on_eof = mock.Mock() @@ -1475,7 +1475,7 @@ async def test_stream_reader_chunks_incomplete() -> None: async def test_data_queue_empty() -> None: # Tests that async looping yields nothing if nothing is there - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() buffer: streams.DataQueue[bytes] = streams.DataQueue(loop) buffer.feed_eof() @@ -1485,7 +1485,7 @@ async def test_data_queue_empty() -> None: async def test_data_queue_items() -> None: # Tests that async looping yields objects identically - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() buffer = streams.DataQueue[str](loop) items = ["a", "b"] @@ -1523,7 +1523,7 @@ async def test_stream_reader_iter_chunks_no_chunked_encoding() -> None: async def test_stream_reader_iter_chunks_chunked_encoding( protocol: BaseProtocol, ) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = streams.StreamReader(protocol, 2**16, loop=loop) for line in DATA.splitlines(keepends=True): stream.begin_http_chunk_receiving() @@ -1545,7 +1545,7 @@ async def test_stream_reader_pause_on_high_water_chunks( protocol: mock.Mock, ) -> None: """Test that reading is paused when chunk count exceeds high water mark.""" - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() # Use small limit so high_water_chunks is small: limit // 16 = 10 stream = streams.StreamReader(protocol, limit=160, loop=loop) @@ -1566,7 +1566,7 @@ async def test_stream_reader_resume_on_low_water_chunks( protocol: mock.Mock, ) -> None: """Test that reading resumes when chunk count drops below low water mark.""" - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() # Use small limit so high_water_chunks is small: limit // 16 = 10 stream = streams.StreamReader(protocol, limit=160, loop=loop) @@ -1597,7 +1597,7 @@ async def test_stream_reader_no_resume_when_chunks_still_high( protocol: mock.Mock, ) -> None: """Test that reading doesn't resume if chunk count is still above low water.""" - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() # Use small limit so high_water_chunks is small: limit // 4 = 10 stream = streams.StreamReader(protocol, limit=40, loop=loop) @@ -1622,7 +1622,7 @@ async def test_stream_reader_read_non_chunked_response( protocol: mock.Mock, ) -> None: """Test that non-chunked responses work correctly (no chunk tracking).""" - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = streams.StreamReader(protocol, limit=40, loop=loop) # Non-chunked: just feed data without begin/end_http_chunk_receiving @@ -1643,7 +1643,7 @@ async def test_stream_reader_resume_non_chunked_when_paused( protocol: mock.Mock, ) -> None: """Test that resume works for non-chunked responses when paused due to size.""" - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() # Small limit so we can trigger pause via size stream = streams.StreamReader(protocol, limit=10, loop=loop) @@ -1673,7 +1673,7 @@ async def test_stream_reader_small_limit_resumes_reading( and low_water_chunks should be at least 2, with high > low to ensure proper flow control. """ - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() stream = streams.StreamReader(protocol, limit=limit, loop=loop) # Verify minimum thresholds are enforced and high > low diff --git a/tests/test_web_app.py b/tests/test_web_app.py index 2f13398085e..de0e0cb2aac 100644 --- a/tests/test_web_app.py +++ b/tests/test_web_app.py @@ -36,7 +36,7 @@ async def test_app_register_on_finish() -> None: async def test_app_register_coro() -> None: app = web.Application() - fut = asyncio.get_event_loop().create_future() + fut = asyncio.get_running_loop().create_future() async def cb(app: web.Application) -> None: await asyncio.sleep(0.001) diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index 823b3e0f14f..bd338723f46 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -97,7 +97,7 @@ async def handler(request: web.Request) -> web.Response: async def test_handler_returns_not_response( aiohttp_server: AiohttpServer, aiohttp_client: AiohttpClient ) -> None: - asyncio.get_event_loop().set_debug(True) + asyncio.get_running_loop().set_debug(True) logger = mock.Mock() async def handler(request: web.Request) -> str: @@ -115,7 +115,7 @@ async def handler(request: web.Request) -> str: async def test_handler_returns_none( aiohttp_server: AiohttpServer, aiohttp_client: AiohttpClient ) -> None: - asyncio.get_event_loop().set_debug(True) + asyncio.get_running_loop().set_debug(True) logger = mock.Mock() async def handler(request: web.Request) -> None: diff --git a/tests/test_web_request.py b/tests/test_web_request.py index 7c0ab5d9be4..9c43f9d609c 100644 --- a/tests/test_web_request.py +++ b/tests/test_web_request.py @@ -836,7 +836,9 @@ def test_clone_headers_dict() -> None: async def test_cannot_clone_after_read(protocol: BaseProtocol) -> None: - payload = StreamReader(protocol, DEFAULT_CHUNK_SIZE, loop=asyncio.get_event_loop()) + payload = StreamReader( + protocol, DEFAULT_CHUNK_SIZE, loop=asyncio.get_running_loop() + ) payload.feed_data(b"data") payload.feed_eof() req = make_mocked_request("GET", "/path", payload=payload) @@ -846,7 +848,7 @@ async def test_cannot_clone_after_read(protocol: BaseProtocol) -> None: async def test_make_too_big_request(protocol: BaseProtocol) -> None: - payload = StreamReader(protocol, 2**16, loop=asyncio.get_event_loop()) + payload = StreamReader(protocol, 2**16, loop=asyncio.get_running_loop()) large_file = 1024**2 * b"x" too_large_file = large_file + b"x" payload.feed_data(too_large_file) @@ -859,7 +861,9 @@ async def test_make_too_big_request(protocol: BaseProtocol) -> None: async def test_request_with_wrong_content_type_encoding(protocol: BaseProtocol) -> None: - payload = StreamReader(protocol, DEFAULT_CHUNK_SIZE, loop=asyncio.get_event_loop()) + payload = StreamReader( + protocol, DEFAULT_CHUNK_SIZE, loop=asyncio.get_running_loop() + ) payload.feed_data(b"{}") payload.feed_eof() headers = {"Content-Type": "text/html; charset=test"} @@ -871,7 +875,7 @@ async def test_request_with_wrong_content_type_encoding(protocol: BaseProtocol) async def test_make_too_big_request_same_size_to_max(protocol: BaseProtocol) -> None: - payload = StreamReader(protocol, 2**16, loop=asyncio.get_event_loop()) + payload = StreamReader(protocol, 2**16, loop=asyncio.get_running_loop()) large_file = 1024**2 * b"x" payload.feed_data(large_file) payload.feed_eof() @@ -882,7 +886,7 @@ async def test_make_too_big_request_same_size_to_max(protocol: BaseProtocol) -> async def test_make_too_big_request_adjust_limit(protocol: BaseProtocol) -> None: - payload = StreamReader(protocol, 2**16, loop=asyncio.get_event_loop()) + payload = StreamReader(protocol, 2**16, loop=asyncio.get_running_loop()) large_file = 1024**2 * b"x" too_large_file = large_file + b"x" payload.feed_data(too_large_file) @@ -894,7 +898,7 @@ async def test_make_too_big_request_adjust_limit(protocol: BaseProtocol) -> None async def test_multipart_formdata(protocol: BaseProtocol) -> None: - payload = StreamReader(protocol, 2**16, loop=asyncio.get_event_loop()) + payload = StreamReader(protocol, 2**16, loop=asyncio.get_running_loop()) payload.feed_data( b"-----------------------------326931944431359\r\n" b'Content-Disposition: form-data; name="a"\r\n' @@ -919,7 +923,9 @@ async def test_multipart_formdata(protocol: BaseProtocol) -> None: async def test_multipart_formdata_field_missing_name(protocol: BaseProtocol) -> None: # Ensure ValueError is raised when Content-Disposition has no name - payload = StreamReader(protocol, DEFAULT_CHUNK_SIZE, loop=asyncio.get_event_loop()) + payload = StreamReader( + protocol, DEFAULT_CHUNK_SIZE, loop=asyncio.get_running_loop() + ) payload.feed_data( b"-----------------------------326931944431359\r\n" b"Content-Disposition: form-data\r\n" # Missing name! @@ -940,7 +946,7 @@ async def test_multipart_formdata_field_missing_name(protocol: BaseProtocol) -> async def test_multipart_formdata_file(protocol: BaseProtocol) -> None: # Make sure file uploads work, even without a content type - payload = StreamReader(protocol, 2**16, loop=asyncio.get_event_loop()) + payload = StreamReader(protocol, 2**16, loop=asyncio.get_running_loop()) payload.feed_data( b"-----------------------------326931944431359\r\n" b'Content-Disposition: form-data; name="a_file"; filename="binary"\r\n' @@ -1018,7 +1024,7 @@ async def test_multipart_formdata_header_too_long(protocol: BaseProtocol) -> Non async def test_make_too_big_request_limit_None(protocol: BaseProtocol) -> None: - payload = StreamReader(protocol, 2**16, loop=asyncio.get_event_loop()) + payload = StreamReader(protocol, 2**16, loop=asyncio.get_running_loop()) large_file = 1024**2 * b"x" too_large_file = large_file + b"x" payload.feed_data(too_large_file) diff --git a/tests/test_web_runner.py b/tests/test_web_runner.py index 77348470cda..c4b7b19e8b7 100644 --- a/tests/test_web_runner.py +++ b/tests/test_web_runner.py @@ -268,7 +268,7 @@ async def test_tcpsite_default_host(make_runner: _RunnerMaker) -> None: m = mock.create_autospec(asyncio.AbstractEventLoop, spec_set=True, instance=True) m.create_server.return_value = mock.create_autospec(asyncio.Server, spec_set=True) with mock.patch( - "asyncio.get_event_loop", autospec=True, spec_set=True, return_value=m + "asyncio.get_running_loop", autospec=True, spec_set=True, return_value=m ): await site.start() From 7eb0e8029cdb270a98a203bc5d4bad32cdc2c19c Mon Sep 17 00:00:00 2001 From: Rodrigo Nogueira Date: Wed, 13 May 2026 16:00:42 -0300 Subject: [PATCH 4/4] Fix CookieError crash with control characters on CPython builds with CVE-2026-3644 patch (#12395) --- CHANGES/12395.bugfix.rst | 4 ++ aiohttp/_cookie_helpers.py | 62 +++++++++++++------ tests/test_cookie_helpers.py | 117 ++++++++++++++++++++++++++++++++++- 3 files changed, 161 insertions(+), 22 deletions(-) create mode 100644 CHANGES/12395.bugfix.rst diff --git a/CHANGES/12395.bugfix.rst b/CHANGES/12395.bugfix.rst new file mode 100644 index 00000000000..e3c67bfa7aa --- /dev/null +++ b/CHANGES/12395.bugfix.rst @@ -0,0 +1,4 @@ +Fixed a crash (:external+python:exc:`~http.cookies.CookieError`) in the cookie parser when receiving cookies +containing ASCII control characters on CPython builds with the :cve:`2026-3644` +patch. The parser now gracefully skips cookies whose value contains control +characters instead of letting the exception propagate -- by :user:`rodrigobnogueira`. diff --git a/aiohttp/_cookie_helpers.py b/aiohttp/_cookie_helpers.py index d545df3ef6c..495f853226e 100644 --- a/aiohttp/_cookie_helpers.py +++ b/aiohttp/_cookie_helpers.py @@ -7,7 +7,7 @@ import re from collections.abc import Sequence -from http.cookies import Morsel +from http.cookies import CookieError, Morsel from typing import cast from .log import internal_logger @@ -106,9 +106,16 @@ def preserve_morsel_with_coded_value(cookie: Morsel[str]) -> Morsel[str]: # bypass validation and set already validated state. This is more stable than # setting protected attributes directly and unlikely to change since it would # break pickling. - mrsl_val.__setstate__( # type: ignore[attr-defined] - {"key": cookie.key, "value": cookie.value, "coded_value": cookie.coded_value} - ) + try: + mrsl_val.__setstate__( # type: ignore[attr-defined] + { + "key": cookie.key, + "value": cookie.value, + "coded_value": cookie.coded_value, + } + ) + except CookieError: + return cookie return mrsl_val @@ -206,10 +213,18 @@ def parse_cookie_header(header: str) -> list[tuple[str, Morsel[str]]]: invalid_names.append(key) else: morsel = Morsel() - morsel.__setstate__( # type: ignore[attr-defined] - {"key": key, "value": _unquote(value), "coded_value": value} - ) - cookies.append((key, morsel)) + try: + morsel.__setstate__( # type: ignore[attr-defined] + { + "key": key, + "value": _unquote(value), + "coded_value": value, + } + ) + except CookieError: + pass + else: + cookies.append((key, morsel)) # Move to next cookie or end i = next_semi + 1 if next_semi != -1 else n @@ -231,9 +246,12 @@ def parse_cookie_header(header: str) -> list[tuple[str, Morsel[str]]]: # bypass validation and set already validated state. This is more stable than # setting protected attributes directly and unlikely to change since it would # break pickling. - morsel.__setstate__( # type: ignore[attr-defined] - {"key": key, "value": _unquote(value), "coded_value": value} - ) + try: + morsel.__setstate__( # type: ignore[attr-defined] + {"key": key, "value": _unquote(value), "coded_value": value} + ) + except CookieError: + continue cookies.append((key, morsel)) @@ -323,15 +341,19 @@ def parse_set_cookie_headers(headers: Sequence[str]) -> list[tuple[str, Morsel[s # Create new morsel current_morsel = Morsel() # Preserve the original value as coded_value (with quotes if present) - # We use __setstate__ instead of the public set() API because it allows us to - # bypass validation and set already validated state. This is more stable than - # setting protected attributes directly and unlikely to change since it would - # break pickling. - current_morsel.__setstate__( # type: ignore[attr-defined] - {"key": key, "value": _unquote(value), "coded_value": value} - ) - parsed_cookies.append((key, current_morsel)) - morsel_seen = True + try: + current_morsel.__setstate__( # type: ignore[attr-defined] + { + "key": key, + "value": _unquote(value), + "coded_value": value, + } + ) + except CookieError: + current_morsel = None + else: + parsed_cookies.append((key, current_morsel)) + morsel_seen = True else: # Invalid cookie string - no value for non-attribute break diff --git a/tests/test_cookie_helpers.py b/tests/test_cookie_helpers.py index fead869d6f3..be6228d698f 100644 --- a/tests/test_cookie_helpers.py +++ b/tests/test_cookie_helpers.py @@ -1134,6 +1134,65 @@ def test_parse_set_cookie_headers_uses_unquote_with_octal( assert morsel.coded_value == expected_coded +@pytest.mark.parametrize( + ("header", "expected_name", "expected_coded"), + [ + pytest.param( + r'name="\012newline\012"', + "name", + r'"\012newline\012"', + id="newline-octal-012", + ), + pytest.param( + r'tab="\011separated\011values"', + "tab", + r'"\011separated\011values"', + id="tab-octal-011", + ), + ], +) +def test_parse_set_cookie_headers_ctl_chars_from_octal( + header: str, expected_name: str, expected_coded: str +) -> None: + """Ensure octal escapes that decode to control characters don't crash the parser. + + CPython builds with the CVE-2026-3644 patch reject control characters in + cookies. When octal unquoting produces a control character, the parser + skips the cookie entirely instead of raising CookieError. + """ + result = parse_set_cookie_headers([header]) + + # On CPython with CVE-2026-3644 patch the cookie is rejected (result is empty); + # on older builds it may be accepted with the decoded value. + # Either way, no crash. + if result: + name, morsel = result[0] + assert name == expected_name + assert morsel.coded_value == expected_coded + + +def test_parse_set_cookie_headers_literal_ctl_chars() -> None: + r"""Ensure literal control characters in a cookie value don't crash the parser. + + If the raw header itself contains a control character (e.g. BEL \\x07), + both the decoded value and coded_value are unsalvageable. The parser + should gracefully skip the cookie instead of raising CookieError. + """ + result = parse_set_cookie_headers(['name="a\x07b"']) + # On CPython with CVE-2026-3644 patch the cookie is skipped; + # on older builds it may be accepted. Either way, no crash. + if result: + assert result[0][0] == "name" + + +def test_parse_set_cookie_headers_literal_ctl_chars_preserves_others() -> None: + """Ensure a cookie with literal control chars doesn't break subsequent cookies.""" + result = parse_set_cookie_headers(['bad="a\x07b"; good=value', "another=cookie"]) + # "good" is an attribute of "bad" (same header), so it's not a separate cookie. + # "another" is in a separate header and must always be preserved. + assert any(name == "another" for name, _ in result) + + # Tests for parse_cookie_header (RFC 6265 compliant Cookie header parser) @@ -1597,8 +1656,17 @@ def test_parse_cookie_header_empty_key_in_fallback( assert name2 == "another" assert morsel2.value == "test" - assert "Cannot load cookie. Illegal cookie name" in caplog.text - assert "''" in caplog.text + +def test_parse_cookie_header_literal_ctl_chars() -> None: + r"""Ensure literal control characters in a cookie value don't crash the parser. + + If the raw header itself contains a control character (e.g. BEL \\x07), + the cookie is unsalvageable. The parser should gracefully skip it. + """ + result = parse_cookie_header('name="a\x07b"; good=cookie') + # On CPython with CVE-2026-3644 patch the bad cookie is skipped; + # on older builds it may be accepted. Either way, no crash. + assert any(name == "good" for name, _ in result) @pytest.mark.parametrize( @@ -1789,3 +1857,48 @@ def test_unquote_compatibility_with_simplecookie(test_value: str) -> None: f"our={_unquote(test_value)!r}, " f"SimpleCookie={simplecookie_unquote(test_value)!r}" ) + + +@pytest.fixture +def mock_strict_morsel( + monkeypatch: pytest.MonkeyPatch, +) -> None: + original_setstate = Morsel.__setstate__ # type: ignore[attr-defined] + + def _mock_setstate(self: Morsel[str], state: dict[str, str]) -> None: + if any(ord(c) < 32 for c in state.get("value", "")): + raise CookieError() + original_setstate(self, state) + + monkeypatch.setattr( + "aiohttp._cookie_helpers.Morsel.__setstate__", + _mock_setstate, + ) + + +@pytest.mark.usefixtures("mock_strict_morsel") +def test_cookie_helpers_cve_fallback() -> None: + # Clean value: mock delegates to original_setstate → succeeds + m: Morsel[str] = Morsel() + m.__setstate__({"key": "k", "value": "clean", "coded_value": "clean"}) # type: ignore[attr-defined] + assert m.key == "k" + + # With strict morsel: any CTL char in value → CookieError → rejected + with pytest.raises(CookieError): + Morsel().__setstate__( # type: ignore[attr-defined] + {"key": "k", "value": "v\n", "coded_value": "v\\012"} + ) + with pytest.raises(CookieError): + Morsel().__setstate__( # type: ignore[attr-defined] + {"key": "k", "value": "v\n", "coded_value": "v\n"} + ) + + cookie: Morsel[str] = Morsel() + cookie._key, cookie._value, cookie._coded_value = "k", "v\n", "v\n" # type: ignore[attr-defined] + assert preserve_morsel_with_coded_value(cookie) is cookie + + assert parse_cookie_header("f=b\x07r;") == [] + assert parse_cookie_header("f=b\x07r") == [] + assert parse_cookie_header('f="b\x07r";') == [] + assert parse_set_cookie_headers(['f="b\x07r";']) == [] + assert parse_set_cookie_headers([r'name="\012newline\012"']) == []