From c5fb40d0f2558fd182d9e6737cc0bed444e651d2 Mon Sep 17 00:00:00 2001
From: Sam Bull
Date: Thu, 20 Feb 2025 19:04:15 +0000
Subject: [PATCH 1/7] Turn on disallow-any-decorated for mypy (#10377)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
.mypy.ini | 2 +-
aiohttp/abc.py | 4 ++--
aiohttp/client.py | 4 ++--
aiohttp/client_reqrep.py | 2 +-
aiohttp/helpers.py | 10 ++++-----
aiohttp/pytest_plugin.py | 36 ++++++++++++++++++++-------------
aiohttp/test_utils.py | 12 +++++++----
aiohttp/web_app.py | 6 +++---
aiohttp/web_request.py | 2 +-
aiohttp/web_routedef.py | 10 +++++----
aiohttp/web_runner.py | 6 +++---
aiohttp/web_server.py | 6 +++---
aiohttp/web_urldispatcher.py | 2 +-
aiohttp/worker.py | 2 +-
tests/autobahn/test_autobahn.py | 8 ++++----
tests/test_client_connection.py | 2 +-
tests/test_client_request.py | 2 +-
tests/test_client_session.py | 10 ++++-----
tests/test_connector.py | 20 +++++++++---------
tests/test_helpers.py | 4 ++--
tests/test_http_parser.py | 2 +-
tests/test_http_writer.py | 4 ++--
tests/test_proxy.py | 32 ++++++++++++++---------------
tests/test_run_app.py | 2 +-
tests/test_tracing.py | 2 +-
25 files changed, 103 insertions(+), 89 deletions(-)
diff --git a/.mypy.ini b/.mypy.ini
index 54efdae5bad..26971cf2bda 100644
--- a/.mypy.ini
+++ b/.mypy.ini
@@ -2,7 +2,7 @@
files = aiohttp, examples, tests
check_untyped_defs = True
follow_imports_for_stubs = True
-#disallow_any_decorated = True
+disallow_any_decorated = True
disallow_any_generics = True
disallow_any_unimported = True
disallow_incomplete_defs = True
diff --git a/aiohttp/abc.py b/aiohttp/abc.py
index 40f406faca1..498eace04d7 100644
--- a/aiohttp/abc.py
+++ b/aiohttp/abc.py
@@ -81,7 +81,7 @@ def http_exception(self) -> Optional[HTTPException]:
"""HTTPException instance raised on router's resolving, or None"""
@abstractmethod # pragma: no branch
- def get_info(self) -> Dict[str, Any]:
+ def get_info(self) -> Dict[str, Any]: # type: ignore[misc]
"""Return a dict with additional info useful for introspection"""
@property # pragma: no branch
@@ -120,7 +120,7 @@ def request(self) -> Request:
return self._request
@abstractmethod
- def __await__(self) -> Generator[Any, None, StreamResponse]:
+ def __await__(self) -> Generator[None, None, StreamResponse]:
"""Execute the view handler."""
diff --git a/aiohttp/client.py b/aiohttp/client.py
index 9a5946617e8..0aec782dac3 100644
--- a/aiohttp/client.py
+++ b/aiohttp/client.py
@@ -1244,7 +1244,7 @@ def skip_auto_headers(self) -> FrozenSet[istr]:
return self._skip_auto_headers
@property
- def auth(self) -> Optional[BasicAuth]:
+ def auth(self) -> Optional[BasicAuth]: # type: ignore[misc]
"""An object that represents HTTP Basic Authorization"""
return self._default_auth
@@ -1281,7 +1281,7 @@ def trust_env(self) -> bool:
return self._trust_env
@property
- def trace_configs(self) -> List[TraceConfig[Any]]:
+ def trace_configs(self) -> List[TraceConfig[Any]]: # type: ignore[misc]
"""A list of TraceConfig instances used for client tracing"""
return self._trace_configs
diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py
index f42379d45c8..d30e8704d3e 100644
--- a/aiohttp/client_reqrep.py
+++ b/aiohttp/client_reqrep.py
@@ -322,7 +322,7 @@ def ssl(self) -> Union["SSLContext", bool, Fingerprint]:
return self._ssl
@property
- def connection_key(self) -> ConnectionKey:
+ def connection_key(self) -> ConnectionKey: # type: ignore[misc]
if proxy_headers := self.proxy_headers:
h: Optional[int] = hash(tuple(proxy_headers.items()))
else:
diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py
index 188967721bf..e53647e274c 100644
--- a/aiohttp/helpers.py
+++ b/aiohttp/helpers.py
@@ -145,7 +145,7 @@ def __new__(
return super().__new__(cls, login, password, encoding)
@classmethod
- def decode(cls, auth_header: str, encoding: str = "latin1") -> "BasicAuth":
+ def decode(cls, auth_header: str, encoding: str = "latin1") -> "BasicAuth": # type: ignore[misc]
"""Create a BasicAuth object from an Authorization HTTP header."""
try:
auth_type, encoded_credentials = auth_header.split(" ", 1)
@@ -174,7 +174,7 @@ def decode(cls, auth_header: str, encoding: str = "latin1") -> "BasicAuth":
return cls(username, password, encoding=encoding)
@classmethod
- def from_url(cls, url: URL, *, encoding: str = "latin1") -> Optional["BasicAuth"]:
+ def from_url(cls, url: URL, *, encoding: str = "latin1") -> Optional["BasicAuth"]: # type: ignore[misc]
"""Create BasicAuth from url."""
if not isinstance(url, URL):
raise TypeError("url should be yarl.URL instance")
@@ -245,7 +245,7 @@ def netrc_from_env() -> Optional[netrc.netrc]:
@frozen_dataclass_decorator
-class ProxyInfo:
+class ProxyInfo: # type: ignore[misc]
proxy: URL
proxy_auth: Optional[BasicAuth]
@@ -870,7 +870,7 @@ def __init_subclass__(cls) -> None:
def __getitem__(self, key: AppKey[_T]) -> _T: ...
@overload
- def __getitem__(self, key: str) -> Any: ...
+ def __getitem__(self, key: str) -> Any: ... # type: ignore[misc]
def __getitem__(self, key: Union[str, AppKey[_T]]) -> Any:
for mapping in self._maps:
@@ -887,7 +887,7 @@ def get(self, key: AppKey[_T], default: _S) -> Union[_T, _S]: ...
def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: ...
@overload
- def get(self, key: str, default: Any = ...) -> Any: ...
+ def get(self, key: str, default: Any = ...) -> Any: ... # type: ignore[misc]
def get(self, key: Union[str, AppKey[_T]], default: Any = None) -> Any:
try:
diff --git a/aiohttp/pytest_plugin.py b/aiohttp/pytest_plugin.py
index f3deadaf0db..dcac5296908 100644
--- a/aiohttp/pytest_plugin.py
+++ b/aiohttp/pytest_plugin.py
@@ -40,8 +40,9 @@
class AiohttpClient(Protocol):
+ # TODO(PY311): Use Unpack to specify ClientSession kwargs.
@overload
- async def __call__(
+ async def __call__( # type: ignore[misc]
self,
__param: Application,
*,
@@ -49,7 +50,7 @@ async def __call__(
**kwargs: Any,
) -> TestClient[Request, Application]: ...
@overload
- async def __call__(
+ async def __call__( # type: ignore[misc]
self,
__param: BaseTestServer[_Request],
*,
@@ -153,19 +154,19 @@ def finalizer(): # type: ignore[no-untyped-def]
@pytest.fixture
-def fast(request): # type: ignore[no-untyped-def]
+def fast(request: pytest.FixtureRequest) -> bool:
"""--fast config option"""
- return request.config.getoption("--aiohttp-fast")
+ return request.config.getoption("--aiohttp-fast") # type: ignore[no-any-return]
@pytest.fixture
-def loop_debug(request): # type: ignore[no-untyped-def]
+def loop_debug(request: pytest.FixtureRequest) -> bool:
"""--enable-loop-debug config option"""
- return request.config.getoption("--aiohttp-enable-loop-debug")
+ return request.config.getoption("--aiohttp-enable-loop-debug") # type: ignore[no-any-return]
@contextlib.contextmanager
-def _runtime_warning_context(): # type: ignore[no-untyped-def]
+def _runtime_warning_context() -> Iterator[None]:
"""Context manager which checks for RuntimeWarnings.
This exists specifically to
@@ -195,7 +196,9 @@ def _runtime_warning_context(): # type: ignore[no-untyped-def]
@contextlib.contextmanager
-def _passthrough_loop_context(loop, fast=False): # type: ignore[no-untyped-def]
+def _passthrough_loop_context(
+ loop: Optional[asyncio.AbstractEventLoop], fast: bool = False
+) -> Iterator[asyncio.AbstractEventLoop]:
"""Passthrough loop context.
Sets up and tears down a loop unless one is passed in via the loop
@@ -268,7 +271,11 @@ def pytest_generate_tests(metafunc): # type: ignore[no-untyped-def]
@pytest.fixture
-def loop(loop_factory, fast, loop_debug): # type: ignore[no-untyped-def]
+def loop(
+ loop_factory: Callable[[], asyncio.AbstractEventLoopPolicy],
+ fast: bool,
+ loop_debug: bool,
+) -> Iterator[asyncio.AbstractEventLoop]:
"""Return an instance of the event loop."""
policy = loop_factory()
asyncio.set_event_loop_policy(policy)
@@ -280,7 +287,7 @@ def loop(loop_factory, fast, loop_debug): # type: ignore[no-untyped-def]
@pytest.fixture
-def proactor_loop(): # type: ignore[no-untyped-def]
+def proactor_loop() -> Iterator[asyncio.AbstractEventLoop]:
policy = asyncio.WindowsProactorEventLoopPolicy() # type: ignore[attr-defined]
asyncio.set_event_loop_policy(policy)
@@ -353,7 +360,7 @@ async def finalize() -> None:
@pytest.fixture
-def aiohttp_client_cls() -> Type[TestClient[Any, Any]]:
+def aiohttp_client_cls() -> Type[TestClient[Any, Any]]: # type: ignore[misc]
"""
Client class to use in ``aiohttp_client`` factory.
@@ -380,7 +387,7 @@ def test_login(aiohttp_client):
@pytest.fixture
-def aiohttp_client(
+def aiohttp_client( # type: ignore[misc]
loop: asyncio.AbstractEventLoop, aiohttp_client_cls: Type[TestClient[Any, Any]]
) -> Iterator[AiohttpClient]:
"""Factory to create a TestClient instance.
@@ -392,14 +399,14 @@ def aiohttp_client(
clients = []
@overload
- async def go(
+ async def go( # type: ignore[misc]
__param: Application,
*,
server_kwargs: Optional[Dict[str, Any]] = None,
**kwargs: Any,
) -> TestClient[Request, Application]: ...
@overload
- async def go(
+ async def go( # type: ignore[misc]
__param: BaseTestServer[_Request],
*,
server_kwargs: Optional[Dict[str, Any]] = None,
@@ -411,6 +418,7 @@ async def go(
server_kwargs: Optional[Dict[str, Any]] = None,
**kwargs: Any,
) -> TestClient[Any, Any]:
+ # TODO(PY311): Use Unpack to specify ClientSession kwargs and server_kwargs.
if isinstance(__param, Application):
server_kwargs = server_kwargs or {}
server = TestServer(__param, **server_kwargs)
diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py
index 7d29125ab41..84813124ecb 100644
--- a/aiohttp/test_utils.py
+++ b/aiohttp/test_utils.py
@@ -149,7 +149,7 @@ async def start_server(self, **kwargs: Any) -> None:
await site.start()
server = site._server
assert server is not None
- sockets = server.sockets # type: ignore[attr-defined]
+ sockets = server.sockets
assert sockets is not None
self.port = sockets[0].getsockname()[1]
if not self.scheme:
@@ -157,8 +157,9 @@ async def start_server(self, **kwargs: Any) -> None:
self._root = URL(f"{self.scheme}://{absolute_host}:{self.port}")
@abstractmethod
- async def _make_runner(self, **kwargs: Any) -> BaseRunner[_Request]:
+ async def _make_runner(self, **kwargs: Any) -> BaseRunner[_Request]: # type: ignore[misc]
"""Return a new runner for the server."""
+ # TODO(PY311): Use Unpack to specify Server kwargs.
def make_url(self, path: StrOrURL) -> URL:
assert self._root is not None
@@ -232,6 +233,7 @@ def __init__(
super().__init__(scheme=scheme, host=host, port=port, **kwargs)
async def _make_runner(self, **kwargs: Any) -> AppRunner:
+ # TODO(PY311): Use Unpack to specify Server kwargs.
return AppRunner(self.app, **kwargs)
@@ -249,6 +251,7 @@ def __init__(
super().__init__(scheme=scheme, host=host, port=port, **kwargs)
async def _make_runner(self, **kwargs: Any) -> ServerRunner:
+ # TODO(PY311): Use Unpack to specify Server kwargs.
srv = Server(self._handler, **kwargs)
return ServerRunner(srv, **kwargs)
@@ -264,7 +267,7 @@ class TestClient(Generic[_Request, _ApplicationNone]):
__test__ = False
@overload
- def __init__(
+ def __init__( # type: ignore[misc]
self: "TestClient[Request, Application]",
server: TestServer,
*,
@@ -272,7 +275,7 @@ def __init__(
**kwargs: Any,
) -> None: ...
@overload
- def __init__(
+ def __init__( # type: ignore[misc]
self: "TestClient[_Request, None]",
server: BaseTestServer[_Request],
*,
@@ -286,6 +289,7 @@ def __init__( # type: ignore[misc]
cookie_jar: Optional[AbstractCookieJar] = None,
**kwargs: Any,
) -> None:
+ # TODO(PY311): Use Unpack to specify ClientSession kwargs.
if not isinstance(server, BaseTestServer):
raise TypeError(
"server must be TestServer instance, found type: %r" % type(server)
diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py
index 78fb1da8514..91c6ef009f4 100644
--- a/aiohttp/web_app.py
+++ b/aiohttp/web_app.py
@@ -164,7 +164,7 @@ def __eq__(self, other: object) -> bool:
def __getitem__(self, key: AppKey[_T]) -> _T: ...
@overload
- def __getitem__(self, key: str) -> Any: ...
+ def __getitem__(self, key: str) -> Any: ... # type: ignore[misc]
def __getitem__(self, key: Union[str, AppKey[_T]]) -> Any:
return self._state[key]
@@ -179,7 +179,7 @@ def _check_frozen(self) -> None:
def __setitem__(self, key: AppKey[_T], value: _T) -> None: ...
@overload
- def __setitem__(self, key: str, value: Any) -> None: ...
+ def __setitem__(self, key: str, value: Any) -> None: ... # type: ignore[misc]
def __setitem__(self, key: Union[str, AppKey[_T]], value: Any) -> None:
self._check_frozen()
@@ -213,7 +213,7 @@ def get(self, key: AppKey[_T], default: None = ...) -> Optional[_T]: ...
def get(self, key: AppKey[_T], default: _U) -> Union[_T, _U]: ...
@overload
- def get(self, key: str, default: Any = ...) -> Any: ...
+ def get(self, key: str, default: Any = ...) -> Any: ... # type: ignore[misc]
def get(self, key: Union[str, AppKey[_T]], default: Any = None) -> Any:
return self._state.get(key, default)
diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py
index 15283b890bd..48eb9814114 100644
--- a/aiohttp/web_request.py
+++ b/aiohttp/web_request.py
@@ -563,7 +563,7 @@ def cookies(self) -> Mapping[str, str]:
return MappingProxyType({key: val.value for key, val in parsed.items()})
@reify
- def http_range(self) -> slice:
+ def http_range(self) -> "slice[int, int, int]":
"""The content of Range HTTP header.
Return a slice instance.
diff --git a/aiohttp/web_routedef.py b/aiohttp/web_routedef.py
index d909e48920b..51c28a074a2 100644
--- a/aiohttp/web_routedef.py
+++ b/aiohttp/web_routedef.py
@@ -54,7 +54,7 @@ def register(self, router: UrlDispatcher) -> List[AbstractRoute]:
@dataclasses.dataclass(frozen=True, repr=False)
-class RouteDef(AbstractRouteDef):
+class RouteDef(AbstractRouteDef): # type: ignore[misc]
method: str
path: str
handler: _HandlerType
@@ -79,7 +79,7 @@ def register(self, router: UrlDispatcher) -> List[AbstractRoute]:
@dataclasses.dataclass(frozen=True, repr=False)
-class StaticDef(AbstractRouteDef):
+class StaticDef(AbstractRouteDef): # type: ignore[misc]
prefix: str
path: PathLike
kwargs: Dict[str, Any]
@@ -163,9 +163,11 @@ def __repr__(self) -> str:
def __getitem__(self, index: int) -> AbstractRouteDef: ...
@overload
- def __getitem__(self, index: slice) -> List[AbstractRouteDef]: ...
+ def __getitem__(self, index: "slice[int, int, int]") -> List[AbstractRouteDef]: ...
- def __getitem__(self, index): # type: ignore[no-untyped-def]
+ def __getitem__(
+ self, index: Union[int, "slice[int, int, int]"]
+ ) -> Union[AbstractRouteDef, List[AbstractRouteDef]]:
return self._items[index]
def __iter__(self) -> Iterator[AbstractRouteDef]:
diff --git a/aiohttp/web_runner.py b/aiohttp/web_runner.py
index fd0850b6186..11f692ce07e 100644
--- a/aiohttp/web_runner.py
+++ b/aiohttp/web_runner.py
@@ -62,7 +62,7 @@ def __init__(
self._runner = runner
self._ssl_context = ssl_context
self._backlog = backlog
- self._server: Optional[asyncio.AbstractServer] = None
+ self._server: Optional[asyncio.Server] = None
@property
@abstractmethod
@@ -254,12 +254,12 @@ def server(self) -> Optional[Server[_Request]]:
return self._server
@property
- def addresses(self) -> List[Any]:
+ def addresses(self) -> List[Any]: # type: ignore[misc]
ret: List[Any] = []
for site in self._sites:
server = site._server
if server is not None:
- sockets = server.sockets # type: ignore[attr-defined]
+ sockets = server.sockets
if sockets is not None:
for sock in sockets:
ret.append(sock.getsockname())
diff --git a/aiohttp/web_server.py b/aiohttp/web_server.py
index e2f6c350f9a..4a82c5ef77c 100644
--- a/aiohttp/web_server.py
+++ b/aiohttp/web_server.py
@@ -40,16 +40,16 @@ class Server(Generic[_Request]):
request_factory: _RequestFactory[_Request]
@overload
- def __init__(
+ def __init__( # type: ignore[misc]
self: "Server[BaseRequest]",
handler: Callable[[_Request], Awaitable[StreamResponse]],
*,
debug: Optional[bool] = None,
handler_cancellation: bool = False,
- **kwargs: Any,
+ **kwargs: Any, # TODO(PY311): Use Unpack to define kwargs from RequestHandler
) -> None: ...
@overload
- def __init__(
+ def __init__( # type: ignore[misc]
self,
handler: Callable[[_Request], Awaitable[StreamResponse]],
*,
diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py
index c9a86a9f7b5..0d3cbb26398 100644
--- a/aiohttp/web_urldispatcher.py
+++ b/aiohttp/web_urldispatcher.py
@@ -949,7 +949,7 @@ async def _iter(self) -> StreamResponse:
self._raise_allowed_methods()
return await method()
- def __await__(self) -> Generator[Any, None, StreamResponse]:
+ def __await__(self) -> Generator[None, None, StreamResponse]:
return self._iter().__await__()
def _raise_allowed_methods(self) -> NoReturn:
diff --git a/aiohttp/worker.py b/aiohttp/worker.py
index 360ab8ac00f..cc100d59faa 100644
--- a/aiohttp/worker.py
+++ b/aiohttp/worker.py
@@ -200,7 +200,7 @@ def handle_abort(self, sig: int, frame: Optional[FrameType]) -> None:
sys.exit(1)
@staticmethod
- def _create_ssl_context(cfg: Any) -> "SSLContext":
+ def _create_ssl_context(cfg: Any) -> "SSLContext": # type: ignore[misc]
"""Creates SSLContext instance for usage in asyncio.create_server.
See ssl.SSLSocket.__init__ for more details.
diff --git a/tests/autobahn/test_autobahn.py b/tests/autobahn/test_autobahn.py
index 50b5bad2473..a72aa270250 100644
--- a/tests/autobahn/test_autobahn.py
+++ b/tests/autobahn/test_autobahn.py
@@ -47,7 +47,7 @@ def get_failed_tests(report_path: str, name: str) -> List[Dict[str, Any]]:
@pytest.mark.skipif(sys.platform == "darwin", reason="Don't run on macOS")
@pytest.mark.xfail
-def test_client(report_dir: Path, request: Any) -> None:
+def test_client(report_dir: Path, request: pytest.FixtureRequest) -> None:
try:
print("Starting autobahn-testsuite server")
autobahn_container = docker.run(
@@ -57,7 +57,7 @@ def test_client(report_dir: Path, request: Any) -> None:
publish=[(9001, 9001)],
remove=True,
volumes=[
- (f"{request.fspath.dirname}/client", "/config"),
+ (f"{request.path.parent}/client", "/config"),
(f"{report_dir}", "/reports"),
],
)
@@ -88,7 +88,7 @@ def test_client(report_dir: Path, request: Any) -> None:
@pytest.mark.skipif(sys.platform == "darwin", reason="Don't run on macOS")
@pytest.mark.xfail
-def test_server(report_dir: Path, request: Any) -> None:
+def test_server(report_dir: Path, request: pytest.FixtureRequest) -> None:
try:
print("Starting aiohttp test server")
server = subprocess.Popen(
@@ -100,7 +100,7 @@ def test_server(report_dir: Path, request: Any) -> None:
name="autobahn",
remove=True,
volumes=[
- (f"{request.fspath.dirname}/server", "/config"),
+ (f"{request.path.parent}/server", "/config"),
(f"{report_dir}", "/reports"),
],
networks=["host"],
diff --git a/tests/test_client_connection.py b/tests/test_client_connection.py
index 9c93ba0d8f2..e36c7aa9595 100644
--- a/tests/test_client_connection.py
+++ b/tests/test_client_connection.py
@@ -16,7 +16,7 @@ def key() -> object:
@pytest.fixture
-def loop() -> Any:
+def loop() -> Any: # type: ignore[misc]
return mock.create_autospec(asyncio.AbstractEventLoop, spec_set=True, instance=True)
diff --git a/tests/test_client_request.py b/tests/test_client_request.py
index 1b5d3a88fff..2b5e2725c49 100644
--- a/tests/test_client_request.py
+++ b/tests/test_client_request.py
@@ -1465,7 +1465,7 @@ def test_gen_default_accept_encoding(has_brotli: bool, expected: str) -> None:
indirect=("netrc_contents",),
)
@pytest.mark.usefixtures("netrc_contents")
-def test_basicauth_from_netrc_present(
+def test_basicauth_from_netrc_present( # type: ignore[misc]
make_request: _RequestMaker,
expected_auth: helpers.BasicAuth,
) -> None:
diff --git a/tests/test_client_session.py b/tests/test_client_session.py
index 41ba0ac4fe1..974d330a3c9 100644
--- a/tests/test_client_session.py
+++ b/tests/test_client_session.py
@@ -62,7 +62,7 @@ async def make_conn() -> BaseConnector:
@pytest.fixture
-def create_session(
+def create_session( # type: ignore[misc]
loop: asyncio.AbstractEventLoop,
) -> Iterator[Callable[..., Awaitable[ClientSession]]]:
session = None
@@ -78,7 +78,7 @@ async def maker(*args: Any, **kwargs: Any) -> ClientSession:
@pytest.fixture
-def session(
+def session( # type: ignore[misc]
create_session: Callable[..., Awaitable[ClientSession]],
loop: asyncio.AbstractEventLoop,
) -> ClientSession:
@@ -531,7 +531,7 @@ async def create_connection(
@pytest.mark.parametrize("protocol", ["http", "https", "ws", "wss"])
-async def test_ws_connect_allowed_protocols(
+async def test_ws_connect_allowed_protocols( # type: ignore[misc]
create_session: Callable[..., Awaitable[ClientSession]],
create_mocked_conn: Callable[[], ResponseHandler],
protocol: str,
@@ -593,7 +593,7 @@ async def create_connection(
@pytest.mark.parametrize("protocol", ["http", "https", "ws", "wss", "unix"])
-async def test_ws_connect_unix_socket_allowed_protocols(
+async def test_ws_connect_unix_socket_allowed_protocols( # type: ignore[misc]
create_session: Callable[..., Awaitable[ClientSession]],
create_mocked_conn: Callable[[], ResponseHandler],
protocol: str,
@@ -1159,7 +1159,7 @@ async def test_requote_redirect_url_default_disable() -> None:
),
],
)
-async def test_build_url_returns_expected_url(
+async def test_build_url_returns_expected_url( # type: ignore[misc]
create_session: Callable[..., Awaitable[ClientSession]],
base_url: Union[URL, str, None],
url: Union[URL, str],
diff --git a/tests/test_connector.py b/tests/test_connector.py
index 61b452eee81..5641d0b9f04 100644
--- a/tests/test_connector.py
+++ b/tests/test_connector.py
@@ -53,26 +53,26 @@
from aiohttp.tracing import Trace
-@pytest.fixture()
-def key() -> ConnectionKey:
+@pytest.fixture
+def key() -> ConnectionKey: # type: ignore[misc]
# Connection key
return ConnectionKey("localhost", 80, False, True, None, None, None)
@pytest.fixture
-def key2() -> ConnectionKey:
+def key2() -> ConnectionKey: # type: ignore[misc]
# Connection key
return ConnectionKey("localhost", 80, False, True, None, None, None)
@pytest.fixture
-def other_host_key2() -> ConnectionKey:
+def other_host_key2() -> ConnectionKey: # type: ignore[misc]
# Connection key
return ConnectionKey("otherhost", 80, False, True, None, None, None)
@pytest.fixture
-def ssl_key() -> ConnectionKey:
+def ssl_key() -> ConnectionKey: # type: ignore[misc]
# Connection key
return ConnectionKey("localhost", 80, True, True, None, None, None)
@@ -221,7 +221,7 @@ async def test_del(loop: asyncio.AbstractEventLoop, key: ConnectionKey) -> None:
@pytest.mark.xfail
-async def test_del_with_scheduled_cleanup(
+async def test_del_with_scheduled_cleanup( # type: ignore[misc]
loop: asyncio.AbstractEventLoop, key: ConnectionKey
) -> None:
loop.set_debug(True)
@@ -251,7 +251,7 @@ async def test_del_with_scheduled_cleanup(
@pytest.mark.skipif(
sys.implementation.name != "cpython", reason="CPython GC is required for the test"
)
-def test_del_with_closed_loop(
+def test_del_with_closed_loop( # type: ignore[misc]
loop: asyncio.AbstractEventLoop, key: ConnectionKey
) -> None:
async def make_conn() -> aiohttp.BaseConnector:
@@ -444,7 +444,7 @@ async def test_release(loop: asyncio.AbstractEventLoop, key: ConnectionKey) -> N
@pytest.mark.usefixtures("enable_cleanup_closed")
-async def test_release_ssl_transport(
+async def test_release_ssl_transport( # type: ignore[misc]
loop: asyncio.AbstractEventLoop, ssl_key: ConnectionKey
) -> None:
conn = aiohttp.BaseConnector(enable_cleanup_closed=True)
@@ -1924,7 +1924,7 @@ async def test_cleanup(key: ConnectionKey) -> None:
@pytest.mark.usefixtures("enable_cleanup_closed")
-async def test_cleanup_close_ssl_transport(
+async def test_cleanup_close_ssl_transport( # type: ignore[misc]
loop: asyncio.AbstractEventLoop, ssl_key: ConnectionKey
) -> None:
proto = create_mocked_conn(loop)
@@ -3788,7 +3788,7 @@ async def test_available_connections_with_limit_per_host(
@pytest.mark.parametrize("limit_per_host", [0, 10])
-async def test_available_connections_without_limit_per_host(
+async def test_available_connections_without_limit_per_host( # type: ignore[misc]
key: ConnectionKey, other_host_key2: ConnectionKey, limit_per_host: int
) -> None:
"""Verify expected values based on active connections with higher host limit."""
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 80d3a0e355e..081760c92fa 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -189,7 +189,7 @@ def test_basic_auth_decode_invalid_credentials() -> None:
),
),
)
-def test_basic_auth_decode_blank_username(
+def test_basic_auth_decode_blank_username( # type: ignore[misc]
credentials: str, expected_auth: helpers.BasicAuth
) -> None:
header = f"Basic {base64.b64encode(credentials.encode()).decode()}"
@@ -1106,7 +1106,7 @@ def test_netrc_from_home_does_not_raise_if_access_denied(
indirect=("netrc_contents",),
)
@pytest.mark.usefixtures("netrc_contents")
-def test_basicauth_present_in_netrc(
+def test_basicauth_present_in_netrc( # type: ignore[misc]
expected_auth: helpers.BasicAuth,
) -> None:
"""Test that netrc file contents are properly parsed into BasicAuth tuples"""
diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py
index 90d3f687247..6bb06159f21 100644
--- a/tests/test_http_parser.py
+++ b/tests/test_http_parser.py
@@ -45,7 +45,7 @@
@pytest.fixture
-def protocol() -> Any:
+def protocol() -> Any: # type: ignore[misc]
return mock.create_autospec(BaseProtocol, spec_set=True, instance=True)
diff --git a/tests/test_http_writer.py b/tests/test_http_writer.py
index b76cac0fe58..71a5bb93d13 100644
--- a/tests/test_http_writer.py
+++ b/tests/test_http_writer.py
@@ -37,7 +37,7 @@ def buf() -> bytearray:
@pytest.fixture
-def transport(buf: bytearray) -> Any:
+def transport(buf: bytearray) -> Any: # type: ignore[misc]
transport = mock.create_autospec(asyncio.Transport, spec_set=True, instance=True)
def write(chunk: bytes) -> None:
@@ -54,7 +54,7 @@ def writelines(chunks: Iterable[bytes]) -> None:
@pytest.fixture
-def protocol(loop: asyncio.AbstractEventLoop, transport: asyncio.Transport) -> Any:
+def protocol(loop: asyncio.AbstractEventLoop, transport: asyncio.Transport) -> Any: # type: ignore[misc]
return mock.create_autospec(
BaseProtocol, spec_set=True, instance=True, transport=transport
)
diff --git a/tests/test_proxy.py b/tests/test_proxy.py
index 2aaafedeb5c..ceaa285caec 100644
--- a/tests/test_proxy.py
+++ b/tests/test_proxy.py
@@ -41,7 +41,7 @@ def tearDown(self) -> None:
autospec=True,
spec_set=True,
)
- def test_connect(
+ def test_connect( # type: ignore[misc]
self, start_connection: mock.Mock, ClientRequestMock: mock.Mock
) -> None:
req = ClientRequest(
@@ -104,7 +104,7 @@ async def make_conn() -> aiohttp.TCPConnector:
autospec=True,
spec_set=True,
)
- def test_proxy_headers(
+ def test_proxy_headers( # type: ignore[misc]
self, start_connection: mock.Mock, ClientRequestMock: mock.Mock
) -> None:
req = ClientRequest(
@@ -167,7 +167,7 @@ async def make_conn() -> aiohttp.TCPConnector:
autospec=True,
spec_set=True,
)
- def test_proxy_auth(self, start_connection: mock.Mock) -> None:
+ def test_proxy_auth(self, start_connection: mock.Mock) -> None: # type: ignore[misc]
with self.assertRaises(ValueError) as ctx:
ClientRequest(
"GET",
@@ -186,7 +186,7 @@ def test_proxy_auth(self, start_connection: mock.Mock) -> None:
autospec=True,
spec_set=True,
)
- def test_proxy_dns_error(self, start_connection: mock.Mock) -> None:
+ def test_proxy_dns_error(self, start_connection: mock.Mock) -> None: # type: ignore[misc]
async def make_conn() -> aiohttp.TCPConnector:
return aiohttp.TCPConnector()
@@ -217,7 +217,7 @@ async def make_conn() -> aiohttp.TCPConnector:
autospec=True,
spec_set=True,
)
- def test_proxy_connection_error(self, start_connection: mock.Mock) -> None:
+ def test_proxy_connection_error(self, start_connection: mock.Mock) -> None: # type: ignore[misc]
async def make_conn() -> aiohttp.TCPConnector:
return aiohttp.TCPConnector()
@@ -257,7 +257,7 @@ async def make_conn() -> aiohttp.TCPConnector:
autospec=True,
spec_set=True,
)
- def test_proxy_server_hostname_default(
+ def test_proxy_server_hostname_default( # type: ignore[misc]
self, start_connection: mock.Mock, ClientRequestMock: mock.Mock
) -> None:
proxy_req = ClientRequest(
@@ -338,7 +338,7 @@ async def make_conn() -> aiohttp.TCPConnector:
autospec=True,
spec_set=True,
)
- def test_proxy_server_hostname_override(
+ def test_proxy_server_hostname_override( # type: ignore[misc]
self, start_connection: mock.Mock, ClientRequestMock: mock.Mock
) -> None:
proxy_req = ClientRequest(
@@ -423,7 +423,7 @@ async def make_conn() -> aiohttp.TCPConnector:
spec_set=True,
)
@pytest.mark.usefixtures("enable_cleanup_closed")
- def test_https_connect_fingerprint_mismatch(
+ def test_https_connect_fingerprint_mismatch( # type: ignore[misc]
self, start_connection: mock.Mock, ClientRequestMock: mock.Mock
) -> None:
async def make_conn() -> aiohttp.TCPConnector:
@@ -531,7 +531,7 @@ def close(self) -> None:
autospec=True,
spec_set=True,
)
- def test_https_connect(
+ def test_https_connect( # type: ignore[misc]
self, start_connection: mock.Mock, ClientRequestMock: mock.Mock
) -> None:
proxy_req = ClientRequest(
@@ -613,7 +613,7 @@ async def make_conn() -> aiohttp.TCPConnector:
autospec=True,
spec_set=True,
)
- def test_https_connect_certificate_error(
+ def test_https_connect_certificate_error( # type: ignore[misc]
self, start_connection: mock.Mock, ClientRequestMock: mock.Mock
) -> None:
proxy_req = ClientRequest(
@@ -690,7 +690,7 @@ async def make_conn() -> aiohttp.TCPConnector:
autospec=True,
spec_set=True,
)
- def test_https_connect_ssl_error(
+ def test_https_connect_ssl_error( # type: ignore[misc]
self, start_connection: mock.Mock, ClientRequestMock: mock.Mock
) -> None:
proxy_req = ClientRequest(
@@ -765,7 +765,7 @@ async def make_conn() -> aiohttp.TCPConnector:
autospec=True,
spec_set=True,
)
- def test_https_connect_http_proxy_error(
+ def test_https_connect_http_proxy_error( # type: ignore[misc]
self, start_connection: mock.Mock, ClientRequestMock: mock.Mock
) -> None:
proxy_req = ClientRequest(
@@ -841,7 +841,7 @@ async def make_conn() -> aiohttp.TCPConnector:
autospec=True,
spec_set=True,
)
- def test_https_connect_resp_start_error(
+ def test_https_connect_resp_start_error( # type: ignore[misc]
self, start_connection: mock.Mock, ClientRequestMock: mock.Mock
) -> None:
proxy_req = ClientRequest(
@@ -911,7 +911,7 @@ async def make_conn() -> aiohttp.TCPConnector:
autospec=True,
spec_set=True,
)
- def test_request_port(
+ def test_request_port( # type: ignore[misc]
self, start_connection: mock.Mock, ClientRequestMock: mock.Mock
) -> None:
proxy_req = ClientRequest(
@@ -977,7 +977,7 @@ def test_proxy_auth_property_default(self) -> None:
autospec=True,
spec_set=True,
)
- def test_https_connect_pass_ssl_context(
+ def test_https_connect_pass_ssl_context( # type: ignore[misc]
self, start_connection: mock.Mock, ClientRequestMock: mock.Mock
) -> None:
proxy_req = ClientRequest(
@@ -1067,7 +1067,7 @@ async def make_conn() -> aiohttp.TCPConnector:
autospec=True,
spec_set=True,
)
- def test_https_auth(
+ def test_https_auth( # type: ignore[misc]
self, start_connection: mock.Mock, ClientRequestMock: mock.Mock
) -> None:
proxy_req = ClientRequest(
diff --git a/tests/test_run_app.py b/tests/test_run_app.py
index 9673145b4e4..af71612ae19 100644
--- a/tests/test_run_app.py
+++ b/tests/test_run_app.py
@@ -442,7 +442,7 @@ def test_run_app_close_loop(patched_loop: asyncio.AbstractEventLoop) -> None:
mixed_bindings_test_params,
ids=mixed_bindings_test_ids,
)
-def test_run_app_mixed_bindings(
+def test_run_app_mixed_bindings( # type: ignore[misc]
run_app_kwargs: Dict[str, Any],
expected_server_calls: List[mock._Call],
expected_unix_server_calls: List[mock._Call],
diff --git a/tests/test_tracing.py b/tests/test_tracing.py
index a0a4c61fb96..d884b4e753e 100644
--- a/tests/test_tracing.py
+++ b/tests/test_tracing.py
@@ -102,7 +102,7 @@ class TestTrace:
("dns_cache_miss", (Mock(),), TraceDnsCacheMissParams),
],
)
- async def test_send(
+ async def test_send( # type: ignore[misc]
self, signal: str, params: Tuple[Mock, ...], param_obj: Any
) -> None:
session = Mock()
From 0c4b1c74cd7bffb544dacb4a4d8793f80fd4ad32 Mon Sep 17 00:00:00 2001
From: Christophe Bornet
Date: Thu, 20 Feb 2025 20:04:40 +0100
Subject: [PATCH 2/7] Detect blocking calls in coroutines using BlockBuster
(#10433)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
---
CHANGES/10433.feature.rst | 1 +
CONTRIBUTORS.txt | 1 +
aiohttp/client.py | 9 ++--
requirements/lint.in | 1 +
requirements/lint.txt | 4 ++
requirements/test.in | 1 +
requirements/test.txt | 4 ++
tests/conftest.py | 31 ++++++++++++
tests/test_client_functional.py | 85 +++++++++++++++++----------------
tests/test_cookiejar.py | 2 +-
tests/test_web_functional.py | 20 ++++----
11 files changed, 103 insertions(+), 56 deletions(-)
create mode 100644 CHANGES/10433.feature.rst
diff --git a/CHANGES/10433.feature.rst b/CHANGES/10433.feature.rst
new file mode 100644
index 00000000000..11a29d6e368
--- /dev/null
+++ b/CHANGES/10433.feature.rst
@@ -0,0 +1 @@
+Detect blocking calls in coroutines using BlockBuster -- by :user:`cbornet`.
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 03e35ce789f..3eead8cded4 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -82,6 +82,7 @@ Chris AtLee
Chris Laws
Chris Moore
Chris Shucksmith
+Christophe Bornet
Christopher Schmitt
Claudiu Popa
Colin Dunklau
diff --git a/aiohttp/client.py b/aiohttp/client.py
index 0aec782dac3..04f03b710f0 100644
--- a/aiohttp/client.py
+++ b/aiohttp/client.py
@@ -607,11 +607,14 @@ async def _request(
if req_cookies:
all_cookies.load(req_cookies)
+ proxy_: Optional[URL] = None
if proxy is not None:
- proxy = URL(proxy)
+ proxy_ = URL(proxy)
elif self._trust_env:
with suppress(LookupError):
- proxy, proxy_auth = get_env_proxy_for_url(url)
+ proxy_, proxy_auth = await asyncio.to_thread(
+ get_env_proxy_for_url, url
+ )
req = self._request_class(
method,
@@ -628,7 +631,7 @@ async def _request(
expect100=expect100,
loop=self._loop,
response_class=self._response_class,
- proxy=proxy,
+ proxy=proxy_,
proxy_auth=proxy_auth,
timer=timer,
session=self,
diff --git a/requirements/lint.in b/requirements/lint.in
index 999f41b7a40..64b34df92a9 100644
--- a/requirements/lint.in
+++ b/requirements/lint.in
@@ -1,4 +1,5 @@
aiodns
+blockbuster
freezegun
mypy; implementation_name == "cpython"
pre-commit
diff --git a/requirements/lint.txt b/requirements/lint.txt
index af13cdbae32..00dc77a967f 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -10,6 +10,8 @@ annotated-types==0.7.0
# via pydantic
async-timeout==5.0.1
# via valkey
+blockbuster==1.5.21
+ # via -r requirements/lint.in
cffi==1.17.1
# via
# cryptography
@@ -27,6 +29,8 @@ exceptiongroup==1.2.2
# via pytest
filelock==3.17.0
# via virtualenv
+forbiddenfruit==0.1.4
+ # via blockbuster
freezegun==1.5.1
# via -r requirements/lint.in
identify==2.6.7
diff --git a/requirements/test.in b/requirements/test.in
index ff28aacfb95..25813a963b7 100644
--- a/requirements/test.in
+++ b/requirements/test.in
@@ -1,5 +1,6 @@
-r base.in
+blockbuster
coverage
freezegun
mypy; implementation_name == "cpython"
diff --git a/requirements/test.txt b/requirements/test.txt
index d3184ce7ff8..25b71f785db 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -14,6 +14,8 @@ annotated-types==0.7.0
# via pydantic
async-timeout==5.0.1 ; python_version < "3.11"
# via -r requirements/runtime-deps.in
+blockbuster==1.5.21
+ # via -r requirements/test.in
brotli==1.1.0 ; platform_python_implementation == "CPython"
# via -r requirements/runtime-deps.in
cffi==1.17.1
@@ -33,6 +35,8 @@ exceptiongroup==1.2.2
# via pytest
execnet==2.1.1
# via pytest-xdist
+forbiddenfruit==0.1.4
+ # via blockbuster
freezegun==1.5.1
# via -r requirements/test.in
frozenlist==1.5.0
diff --git a/tests/conftest.py b/tests/conftest.py
index e24d05b31ff..17631965ecd 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -12,6 +12,7 @@
from uuid import uuid4
import pytest
+from blockbuster import blockbuster_ctx
from aiohttp.client_proto import ResponseHandler
from aiohttp.http import WS_KEY
@@ -33,6 +34,36 @@
IS_LINUX = sys.platform.startswith("linux")
+@pytest.fixture(autouse=True)
+def blockbuster(request: pytest.FixtureRequest) -> Iterator[None]:
+ # No blockbuster for benchmark tests.
+ node = request.node.parent
+ while node:
+ if node.name.startswith("test_benchmarks"):
+ yield
+ return
+ node = node.parent
+ with blockbuster_ctx(
+ "aiohttp", excluded_modules=["aiohttp.pytest_plugin", "aiohttp.test_utils"]
+ ) as bb:
+ # TODO: Fix blocking call in ClientRequest's constructor.
+ # https://github.com/aio-libs/aiohttp/issues/10435
+ for func in ["io.TextIOWrapper.read", "os.stat"]:
+ bb.functions[func].can_block_in("aiohttp/client_reqrep.py", "update_auth")
+ for func in [
+ "os.getcwd",
+ "os.readlink",
+ "os.stat",
+ "os.path.abspath",
+ "os.path.samestat",
+ ]:
+ bb.functions[func].can_block_in(
+ "aiohttp/web_urldispatcher.py", "add_static"
+ )
+ bb.functions["os.getcwd"].can_block_in("coverage/control.py", "_should_trace")
+ yield
+
+
@pytest.fixture
def tls_certificate_authority() -> trustme.CA:
if not TRUSTME:
diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py
index af63cda9577..04538966062 100644
--- a/tests/test_client_functional.py
+++ b/tests/test_client_functional.py
@@ -507,7 +507,7 @@ async def handler(request: web.Request) -> web.Response:
assert ["file"] == list(post_data.keys())
file_field = post_data["file"]
assert isinstance(file_field, web.FileField)
- assert data == file_field.file.read()
+ assert data == await asyncio.to_thread(file_field.file.read)
return web.Response()
app = web.Application()
@@ -1633,16 +1633,16 @@ async def handler(request: web.Request) -> web.Response:
async def test_POST_FILES(aiohttp_client: AiohttpClient, fname: pathlib.Path) -> None:
+ content1 = fname.read_bytes()
+
async def handler(request: web.Request) -> web.Response:
data = await request.post()
assert isinstance(data["some"], web.FileField)
assert data["some"].filename == fname.name
- with fname.open("rb") as f:
- content1 = f.read()
- content2 = data["some"].file.read()
- assert content1 == content2
+ content2 = await asyncio.to_thread(data["some"].file.read)
+ assert content2 == content1
assert isinstance(data["test"], web.FileField)
- assert data["test"].file.read() == b"data"
+ assert await asyncio.to_thread(data["test"].file.read) == b"data"
assert isinstance(data["some"], web.FileField)
data["some"].file.close()
data["test"].file.close()
@@ -1662,15 +1662,15 @@ async def handler(request: web.Request) -> web.Response:
async def test_POST_FILES_DEFLATE(
aiohttp_client: AiohttpClient, fname: pathlib.Path
) -> None:
+ content1 = fname.read_bytes()
+
async def handler(request: web.Request) -> web.Response:
data = await request.post()
assert isinstance(data["some"], web.FileField)
assert data["some"].filename == fname.name
- with fname.open("rb") as f:
- content1 = f.read()
- content2 = data["some"].file.read()
+ content2 = await asyncio.to_thread(data["some"].file.read)
data["some"].file.close()
- assert content1 == content2
+ assert content2 == content1
return web.Response()
app = web.Application()
@@ -1722,12 +1722,12 @@ async def handler(request: web.Request) -> web.Response:
async def test_POST_FILES_STR(
aiohttp_client: AiohttpClient, fname: pathlib.Path
) -> None:
+ content1 = fname.read_bytes().decode()
+
async def handler(request: web.Request) -> web.Response:
data = await request.post()
- with fname.open("rb") as f:
- content1 = f.read().decode()
content2 = data["some"]
- assert content1 == content2
+ assert content2 == content1
return web.Response()
app = web.Application()
@@ -1742,11 +1742,11 @@ async def handler(request: web.Request) -> web.Response:
async def test_POST_FILES_STR_SIMPLE(
aiohttp_client: AiohttpClient, fname: pathlib.Path
) -> None:
+ content = fname.read_bytes()
+
async def handler(request: web.Request) -> web.Response:
data = await request.read()
- with fname.open("rb") as f:
- content = f.read()
- assert content == data
+ assert data == content
return web.Response()
app = web.Application()
@@ -1761,13 +1761,13 @@ async def handler(request: web.Request) -> web.Response:
async def test_POST_FILES_LIST(
aiohttp_client: AiohttpClient, fname: pathlib.Path
) -> None:
+ content = fname.read_bytes()
+
async def handler(request: web.Request) -> web.Response:
data = await request.post()
assert isinstance(data["some"], web.FileField)
assert fname.name == data["some"].filename
- with fname.open("rb") as f:
- content = f.read()
- assert content == data["some"].file.read()
+ assert await asyncio.to_thread(data["some"].file.read) == content
data["some"].file.close()
return web.Response()
@@ -1783,14 +1783,14 @@ async def handler(request: web.Request) -> web.Response:
async def test_POST_FILES_CT(
aiohttp_client: AiohttpClient, fname: pathlib.Path
) -> None:
+ content = fname.read_bytes()
+
async def handler(request: web.Request) -> web.Response:
data = await request.post()
assert isinstance(data["some"], web.FileField)
assert fname.name == data["some"].filename
assert "text/plain" == data["some"].content_type
- with fname.open("rb") as f:
- content = f.read()
- assert content == data["some"].file.read()
+ assert await asyncio.to_thread(data["some"].file.read) == content
data["some"].file.close()
return web.Response()
@@ -1808,11 +1808,11 @@ async def handler(request: web.Request) -> web.Response:
async def test_POST_FILES_SINGLE(
aiohttp_client: AiohttpClient, fname: pathlib.Path
) -> None:
+ content = fname.read_bytes().decode()
+
async def handler(request: web.Request) -> web.Response:
data = await request.text()
- with fname.open("rb") as f:
- content = f.read().decode()
- assert content == data
+ assert data == content
# if system cannot determine 'text/x-python' MIME type
# then use 'application/octet-stream' default
assert request.content_type in [
@@ -1836,11 +1836,11 @@ async def handler(request: web.Request) -> web.Response:
async def test_POST_FILES_SINGLE_content_disposition(
aiohttp_client: AiohttpClient, fname: pathlib.Path
) -> None:
+ content = fname.read_bytes().decode()
+
async def handler(request: web.Request) -> web.Response:
data = await request.text()
- with fname.open("rb") as f:
- content = f.read().decode()
- assert content == data
+ assert data == content
# if system cannot determine 'application/pgp-keys' MIME type
# then use 'application/octet-stream' default
assert request.content_type in [
@@ -1868,11 +1868,11 @@ async def handler(request: web.Request) -> web.Response:
async def test_POST_FILES_SINGLE_BINARY(
aiohttp_client: AiohttpClient, fname: pathlib.Path
) -> None:
+ content = fname.read_bytes()
+
async def handler(request: web.Request) -> web.Response:
data = await request.read()
- with fname.open("rb") as f:
- content = f.read()
- assert content == data
+ assert data == content
# if system cannot determine 'application/pgp-keys' MIME type
# then use 'application/octet-stream' default
assert request.content_type in [
@@ -1896,7 +1896,7 @@ async def test_POST_FILES_IO(aiohttp_client: AiohttpClient) -> None:
async def handler(request: web.Request) -> web.Response:
data = await request.post()
assert isinstance(data["unknown"], web.FileField)
- assert b"data" == data["unknown"].file.read()
+ assert b"data" == await asyncio.to_thread(data["unknown"].file.read)
assert data["unknown"].content_type == "application/octet-stream"
assert data["unknown"].filename == "unknown"
data["unknown"].file.close()
@@ -1918,7 +1918,7 @@ async def handler(request: web.Request) -> web.Response:
assert isinstance(data["unknown"], web.FileField)
assert data["unknown"].content_type == "application/octet-stream"
assert data["unknown"].filename == "unknown"
- assert data["unknown"].file.read() == b"data"
+ assert await asyncio.to_thread(data["unknown"].file.read) == b"data"
data["unknown"].file.close()
assert data.getall("q") == ["t1", "t2"]
@@ -1939,6 +1939,8 @@ async def handler(request: web.Request) -> web.Response:
async def test_POST_FILES_WITH_DATA(
aiohttp_client: AiohttpClient, fname: pathlib.Path
) -> None:
+ content = fname.read_bytes()
+
async def handler(request: web.Request) -> web.Response:
data = await request.post()
assert data["test"] == "true"
@@ -1949,9 +1951,8 @@ async def handler(request: web.Request) -> web.Response:
"application/octet-stream",
]
assert data["some"].filename == fname.name
- with fname.open("rb") as f:
- assert data["some"].file.read() == f.read()
- data["some"].file.close()
+ assert await asyncio.to_thread(data["some"].file.read) == content
+ data["some"].file.close()
return web.Response()
@@ -1967,13 +1968,13 @@ async def handler(request: web.Request) -> web.Response:
async def test_POST_STREAM_DATA(
aiohttp_client: AiohttpClient, fname: pathlib.Path
) -> None:
+ expected = fname.read_bytes()
+
async def handler(request: web.Request) -> web.Response:
assert request.content_type == "application/octet-stream"
content = await request.read()
- with fname.open("rb") as f:
- expected = f.read()
- assert request.content_length == len(expected)
- assert content == expected
+ assert request.content_length == len(expected)
+ assert content == expected
return web.Response()
@@ -1986,10 +1987,10 @@ async def handler(request: web.Request) -> web.Response:
async def gen(fname: pathlib.Path) -> AsyncIterator[bytes]:
with fname.open("rb") as f:
- data = f.read(100)
+ data = await asyncio.to_thread(f.read, 100)
while data:
yield data
- data = f.read(100)
+ data = await asyncio.to_thread(f.read, 100)
async with client.post(
"/", data=gen(fname), headers={"Content-Length": str(data_size)}
diff --git a/tests/test_cookiejar.py b/tests/test_cookiejar.py
index ce125946823..1f78c5abba9 100644
--- a/tests/test_cookiejar.py
+++ b/tests/test_cookiejar.py
@@ -185,7 +185,7 @@ async def test_constructor_with_expired(
assert jar_cookies != expected_cookies
-async def test_save_load(
+def test_save_load(
tmp_path: Path,
loop: asyncio.AbstractEventLoop,
cookies_to_send: SimpleCookie,
diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py
index 8b65d1e08fb..332e5cba6bb 100644
--- a/tests/test_web_functional.py
+++ b/tests/test_web_functional.py
@@ -414,7 +414,7 @@ async def handler(request: web.Request) -> web.Response:
assert ["data.unknown_mime_type"] == list(data.keys())
for fs in data.values():
assert isinstance(fs, aiohttp.web_request.FileField)
- check_file(fs)
+ await asyncio.to_thread(check_file, fs)
fs.file.close()
resp = web.Response(body=b"OK")
return resp
@@ -441,9 +441,9 @@ async def handler(request: web.Request) -> web.Response:
assert isinstance(_file, aiohttp.web_request.FileField)
assert not _file.file.closed
if _file.filename == "test1.jpeg":
- assert _file.file.read() == b"binary data 1"
+ assert await asyncio.to_thread(_file.file.read) == b"binary data 1"
if _file.filename == "test2.jpeg":
- assert _file.file.read() == b"binary data 2"
+ assert await asyncio.to_thread(_file.file.read) == b"binary data 2"
file_names.add(_file.filename)
_file.file.close()
assert len(files) == 2
@@ -483,7 +483,7 @@ async def handler(request: web.Request) -> web.Response:
assert ["data.unknown_mime_type", "conftest.py"] == list(data.keys())
for fs in data.values():
assert isinstance(fs, aiohttp.web_request.FileField)
- check_file(fs)
+ await asyncio.to_thread(check_file, fs)
fs.file.close()
resp = web.Response(body=b"OK")
return resp
@@ -762,7 +762,7 @@ async def handler(request: web.Request) -> web.Response:
form = await request.post()
form_file = form["file"]
assert isinstance(form_file, aiohttp.web_request.FileField)
- raw_data = form_file.file.read()
+ raw_data = await asyncio.to_thread(form_file.file.read)
form_file.file.close()
assert data == raw_data
return web.Response()
@@ -787,7 +787,7 @@ async def handler(request: web.Request) -> web.Response:
form = await request.post()
form_file = form["file"]
assert isinstance(form_file, aiohttp.web_request.FileField)
- raw_data = form_file.file.read()
+ raw_data = await asyncio.to_thread(form_file.file.read)
form_file.file.close()
assert data == raw_data
return web.Response()
@@ -914,10 +914,10 @@ async def test_response_with_async_gen(
async def stream(f_name: pathlib.Path) -> AsyncIterator[bytes]:
with f_name.open("rb") as f:
- data = f.read(100)
+ data = await asyncio.to_thread(f.read, 100)
while data:
yield data
- data = f.read(100)
+ data = await asyncio.to_thread(f.read, 100)
async def handler(request: web.Request) -> web.Response:
headers = {"Content-Length": str(data_size)}
@@ -946,10 +946,10 @@ async def test_response_with_async_gen_no_params(
async def stream() -> AsyncIterator[bytes]:
with fname.open("rb") as f:
- data = f.read(100)
+ data = await asyncio.to_thread(f.read, 100)
while data:
yield data
- data = f.read(100)
+ data = await asyncio.to_thread(f.read, 100)
async def handler(request: web.Request) -> web.Response:
headers = {"Content-Length": str(data_size)}
From 182198fccae2d71b857a0f26b18e7317fd6cff4f Mon Sep 17 00:00:00 2001
From: Andrew Top <142360808+top-oai@users.noreply.github.com>
Date: Thu, 20 Feb 2025 11:07:07 -0800
Subject: [PATCH 3/7] Explicitly close the socket if there is a failure in
start_connection() (#10464)
---
CHANGES/10464.bugfix.rst | 1 +
CONTRIBUTORS.txt | 1 +
aiohttp/connector.py | 13 ++++++++++++-
tests/conftest.py | 1 +
tests/test_connector.py | 23 +++++++++++++++++++++++
tests/test_proxy.py | 1 +
6 files changed, 39 insertions(+), 1 deletion(-)
create mode 100644 CHANGES/10464.bugfix.rst
diff --git a/CHANGES/10464.bugfix.rst b/CHANGES/10464.bugfix.rst
new file mode 100644
index 00000000000..4e21000a317
--- /dev/null
+++ b/CHANGES/10464.bugfix.rst
@@ -0,0 +1 @@
+Changed connection creation to explicitly close sockets if an exception is raised in the event loop's ``create_connection`` method -- by :user:`top-oai`.
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 3eead8cded4..42062c972c8 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -43,6 +43,7 @@ Andrej Antonov
Andrew Leech
Andrew Lytvyn
Andrew Svetlov
+Andrew Top
Andrew Zhou
Andrii Soldatenko
Anes Abismail
diff --git a/aiohttp/connector.py b/aiohttp/connector.py
index 263a4823334..99b281b24a5 100644
--- a/aiohttp/connector.py
+++ b/aiohttp/connector.py
@@ -1101,6 +1101,7 @@ async def _wrap_create_connection(
client_error: Type[Exception] = ClientConnectorError,
**kwargs: Any,
) -> Tuple[asyncio.Transport, ResponseHandler]:
+ sock: Union[socket.socket, None] = None
try:
async with ceil_timeout(
timeout.sock_connect, ceil_threshold=timeout.ceil_threshold
@@ -1112,7 +1113,11 @@ async def _wrap_create_connection(
interleave=self._interleave,
loop=self._loop,
)
- return await self._loop.create_connection(*args, **kwargs, sock=sock)
+ connection = await self._loop.create_connection(
+ *args, **kwargs, sock=sock
+ )
+ sock = None
+ return connection
except cert_errors as exc:
raise ClientConnectorCertificateError(req.connection_key, exc) from exc
except ssl_errors as exc:
@@ -1121,6 +1126,12 @@ async def _wrap_create_connection(
if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
raise
raise client_error(req.connection_key, exc) from exc
+ finally:
+ if sock is not None:
+ # Will be hit if an exception is thrown before the event loop takes the socket.
+ # In that case, proactively close the socket to guard against event loop leaks.
+ # For example, see https://github.com/MagicStack/uvloop/issues/653.
+ sock.close()
def _warn_about_tls_in_tls(
self,
diff --git a/tests/conftest.py b/tests/conftest.py
index 17631965ecd..b267cdcddcf 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -253,6 +253,7 @@ def start_connection() -> Iterator[mock.Mock]:
"aiohttp.connector.aiohappyeyeballs.start_connection",
autospec=True,
spec_set=True,
+ return_value=mock.create_autospec(socket.socket, spec_set=True, instance=True),
) as start_connection_mock:
yield start_connection_mock
diff --git a/tests/test_connector.py b/tests/test_connector.py
index 5641d0b9f04..80fb9ba0c0b 100644
--- a/tests/test_connector.py
+++ b/tests/test_connector.py
@@ -646,6 +646,29 @@ async def test_tcp_connector_certificate_error(
await conn.close()
+async def test_tcp_connector_closes_socket_on_error(
+ loop: asyncio.AbstractEventLoop, start_connection: mock.AsyncMock
+) -> None:
+ req = ClientRequest("GET", URL("https://127.0.0.1:443"), loop=loop)
+
+ conn = aiohttp.TCPConnector()
+ with (
+ mock.patch.object(
+ conn._loop,
+ "create_connection",
+ autospec=True,
+ spec_set=True,
+ side_effect=ValueError,
+ ),
+ pytest.raises(ValueError),
+ ):
+ await conn.connect(req, [], ClientTimeout())
+
+ assert start_connection.return_value.close.called
+
+ await conn.close()
+
+
async def test_tcp_connector_server_hostname_default(
loop: asyncio.AbstractEventLoop, start_connection: mock.AsyncMock
) -> None:
diff --git a/tests/test_proxy.py b/tests/test_proxy.py
index ceaa285caec..a4baabb4047 100644
--- a/tests/test_proxy.py
+++ b/tests/test_proxy.py
@@ -216,6 +216,7 @@ async def make_conn() -> aiohttp.TCPConnector:
"aiohttp.connector.aiohappyeyeballs.start_connection",
autospec=True,
spec_set=True,
+ return_value=mock.create_autospec(socket.socket, spec_set=True, instance=True),
)
def test_proxy_connection_error(self, start_connection: mock.Mock) -> None: # type: ignore[misc]
async def make_conn() -> aiohttp.TCPConnector:
From e82f3fd5720ff149405f05c46662712d8d797f1b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 20 Feb 2025 20:39:00 +0000
Subject: [PATCH 4/7] Bump aiohappyeyeballs from 2.4.4 to 2.4.6 (#10439)
Bumps [aiohappyeyeballs](https://github.com/aio-libs/aiohappyeyeballs)
from 2.4.4 to 2.4.6.
Release notes
Sourced from aiohappyeyeballs's
releases.
v2.4.6 (2025-02-07)
Bug Fixes
- Ensure all timers are cancelled when after staggered race finishes
(#136,
f75891d)
Detailed Changes: v2.4.5...v2.4.6
v2.4.5 (2025-02-07)
Bug Fixes
- Keep classifiers in project to avoid automatic enrichment (#134,
99edb20)
Co-authored-by: J. Nick Koston nick@koston.org
- Move classifiers to prevent recalculation by Poetry (#131,
66e1c90)
Co-authored-by: Martin Styk martin.styk@oracle.com
Co-authored-by: J. Nick Koston nick@koston.org
Detailed Changes: v2.4.4...v2.4.5
Changelog
Sourced from aiohappyeyeballs's
changelog.
v2.4.6 (2025-02-07)
Bug fixes
- Ensure all timers are cancelled when after staggered race finishes
(#136)
(
f75891d)
v2.4.5 (2025-02-07)
Bug fixes
- Keep classifiers in project to avoid automatic enrichment (#134)
(
99edb20)
- Move classifiers to prevent recalculation by poetry (#131)
(
66e1c90)
Commits
f18ad49
2.4.6
f75891d
fix: ensure all timers are cancelled when after staggered race finishes
(#136)
cbc674d
2.4.5
99edb20
fix: keep classifiers in project to avoid automatic enrichment (#134)
9baf0b3
chore(deps-ci): bump the github-actions group with 9 updates (#135)
678eab0
chore: update dependabot.yml to include GHA (#133)
66e1c90
fix: move classifiers to prevent recalculation by Poetry (#131)
850640e
chore: migrate to poetry 2.0 (#132)
75ec0dc
chore(pre-commit.ci): pre-commit autoupdate (#129)
7d7f118
chore(pre-commit.ci): pre-commit autoupdate (#128)
- Additional commits viewable in compare
view
[](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 | 8 +++++++-
requirements/dev.txt | 8 +++++++-
requirements/runtime-deps.txt | 2 +-
requirements/test.txt | 2 +-
5 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/requirements/base.txt b/requirements/base.txt
index ef4f1989f18..49891cf5b39 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.4.4
+aiohappyeyeballs==2.4.6
# via -r requirements/runtime-deps.in
aiosignal==1.3.2
# via -r requirements/runtime-deps.in
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index 23df00e7000..b4efb4eb211 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.4.4
+aiohappyeyeballs==2.4.6
# via -r requirements/runtime-deps.in
aiohttp-theme==0.1.7
# via -r requirements/doc.in
@@ -24,6 +24,10 @@ async-timeout==5.0.1 ; python_version < "3.11"
# valkey
babel==2.17.0
# via sphinx
+blockbuster==1.5.21
+ # via
+ # -r requirements/lint.in
+ # -r requirements/test.in
brotli==1.1.0 ; platform_python_implementation == "CPython"
# via -r requirements/runtime-deps.in
build==1.2.2.post1
@@ -68,6 +72,8 @@ execnet==2.1.1
# via pytest-xdist
filelock==3.17.0
# via virtualenv
+forbiddenfruit==0.1.4
+ # via blockbuster
freezegun==1.5.1
# via
# -r requirements/lint.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 9597914986c..284c04cb03f 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.4.4
+aiohappyeyeballs==2.4.6
# via -r requirements/runtime-deps.in
aiohttp-theme==0.1.7
# via -r requirements/doc.in
@@ -24,6 +24,10 @@ async-timeout==5.0.1 ; python_version < "3.11"
# valkey
babel==2.17.0
# via sphinx
+blockbuster==1.5.21
+ # via
+ # -r requirements/lint.in
+ # -r requirements/test.in
brotli==1.1.0 ; platform_python_implementation == "CPython"
# via -r requirements/runtime-deps.in
build==1.2.2.post1
@@ -66,6 +70,8 @@ execnet==2.1.1
# via pytest-xdist
filelock==3.17.0
# via virtualenv
+forbiddenfruit==0.1.4
+ # via blockbuster
freezegun==1.5.1
# via
# -r requirements/lint.in
diff --git a/requirements/runtime-deps.txt b/requirements/runtime-deps.txt
index db7999b6903..8ab8f3f43b9 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.4.4
+aiohappyeyeballs==2.4.6
# via -r requirements/runtime-deps.in
aiosignal==1.3.2
# via -r requirements/runtime-deps.in
diff --git a/requirements/test.txt b/requirements/test.txt
index 25b71f785db..7ef839c0fcd 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.4.4
+aiohappyeyeballs==2.4.6
# via -r requirements/runtime-deps.in
aiosignal==1.3.2
# via -r requirements/runtime-deps.in
From 2ab88c5db7d9e940049c707eab3d98019ffc8778 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 20 Feb 2025 20:39:03 +0000
Subject: [PATCH 5/7] Bump proxy-py from 2.4.9 to 2.4.10 (#10470)
Bumps [proxy-py](https://github.com/abhinavsingh/proxy.py) from 2.4.9 to
2.4.10.
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 +-
requirements/test.txt | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
index b4efb4eb211..29896118d62 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -140,7 +140,7 @@ propcache==0.2.1
# via
# -r requirements/runtime-deps.in
# yarl
-proxy-py==2.4.9
+proxy-py==2.4.10
# via
# -r requirements/lint.in
# -r requirements/test.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 284c04cb03f..c8cad67d4cd 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -137,7 +137,7 @@ propcache==0.2.1
# via
# -r requirements/runtime-deps.in
# yarl
-proxy-py==2.4.9
+proxy-py==2.4.10
# via
# -r requirements/lint.in
# -r requirements/test.in
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 00dc77a967f..b84e0538998 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -57,7 +57,7 @@ pluggy==1.5.0
# via pytest
pre-commit==4.1.0
# via -r requirements/lint.in
-proxy-py==2.4.9
+proxy-py==2.4.10
# via -r requirements/lint.in
pycares==4.5.0
# via aiodns
diff --git a/requirements/test.txt b/requirements/test.txt
index 7ef839c0fcd..7b22a2a9908 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -73,7 +73,7 @@ propcache==0.2.1
# via
# -r requirements/runtime-deps.in
# yarl
-proxy-py==2.4.9
+proxy-py==2.4.10
# via -r requirements/test.in
pycares==4.5.0
# via aiodns
From 36aa738078ff8db20d418125a0da8cacd72735a7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 20 Feb 2025 20:39:05 +0000
Subject: [PATCH 6/7] Bump cryptography from 44.0.0 to 44.0.1 (#10457)
Bumps [cryptography](https://github.com/pyca/cryptography) from 44.0.0
to 44.0.1.
Changelog
Sourced from cryptography's
changelog.
44.0.1 - 2025-02-11
* Updated Windows, macOS, and Linux wheels to be compiled with OpenSSL
3.4.1.
* We now build ``armv7l`` ``manylinux`` wheels and publish them to PyPI.
* We now build ``manylinux_2_34`` wheels and publish them to PyPI.
.. _v44-0-0:
Commits
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot 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 29896118d62..cc4f417b73c 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -56,7 +56,7 @@ coverage==7.6.12
# via
# -r requirements/test.in
# pytest-cov
-cryptography==44.0.0
+cryptography==44.0.1
# via
# pyjwt
# trustme
diff --git a/requirements/dev.txt b/requirements/dev.txt
index c8cad67d4cd..0490d995886 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -56,7 +56,7 @@ coverage==7.6.12
# via
# -r requirements/test.in
# pytest-cov
-cryptography==44.0.0
+cryptography==44.0.1
# via
# pyjwt
# trustme
diff --git a/requirements/lint.txt b/requirements/lint.txt
index b84e0538998..791f9660800 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -21,7 +21,7 @@ cfgv==3.4.0
# via pre-commit
click==8.1.8
# via slotscheck
-cryptography==44.0.0
+cryptography==44.0.1
# via trustme
distlib==0.3.9
# via virtualenv
diff --git a/requirements/test.txt b/requirements/test.txt
index 7b22a2a9908..315ba0550c2 100644
--- a/requirements/test.txt
+++ b/requirements/test.txt
@@ -29,7 +29,7 @@ coverage==7.6.12
# via
# -r requirements/test.in
# pytest-cov
-cryptography==44.0.0
+cryptography==44.0.1
# via trustme
exceptiongroup==1.2.2
# via pytest
From ca09d1da0a3011a676ee031e561826b4940f3c92 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 20 Feb 2025 21:22:26 +0000
Subject: [PATCH 7/7] Bump valkey from 6.0.2 to 6.1.0 (#10459)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Bumps [valkey](https://github.com/valkey-io/valkey-py) from 6.0.2 to
6.1.0.
Release notes
Sourced from valkey's
releases.
6.1.0
Changes
- v6.1.0 (#173)
- Fix updated linters errors (#172)
- Removing my work account from CODEOWNERS (#168)
- connection: fix getpid() call on disconnect (#166)
- Revert some typing commits (#163)
- fixed type hints of hash methods (#154)
- fixed type hint of hrandfield method (#155)
- Add new CODEOWNERS (#152)
- Add more test cases (#147)
- refactor: updating typing for xreadgroup / xread / xadd (#130)
- update typing for copy (#139)
- update the return type of scan family (#135)
- Issue 131: Fixes Flaky test by increasing timeout to 1s (#136)
- Fixes for valkey 8.0 (#124)
- Set socket_timeout default value to 5 seconds (#120)
- sort methods
ACL DELUSER and ACL DRYRUN by
alphabetically, checked doc (#103)
- drop compose format and commands v1, use supported v2+ (#105)
- fix redis-cluster-py link (#99)
- make documentation link more obvious (#98)
- Added geosearch tests (#97)
π₯ Breaking Changes
- Remove expiration/TTL commands that are not supported by Valkey (#125)
π New Features
- Add dynamic_startup_nodes parameter to async ValkeyCluster (#167)
- fixed problems in #143
(#144)
- Add async class aliases for redis-py compatibility (#148)
π Bug Fixes
- Allow relative path in unix socket URLs (#153)
π§° Maintenance
- connection: add a pointer to os.getpid to ConnectionPool (#159)
- fix verify params in getex() (#145)
- build(deps): bump rojopolis/spellcheck-github-actions from 0.44.0 to
0.45.0 (#128)
- build(deps): bump codecov/codecov-action from 4 to 5 (#127)
- Add support for Python 3.13 (#116)
- build(deps): bump rojopolis/spellcheck-github-actions from 0.42.0 to
0.44.0 (#118)
- Revert "Temporarily fix https://github.com/actions/runner-images/issuβ¦"
(#117)
- parsers: resp3: be less verbose (#112)
- Temporarily fix actions/runner-images#10781
(#113)
- build(deps): bump rojopolis/spellcheck-github-actions from 0.41.0 to
0.42.0 (#108)
... (truncated)
Commits
95c4b68
Merge pull request #173
from valkey-io/mkmkme/6.1.0
a589997
v6.1.0
2d648a4
Merge pull request #172
from valkey-io/mkmkme/fix-isort
05e1f31
tests: fix floating-point error for georadius tests
81f63b3
fix black formatting
0b825f1
tests: fix flynt error
d0e5bdf
url_parser: fix isort error
457be2d
Merge pull request #168
from valkey-io/aiven-sal/removeme
e4025bd
Removing my work account from CODEOWNERS
bcd5e2c
Merge pull request #167
from Kakadus/2472redis-add-dynamic-startup-nodes-flag...
- Additional commits viewable in compare
view
[](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 cc4f417b73c..56de66cc419 100644
--- a/requirements/constraints.txt
+++ b/requirements/constraints.txt
@@ -270,7 +270,7 @@ uvloop==0.21.0 ; platform_system != "Windows"
# via
# -r requirements/base.in
# -r requirements/lint.in
-valkey==6.0.2
+valkey==6.1.0
# via -r requirements/lint.in
virtualenv==20.29.2
# via pre-commit
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 0490d995886..b6f1e849123 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -261,7 +261,7 @@ uvloop==0.21.0 ; platform_system != "Windows" and implementation_name == "cpytho
# via
# -r requirements/base.in
# -r requirements/lint.in
-valkey==6.0.2
+valkey==6.1.0
# via -r requirements/lint.in
virtualenv==20.29.2
# via pre-commit
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 791f9660800..3c9dfb088be 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -106,7 +106,7 @@ typing-extensions==4.12.2
# rich
uvloop==0.21.0 ; platform_system != "Windows"
# via -r requirements/lint.in
-valkey==6.0.2
+valkey==6.1.0
# via -r requirements/lint.in
virtualenv==20.29.2
# via pre-commit