From 2082fbad0b52b7c612c1ccb9875ac04f1469b3dc Mon Sep 17 00:00:00 2001
From: "J. Nick Koston"
Date: Mon, 31 Mar 2025 20:15:15 -1000
Subject: [PATCH 1/9] Increment version to 3.11.16.dev0 (#10661)
---
aiohttp/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py
index aba86dc3a32..acfef952d61 100644
--- a/aiohttp/__init__.py
+++ b/aiohttp/__init__.py
@@ -1,4 +1,4 @@
-__version__ = "3.11.15"
+__version__ = "3.11.16.dev0"
from typing import TYPE_CHECKING, Tuple
From 83a9df574187b432589637a2596e029b0bcf6e33 Mon Sep 17 00:00:00 2001
From: layday
Date: Tue, 1 Apr 2025 12:25:50 +0200
Subject: [PATCH 2/9] =?UTF-8?q?Replace=20deprecated=20`asyncio.iscoroutine?=
=?UTF-8?q?function`=20with=20its=20counterpart=E2=80=A6=20(#10664)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
… from `inspect` (#10634)
(cherry picked from commit 77ad7d7ea173eda1306297d275b2d5f7348f9f60)
---
CHANGES/10634.bugfix.rst | 2 ++
aiohttp/pytest_plugin.py | 6 +++---
aiohttp/web_urldispatcher.py | 8 +++++---
aiohttp/worker.py | 5 ++++-
4 files changed, 14 insertions(+), 7 deletions(-)
create mode 100644 CHANGES/10634.bugfix.rst
diff --git a/CHANGES/10634.bugfix.rst b/CHANGES/10634.bugfix.rst
new file mode 100644
index 00000000000..d6ec64a607e
--- /dev/null
+++ b/CHANGES/10634.bugfix.rst
@@ -0,0 +1,2 @@
+Replaced deprecated ``asyncio.iscoroutinefunction`` with its counterpart from ``inspect``
+-- by :user:`layday`.
diff --git a/aiohttp/pytest_plugin.py b/aiohttp/pytest_plugin.py
index 158fd684b7a..128dc46081d 100644
--- a/aiohttp/pytest_plugin.py
+++ b/aiohttp/pytest_plugin.py
@@ -98,7 +98,7 @@ def pytest_fixture_setup(fixturedef): # type: ignore[no-untyped-def]
if inspect.isasyncgenfunction(func):
# async generator fixture
is_async_gen = True
- elif asyncio.iscoroutinefunction(func):
+ elif inspect.iscoroutinefunction(func):
# regular async fixture
is_async_gen = False
else:
@@ -200,14 +200,14 @@ def _passthrough_loop_context(loop, fast=False): # type: ignore[no-untyped-def]
def pytest_pycollect_makeitem(collector, name, obj): # type: ignore[no-untyped-def]
"""Fix pytest collecting for coroutines."""
- if collector.funcnamefilter(name) and asyncio.iscoroutinefunction(obj):
+ if collector.funcnamefilter(name) and inspect.iscoroutinefunction(obj):
return list(collector._genfunctions(name, obj))
def pytest_pyfunc_call(pyfuncitem): # type: ignore[no-untyped-def]
"""Run coroutines in an event loop instead of a normal function call."""
fast = pyfuncitem.config.getoption("--aiohttp-fast")
- if asyncio.iscoroutinefunction(pyfuncitem.function):
+ if inspect.iscoroutinefunction(pyfuncitem.function):
existing_loop = pyfuncitem.funcargs.get(
"proactor_loop"
) or pyfuncitem.funcargs.get("loop", None)
diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py
index 6443c500a33..28ae2518fec 100644
--- a/aiohttp/web_urldispatcher.py
+++ b/aiohttp/web_urldispatcher.py
@@ -180,8 +180,8 @@ def __init__(
if expect_handler is None:
expect_handler = _default_expect_handler
- assert asyncio.iscoroutinefunction(
- expect_handler
+ assert inspect.iscoroutinefunction(expect_handler) or (
+ sys.version_info < (3, 14) and asyncio.iscoroutinefunction(expect_handler)
), f"Coroutine is expected, got {expect_handler!r}"
method = method.upper()
@@ -189,7 +189,9 @@ def __init__(
raise ValueError(f"{method} is not allowed HTTP method")
assert callable(handler), handler
- if asyncio.iscoroutinefunction(handler):
+ if inspect.iscoroutinefunction(handler) or (
+ sys.version_info < (3, 14) and asyncio.iscoroutinefunction(handler)
+ ):
pass
elif inspect.isgeneratorfunction(handler):
warnings.warn(
diff --git a/aiohttp/worker.py b/aiohttp/worker.py
index 8ed121ac955..f7281bfde75 100644
--- a/aiohttp/worker.py
+++ b/aiohttp/worker.py
@@ -1,6 +1,7 @@
"""Async gunicorn worker for aiohttp.web"""
import asyncio
+import inspect
import os
import re
import signal
@@ -71,7 +72,9 @@ async def _run(self) -> None:
runner = None
if isinstance(self.wsgi, Application):
app = self.wsgi
- elif asyncio.iscoroutinefunction(self.wsgi):
+ elif inspect.iscoroutinefunction(self.wsgi) or (
+ sys.version_info < (3, 14) and asyncio.iscoroutinefunction(self.wsgi)
+ ):
wsgi = await self.wsgi()
if isinstance(wsgi, web.AppRunner):
runner = wsgi
From a60d447aa0e518425b889eb16a2ed54f286e41fb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 1 Apr 2025 10:38:56 +0000
Subject: [PATCH 3/9] Bump virtualenv from 20.29.3 to 20.30.0 (#10666)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.29.3 to
20.30.0.
Release notes
Sourced from virtualenv's
releases.
20.30.0
What's Changed
New Contributors
Full Changelog: https://github.com/pypa/virtualenv/compare/20.29.3...20.30.0
Changelog
Sourced from virtualenv's
changelog.
v20.30.0 (2025-03-31)
Features - 20.30.0
- Add support for `GraalPy
<https://github.com/oracle/graalpython>`_. (:issue:`2832`)
Bugfixes - 20.30.0
-
Upgrade embedded wheels:
- setuptools to
78.1.0 from 75.3.2
(:issue:2863)
Commits
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot 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 a88d3895a16..142c09092b0 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -279,7 +279,7 @@ uvloop==0.21.0 ; platform_system != "Windows"
# -r requirements/lint.in
valkey==6.1.0
# via -r requirements/lint.in
-virtualenv==20.29.3
+virtualenv==20.30.0
# via pre-commit
wait-for-it==2.3.0
# via -r requirements/test.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 4414468a31f..b9e52c24751 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -270,7 +270,7 @@ uvloop==0.21.0 ; platform_system != "Windows" and implementation_name == "cpytho
# -r requirements/lint.in
valkey==6.1.0
# via -r requirements/lint.in
-virtualenv==20.29.3
+virtualenv==20.30.0
# via pre-commit
wait-for-it==2.3.0
# via -r requirements/test.in
diff --git a/requirements/lint.txt b/requirements/lint.txt
index ff910da444b..c400e12cea0 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -109,5 +109,5 @@ uvloop==0.21.0 ; platform_system != "Windows"
# via -r requirements/lint.in
valkey==6.1.0
# via -r requirements/lint.in
-virtualenv==20.29.3
+virtualenv==20.30.0
# via pre-commit
From b65000da599aeb2c9f340387b499a83b3d4ea598 Mon Sep 17 00:00:00 2001
From: layday
Date: Tue, 1 Apr 2025 14:18:39 +0200
Subject: [PATCH 4/9] =?UTF-8?q?Replace=20deprecated=20`asyncio.iscoroutine?=
=?UTF-8?q?function`=20with=20its=20counterpart=E2=80=A6=20(#10663)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
… from `inspect` (#10634)
(cherry picked from commit 77ad7d7ea173eda1306297d275b2d5f7348f9f60)
---
CHANGES/10634.bugfix.rst | 2 ++
aiohttp/pytest_plugin.py | 6 +++---
aiohttp/web_urldispatcher.py | 8 +++++---
aiohttp/worker.py | 5 ++++-
4 files changed, 14 insertions(+), 7 deletions(-)
create mode 100644 CHANGES/10634.bugfix.rst
diff --git a/CHANGES/10634.bugfix.rst b/CHANGES/10634.bugfix.rst
new file mode 100644
index 00000000000..d6ec64a607e
--- /dev/null
+++ b/CHANGES/10634.bugfix.rst
@@ -0,0 +1,2 @@
+Replaced deprecated ``asyncio.iscoroutinefunction`` with its counterpart from ``inspect``
+-- by :user:`layday`.
diff --git a/aiohttp/pytest_plugin.py b/aiohttp/pytest_plugin.py
index 7ce60faa4a4..21d6ea7bbcd 100644
--- a/aiohttp/pytest_plugin.py
+++ b/aiohttp/pytest_plugin.py
@@ -98,7 +98,7 @@ def pytest_fixture_setup(fixturedef): # type: ignore[no-untyped-def]
if inspect.isasyncgenfunction(func):
# async generator fixture
is_async_gen = True
- elif asyncio.iscoroutinefunction(func):
+ elif inspect.iscoroutinefunction(func):
# regular async fixture
is_async_gen = False
else:
@@ -200,14 +200,14 @@ def _passthrough_loop_context(loop, fast=False): # type: ignore[no-untyped-def]
def pytest_pycollect_makeitem(collector, name, obj): # type: ignore[no-untyped-def]
"""Fix pytest collecting for coroutines."""
- if collector.funcnamefilter(name) and asyncio.iscoroutinefunction(obj):
+ if collector.funcnamefilter(name) and inspect.iscoroutinefunction(obj):
return list(collector._genfunctions(name, obj))
def pytest_pyfunc_call(pyfuncitem): # type: ignore[no-untyped-def]
"""Run coroutines in an event loop instead of a normal function call."""
fast = pyfuncitem.config.getoption("--aiohttp-fast")
- if asyncio.iscoroutinefunction(pyfuncitem.function):
+ if inspect.iscoroutinefunction(pyfuncitem.function):
existing_loop = pyfuncitem.funcargs.get(
"proactor_loop"
) or pyfuncitem.funcargs.get("loop", None)
diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py
index 6443c500a33..28ae2518fec 100644
--- a/aiohttp/web_urldispatcher.py
+++ b/aiohttp/web_urldispatcher.py
@@ -180,8 +180,8 @@ def __init__(
if expect_handler is None:
expect_handler = _default_expect_handler
- assert asyncio.iscoroutinefunction(
- expect_handler
+ assert inspect.iscoroutinefunction(expect_handler) or (
+ sys.version_info < (3, 14) and asyncio.iscoroutinefunction(expect_handler)
), f"Coroutine is expected, got {expect_handler!r}"
method = method.upper()
@@ -189,7 +189,9 @@ def __init__(
raise ValueError(f"{method} is not allowed HTTP method")
assert callable(handler), handler
- if asyncio.iscoroutinefunction(handler):
+ if inspect.iscoroutinefunction(handler) or (
+ sys.version_info < (3, 14) and asyncio.iscoroutinefunction(handler)
+ ):
pass
elif inspect.isgeneratorfunction(handler):
warnings.warn(
diff --git a/aiohttp/worker.py b/aiohttp/worker.py
index 8ed121ac955..f7281bfde75 100644
--- a/aiohttp/worker.py
+++ b/aiohttp/worker.py
@@ -1,6 +1,7 @@
"""Async gunicorn worker for aiohttp.web"""
import asyncio
+import inspect
import os
import re
import signal
@@ -71,7 +72,9 @@ async def _run(self) -> None:
runner = None
if isinstance(self.wsgi, Application):
app = self.wsgi
- elif asyncio.iscoroutinefunction(self.wsgi):
+ elif inspect.iscoroutinefunction(self.wsgi) or (
+ sys.version_info < (3, 14) and asyncio.iscoroutinefunction(self.wsgi)
+ ):
wsgi = await self.wsgi()
if isinstance(wsgi, web.AppRunner):
runner = wsgi
From be154728df86462aa5b4bb74a508dd2025ec8bfd Mon Sep 17 00:00:00 2001
From: "J. Nick Koston"
Date: Tue, 1 Apr 2025 11:41:52 -1000
Subject: [PATCH 5/9] [PR #10672/35c979b backport][3.11] Fix headers being
mutated if passed to web.Response as a CIMultiDict (#10673)
If `CIMultiDict` is passed in we need to make a copy to avoid mutating
it. In some cases we used to copy these twice which was fixed in #10045
but for this case that was the only copy being made and the source of
this regression.
fixes #10670
(cherry picked from commit 35c979be79dcc75ecbf7270ccc9fde264e5e2948)
---
CHANGES/10672.bugfix.rst | 1 +
aiohttp/web_response.py | 4 +---
tests/test_web_response.py | 14 +++++++++++++-
3 files changed, 15 insertions(+), 4 deletions(-)
create mode 100644 CHANGES/10672.bugfix.rst
diff --git a/CHANGES/10672.bugfix.rst b/CHANGES/10672.bugfix.rst
new file mode 100644
index 00000000000..a4434f8c87a
--- /dev/null
+++ b/CHANGES/10672.bugfix.rst
@@ -0,0 +1 @@
+Fixed :class:`multidict.CIMultiDict` being mutated when passed to :class:`aiohttp.web.Response` -- by :user:`bdraco`.
diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py
index e498a905caf..367ac6e8c0a 100644
--- a/aiohttp/web_response.py
+++ b/aiohttp/web_response.py
@@ -629,10 +629,8 @@ def __init__(
if headers is None:
real_headers: CIMultiDict[str] = CIMultiDict()
- elif not isinstance(headers, CIMultiDict):
- real_headers = CIMultiDict(headers)
else:
- real_headers = headers # = cast('CIMultiDict[str]', headers)
+ real_headers = CIMultiDict(headers)
if content_type is not None and "charset" in content_type:
raise ValueError("charset must not be in content_type argument")
diff --git a/tests/test_web_response.py b/tests/test_web_response.py
index 0591426c57b..95769161804 100644
--- a/tests/test_web_response.py
+++ b/tests/test_web_response.py
@@ -10,7 +10,7 @@
import aiosignal
import pytest
-from multidict import CIMultiDict, CIMultiDictProxy
+from multidict import CIMultiDict, CIMultiDictProxy, MultiDict
from re_assert import Matches
from aiohttp import HttpVersion, HttpVersion10, HttpVersion11, hdrs
@@ -1479,3 +1479,15 @@ def test_text_is_json_encoded(self) -> None:
def test_content_type_is_overrideable(self) -> None:
resp = json_response({"foo": 42}, content_type="application/vnd.json+api")
assert "application/vnd.json+api" == resp.content_type
+
+
+@pytest.mark.parametrize("loose_header_type", (MultiDict, CIMultiDict, dict))
+async def test_passing_cimultidict_to_web_response_not_mutated(
+ loose_header_type: type,
+) -> None:
+ req = make_request("GET", "/")
+ headers = loose_header_type({})
+ resp = Response(body=b"answer", headers=headers)
+ await resp.prepare(req)
+ assert resp.content_length == 6
+ assert not headers
From 6a3d83579923271acd33749be2a9c5c81c175dc4 Mon Sep 17 00:00:00 2001
From: "J. Nick Koston"
Date: Tue, 1 Apr 2025 12:10:22 -1000
Subject: [PATCH 6/9] [PR #10675/048ef4c backport][3.11] Remove useless
nonlocal statements in tests (#10677)
discovered by new flake8 in
https://github.com/aio-libs/aiohttp/pull/10653
(cherry picked from commit 048ef4c617130ef5a29a70ad67e34d9395891ac1)
---
tests/test_client_ws_functional.py | 3 +--
tests/test_helpers.py | 1 -
tests/test_web_server.py | 3 +--
3 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/tests/test_client_ws_functional.py b/tests/test_client_ws_functional.py
index 54cd5e92f80..0ca57ab3ab2 100644
--- a/tests/test_client_ws_functional.py
+++ b/tests/test_client_ws_functional.py
@@ -315,7 +315,6 @@ async def test_concurrent_close(aiohttp_client) -> None:
client_ws = None
async def handler(request):
- nonlocal client_ws
ws = web.WebSocketResponse()
await ws.prepare(request)
@@ -936,7 +935,7 @@ async def delayed_send_frame(
message: bytes, opcode: int, compress: Optional[int] = None
) -> None:
assert opcode == WSMsgType.PING
- nonlocal cancelled, ping_started
+ nonlocal cancelled
ping_started.set_result(None)
try:
await asyncio.sleep(1)
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 2a83032e557..a343cbdfedf 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -351,7 +351,6 @@ async def test_timer_context_timeout_does_swallow_cancellation() -> None:
ctx = helpers.TimerContext(loop)
async def task_with_timeout() -> None:
- nonlocal ctx
new_task = asyncio.current_task()
assert new_task is not None
with pytest.raises(asyncio.TimeoutError):
diff --git a/tests/test_web_server.py b/tests/test_web_server.py
index 9098ef9e7bf..d2f1341afe0 100644
--- a/tests/test_web_server.py
+++ b/tests/test_web_server.py
@@ -347,7 +347,6 @@ async def test_handler_cancellation(unused_port_socket: socket.socket) -> None:
port = sock.getsockname()[1]
async def on_request(_: web.Request) -> web.Response:
- nonlocal event
try:
await asyncio.sleep(10)
except asyncio.CancelledError:
@@ -389,7 +388,7 @@ async def test_no_handler_cancellation(unused_port_socket: socket.socket) -> Non
started = False
async def on_request(_: web.Request) -> web.Response:
- nonlocal done_event, started, timeout_event
+ nonlocal started
started = True
await asyncio.wait_for(timeout_event.wait(), timeout=5)
done_event.set()
From d3c4543fb4ccd3bfb6f03d4d7939bad48ac59970 Mon Sep 17 00:00:00 2001
From: "J. Nick Koston"
Date: Tue, 1 Apr 2025 13:05:24 -1000
Subject: [PATCH 7/9] [PR #10672/35c979b backport][3.12] Fix headers being
mutated if passed to web.Response as a CIMultiDict (#10674)
---
CHANGES/10672.bugfix.rst | 1 +
aiohttp/web_response.py | 4 +---
tests/test_web_response.py | 14 +++++++++++++-
3 files changed, 15 insertions(+), 4 deletions(-)
create mode 100644 CHANGES/10672.bugfix.rst
diff --git a/CHANGES/10672.bugfix.rst b/CHANGES/10672.bugfix.rst
new file mode 100644
index 00000000000..a4434f8c87a
--- /dev/null
+++ b/CHANGES/10672.bugfix.rst
@@ -0,0 +1 @@
+Fixed :class:`multidict.CIMultiDict` being mutated when passed to :class:`aiohttp.web.Response` -- by :user:`bdraco`.
diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py
index a1955ca0d9e..151fbea3473 100644
--- a/aiohttp/web_response.py
+++ b/aiohttp/web_response.py
@@ -639,10 +639,8 @@ def __init__(
if headers is None:
real_headers: CIMultiDict[str] = CIMultiDict()
- elif not isinstance(headers, CIMultiDict):
- real_headers = CIMultiDict(headers)
else:
- real_headers = headers # = cast('CIMultiDict[str]', headers)
+ real_headers = CIMultiDict(headers)
if content_type is not None and "charset" in content_type:
raise ValueError("charset must not be in content_type argument")
diff --git a/tests/test_web_response.py b/tests/test_web_response.py
index 0a2c5273080..54176ea661b 100644
--- a/tests/test_web_response.py
+++ b/tests/test_web_response.py
@@ -11,7 +11,7 @@
import aiosignal
import pytest
-from multidict import CIMultiDict, CIMultiDictProxy
+from multidict import CIMultiDict, CIMultiDictProxy, MultiDict
from re_assert import Matches
from aiohttp import HttpVersion, HttpVersion10, HttpVersion11, hdrs
@@ -1501,3 +1501,15 @@ def test_text_is_json_encoded(self) -> None:
def test_content_type_is_overrideable(self) -> None:
resp = json_response({"foo": 42}, content_type="application/vnd.json+api")
assert "application/vnd.json+api" == resp.content_type
+
+
+@pytest.mark.parametrize("loose_header_type", (MultiDict, CIMultiDict, dict))
+async def test_passing_cimultidict_to_web_response_not_mutated(
+ loose_header_type: type,
+) -> None:
+ req = make_request("GET", "/")
+ headers = loose_header_type({})
+ resp = Response(body=b"answer", headers=headers)
+ await resp.prepare(req)
+ assert resp.content_length == 6
+ assert not headers
From cca85c46027d7172d9aa7b022098a036aef906b8 Mon Sep 17 00:00:00 2001
From: "J. Nick Koston"
Date: Tue, 1 Apr 2025 13:33:26 -1000
Subject: [PATCH 8/9] [PR #10675/048ef4c backport][3.12] Remove useless
nonlocal statements in tests (#10678)
discovered by new flake8 in
https://github.com/aio-libs/aiohttp/pull/10653
(cherry picked from commit 048ef4c617130ef5a29a70ad67e34d9395891ac1)
---
tests/test_client_ws_functional.py | 3 +--
tests/test_helpers.py | 1 -
tests/test_web_server.py | 3 +--
3 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/tests/test_client_ws_functional.py b/tests/test_client_ws_functional.py
index 54cd5e92f80..0ca57ab3ab2 100644
--- a/tests/test_client_ws_functional.py
+++ b/tests/test_client_ws_functional.py
@@ -315,7 +315,6 @@ async def test_concurrent_close(aiohttp_client) -> None:
client_ws = None
async def handler(request):
- nonlocal client_ws
ws = web.WebSocketResponse()
await ws.prepare(request)
@@ -936,7 +935,7 @@ async def delayed_send_frame(
message: bytes, opcode: int, compress: Optional[int] = None
) -> None:
assert opcode == WSMsgType.PING
- nonlocal cancelled, ping_started
+ nonlocal cancelled
ping_started.set_result(None)
try:
await asyncio.sleep(1)
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 2a83032e557..a343cbdfedf 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -351,7 +351,6 @@ async def test_timer_context_timeout_does_swallow_cancellation() -> None:
ctx = helpers.TimerContext(loop)
async def task_with_timeout() -> None:
- nonlocal ctx
new_task = asyncio.current_task()
assert new_task is not None
with pytest.raises(asyncio.TimeoutError):
diff --git a/tests/test_web_server.py b/tests/test_web_server.py
index 9098ef9e7bf..d2f1341afe0 100644
--- a/tests/test_web_server.py
+++ b/tests/test_web_server.py
@@ -347,7 +347,6 @@ async def test_handler_cancellation(unused_port_socket: socket.socket) -> None:
port = sock.getsockname()[1]
async def on_request(_: web.Request) -> web.Response:
- nonlocal event
try:
await asyncio.sleep(10)
except asyncio.CancelledError:
@@ -389,7 +388,7 @@ async def test_no_handler_cancellation(unused_port_socket: socket.socket) -> Non
started = False
async def on_request(_: web.Request) -> web.Response:
- nonlocal done_event, started, timeout_event
+ nonlocal started
started = True
await asyncio.wait_for(timeout_event.wait(), timeout=5)
done_event.set()
From c723b3ce07b614d0c5fc7806d17f64b2f8ef268a Mon Sep 17 00:00:00 2001
From: "J. Nick Koston"
Date: Tue, 1 Apr 2025 14:36:56 -1000
Subject: [PATCH 9/9] Release 3.11.16 (#10679)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
CHANGES.rst | 27 +++++++++++++++++++++++++++
CHANGES/10634.bugfix.rst | 2 --
CHANGES/10672.bugfix.rst | 1 -
aiohttp/__init__.py | 2 +-
4 files changed, 28 insertions(+), 4 deletions(-)
delete mode 100644 CHANGES/10634.bugfix.rst
delete mode 100644 CHANGES/10672.bugfix.rst
diff --git a/CHANGES.rst b/CHANGES.rst
index c2654b99214..00d728e775d 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -10,6 +10,33 @@
.. towncrier release notes start
+3.11.16 (2025-04-01)
+====================
+
+Bug fixes
+---------
+
+- Replaced deprecated ``asyncio.iscoroutinefunction`` with its counterpart from ``inspect``
+ -- by :user:`layday`.
+
+
+ *Related issues and pull requests on GitHub:*
+ :issue:`10634`.
+
+
+
+- Fixed :class:`multidict.CIMultiDict` being mutated when passed to :class:`aiohttp.web.Response` -- by :user:`bdraco`.
+
+
+ *Related issues and pull requests on GitHub:*
+ :issue:`10672`.
+
+
+
+
+----
+
+
3.11.15 (2025-03-31)
====================
diff --git a/CHANGES/10634.bugfix.rst b/CHANGES/10634.bugfix.rst
deleted file mode 100644
index d6ec64a607e..00000000000
--- a/CHANGES/10634.bugfix.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-Replaced deprecated ``asyncio.iscoroutinefunction`` with its counterpart from ``inspect``
--- by :user:`layday`.
diff --git a/CHANGES/10672.bugfix.rst b/CHANGES/10672.bugfix.rst
deleted file mode 100644
index a4434f8c87a..00000000000
--- a/CHANGES/10672.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-Fixed :class:`multidict.CIMultiDict` being mutated when passed to :class:`aiohttp.web.Response` -- by :user:`bdraco`.
diff --git a/aiohttp/__init__.py b/aiohttp/__init__.py
index acfef952d61..93b06c7367a 100644
--- a/aiohttp/__init__.py
+++ b/aiohttp/__init__.py
@@ -1,4 +1,4 @@
-__version__ = "3.11.16.dev0"
+__version__ = "3.11.16"
from typing import TYPE_CHECKING, Tuple