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

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=aiohappyeyeballs&package-manager=pip&previous-version=2.4.4&new-version=2.4.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/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

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=proxy-py&package-manager=pip&previous-version=2.4.9&new-version=2.4.10)](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

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cryptography&package-manager=pip&previous-version=44.0.0&new-version=44.0.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/constraints.txt | 2 +- requirements/dev.txt | 2 +- requirements/lint.txt | 2 +- requirements/test.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 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

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=valkey&package-manager=pip&previous-version=6.0.2&new-version=6.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements/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