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/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/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 03e35ce789f..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 @@ -82,6 +83,7 @@ Chris AtLee Chris Laws Chris Moore Chris Shucksmith +Christophe Bornet Christopher Schmitt Claudiu Popa Colin Dunklau 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..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, @@ -1244,7 +1247,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 +1284,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/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/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/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..56de66cc419 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 @@ -52,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 @@ -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 @@ -134,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 @@ -264,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 9597914986c..b6f1e849123 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 @@ -52,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 @@ -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 @@ -131,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 @@ -255,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.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..3c9dfb088be 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 @@ -19,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 @@ -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 @@ -53,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 @@ -102,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 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.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..315ba0550c2 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 @@ -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 @@ -27,12 +29,14 @@ 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 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 @@ -69,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 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/conftest.py b/tests/conftest.py index e24d05b31ff..b267cdcddcf 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: @@ -222,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_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_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_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..80fb9ba0c0b 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) @@ -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: @@ -1924,7 +1947,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 +3811,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_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_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..a4baabb4047 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() @@ -216,8 +216,9 @@ 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: + 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 +258,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 +339,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 +424,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 +532,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 +614,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 +691,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 +766,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 +842,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 +912,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 +978,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 +1068,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() 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)}