Skip to content

Commit e0ff524

Browse files
Test typing round 2 (#8620)
1 parent c99a1e2 commit e0ff524

24 files changed

+2897
-2110
lines changed

.coveragerc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ omit = site-packages
66
[report]
77
exclude_also =
88
if TYPE_CHECKING
9+
assert False
10+
: \.\.\.(\s*#.*)?$
11+
^ +\.\.\.$

.github/workflows/ci-cd.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ jobs:
4949
- name: Setup Python
5050
uses: actions/setup-python@v5
5151
with:
52-
python-version: 3.9
52+
python-version: 3.11
5353
- name: Cache PyPI
5454
uses: actions/[email protected]
5555
with:

aiohttp/client.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import traceback
1111
import warnings
1212
from contextlib import suppress
13-
from types import SimpleNamespace, TracebackType
13+
from types import TracebackType
1414
from typing import (
1515
TYPE_CHECKING,
1616
Any,
@@ -156,7 +156,7 @@
156156

157157

158158
class _RequestOptions(TypedDict, total=False):
159-
params: Union[Mapping[str, str], None]
159+
params: Union[Mapping[str, Union[str, int]], str, None]
160160
data: Any
161161
json: Any
162162
cookies: Union[LooseCookies, None]
@@ -176,7 +176,7 @@ class _RequestOptions(TypedDict, total=False):
176176
ssl: Union[SSLContext, bool, Fingerprint]
177177
server_hostname: Union[str, None]
178178
proxy_headers: Union[LooseHeaders, None]
179-
trace_request_ctx: Union[SimpleNamespace, None]
179+
trace_request_ctx: Union[Mapping[str, str], None]
180180
read_bufsize: Union[int, None]
181181
auto_decompress: Union[bool, None]
182182
max_line_size: Union[int, None]
@@ -374,11 +374,22 @@ def __del__(self, _warnings: Any = warnings) -> None:
374374
context["source_traceback"] = self._source_traceback
375375
self._loop.call_exception_handler(context)
376376

377-
def request(
378-
self, method: str, url: StrOrURL, **kwargs: Any
379-
) -> "_RequestContextManager":
380-
"""Perform HTTP request."""
381-
return _RequestContextManager(self._request(method, url, **kwargs))
377+
if sys.version_info >= (3, 11) and TYPE_CHECKING:
378+
379+
def request(
380+
self,
381+
method: str,
382+
url: StrOrURL,
383+
**kwargs: Unpack[_RequestOptions],
384+
) -> "_RequestContextManager": ...
385+
386+
else:
387+
388+
def request(
389+
self, method: str, url: StrOrURL, **kwargs: Any
390+
) -> "_RequestContextManager":
391+
"""Perform HTTP request."""
392+
return _RequestContextManager(self._request(method, url, **kwargs))
382393

383394
def _build_url(self, str_or_url: StrOrURL) -> URL:
384395
url = URL(str_or_url)
@@ -415,7 +426,7 @@ async def _request(
415426
ssl: Union[SSLContext, bool, Fingerprint] = True,
416427
server_hostname: Optional[str] = None,
417428
proxy_headers: Optional[LooseHeaders] = None,
418-
trace_request_ctx: Optional[SimpleNamespace] = None,
429+
trace_request_ctx: Optional[Mapping[str, str]] = None,
419430
read_bufsize: Optional[int] = None,
420431
auto_decompress: Optional[bool] = None,
421432
max_line_size: Optional[int] = None,

aiohttp/client_exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def __str__(self) -> str:
8585
return "{}, message={!r}, url={!r}".format(
8686
self.status,
8787
self.message,
88-
self.request_info.real_url,
88+
str(self.request_info.real_url),
8989
)
9090

9191
def __repr__(self) -> str:

aiohttp/client_reqrep.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ class ClientRequest:
179179
hdrs.ACCEPT_ENCODING: _gen_default_accept_encoding(),
180180
}
181181

182-
body = b""
182+
# Type of body depends on PAYLOAD_REGISTRY, which is dynamic.
183+
body: Any = b""
183184
auth = None
184185
response = None
185186

@@ -371,7 +372,7 @@ def update_headers(self, headers: Optional[LooseHeaders]) -> None:
371372

372373
if headers:
373374
if isinstance(headers, (dict, MultiDictProxy, MultiDict)):
374-
headers = headers.items() # type: ignore[assignment]
375+
headers = headers.items()
375376

376377
for key, value in headers: # type: ignore[misc]
377378
# A special case for Host header
@@ -532,6 +533,10 @@ def update_proxy(
532533
raise ValueError("proxy_auth must be None or BasicAuth() tuple")
533534
self.proxy = proxy
534535
self.proxy_auth = proxy_auth
536+
if proxy_headers is not None and not isinstance(
537+
proxy_headers, (MultiDict, MultiDictProxy)
538+
):
539+
proxy_headers = CIMultiDict(proxy_headers)
535540
self.proxy_headers = proxy_headers
536541

537542
def keep_alive(self) -> bool:
@@ -567,10 +572,10 @@ async def write_bytes(
567572
await self.body.write(writer)
568573
else:
569574
if isinstance(self.body, (bytes, bytearray)):
570-
self.body = (self.body,) # type: ignore[assignment]
575+
self.body = (self.body,)
571576

572577
for chunk in self.body:
573-
await writer.write(chunk) # type: ignore[arg-type]
578+
await writer.write(chunk)
574579
except OSError as underlying_exc:
575580
reraised_exc = underlying_exc
576581

aiohttp/connector.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
List,
2626
Literal,
2727
Optional,
28+
Sequence,
2829
Set,
2930
Tuple,
3031
Type,
@@ -812,7 +813,7 @@ def clear_dns_cache(
812813
self._cached_hosts.clear()
813814

814815
async def _resolve_host(
815-
self, host: str, port: int, traces: Optional[List["Trace"]] = None
816+
self, host: str, port: int, traces: Optional[Sequence["Trace"]] = None
816817
) -> List[ResolveResult]:
817818
"""Resolve host and return list of addresses."""
818819
if is_ip_address(host):
@@ -880,7 +881,7 @@ async def _resolve_host_with_throttle(
880881
key: Tuple[str, int],
881882
host: str,
882883
port: int,
883-
traces: Optional[List["Trace"]],
884+
traces: Optional[Sequence["Trace"]],
884885
) -> List[ResolveResult]:
885886
"""Resolve host with a dns events throttle."""
886887
if key in self._throttle_dns_events:

aiohttp/cookiejar.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
from typing import (
1515
DefaultDict,
1616
Dict,
17+
FrozenSet,
1718
Iterable,
1819
Iterator,
19-
List,
2020
Mapping,
2121
Optional,
2222
Set,
@@ -83,7 +83,7 @@ def __init__(
8383
*,
8484
unsafe: bool = False,
8585
quote_cookie: bool = True,
86-
treat_as_secure_origin: Union[StrOrURL, List[StrOrURL], None] = None,
86+
treat_as_secure_origin: Union[StrOrURL, Iterable[StrOrURL], None] = None,
8787
) -> None:
8888
self._cookies: DefaultDict[Tuple[str, str], SimpleCookie] = defaultdict(
8989
SimpleCookie
@@ -92,17 +92,20 @@ def __init__(
9292
self._unsafe = unsafe
9393
self._quote_cookie = quote_cookie
9494
if treat_as_secure_origin is None:
95-
treat_as_secure_origin = []
95+
self._treat_as_secure_origin: FrozenSet[URL] = frozenset()
9696
elif isinstance(treat_as_secure_origin, URL):
97-
treat_as_secure_origin = [treat_as_secure_origin.origin()]
97+
self._treat_as_secure_origin = frozenset({treat_as_secure_origin.origin()})
9898
elif isinstance(treat_as_secure_origin, str):
99-
treat_as_secure_origin = [URL(treat_as_secure_origin).origin()]
99+
self._treat_as_secure_origin = frozenset(
100+
{URL(treat_as_secure_origin).origin()}
101+
)
100102
else:
101-
treat_as_secure_origin = [
102-
URL(url).origin() if isinstance(url, str) else url.origin()
103-
for url in treat_as_secure_origin
104-
]
105-
self._treat_as_secure_origin = treat_as_secure_origin
103+
self._treat_as_secure_origin = frozenset(
104+
{
105+
URL(url).origin() if isinstance(url, str) else url.origin()
106+
for url in treat_as_secure_origin
107+
}
108+
)
106109
self._next_expiration: float = ceil(time.time())
107110
self._expirations: Dict[Tuple[str, str, str], float] = {}
108111

@@ -243,7 +246,7 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No
243246

244247
self._do_expiration()
245248

246-
def filter_cookies(self, request_url: URL = URL()) -> "BaseCookie[str]":
249+
def filter_cookies(self, request_url: URL) -> "BaseCookie[str]":
247250
"""Returns this jar's cookies filtered by their attributes."""
248251
if not isinstance(request_url, URL):
249252
warnings.warn(

aiohttp/pytest_plugin.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,17 @@
22
import contextlib
33
import inspect
44
import warnings
5-
from typing import Any, Awaitable, Callable, Dict, Iterator, Optional, Type, Union
5+
from typing import (
6+
Any,
7+
Awaitable,
8+
Callable,
9+
Dict,
10+
Iterator,
11+
Optional,
12+
Protocol,
13+
Type,
14+
Union,
15+
)
616

717
import pytest
818

@@ -24,9 +34,23 @@
2434
except ImportError: # pragma: no cover
2535
uvloop = None # type: ignore[assignment]
2636

27-
AiohttpClient = Callable[[Union[Application, BaseTestServer]], Awaitable[TestClient]]
2837
AiohttpRawServer = Callable[[Application], Awaitable[RawTestServer]]
29-
AiohttpServer = Callable[[Application], Awaitable[TestServer]]
38+
39+
40+
class AiohttpClient(Protocol):
41+
def __call__(
42+
self,
43+
__param: Union[Application, BaseTestServer],
44+
*,
45+
server_kwargs: Optional[Dict[str, Any]] = None,
46+
**kwargs: Any
47+
) -> Awaitable[TestClient]: ...
48+
49+
50+
class AiohttpServer(Protocol):
51+
def __call__(
52+
self, app: Application, *, port: Optional[int] = None, **kwargs: Any
53+
) -> Awaitable[TestServer]: ...
3054

3155

3256
def pytest_addoption(parser): # type: ignore[no-untyped-def]
@@ -258,7 +282,9 @@ def aiohttp_server(loop: asyncio.AbstractEventLoop) -> Iterator[AiohttpServer]:
258282
"""
259283
servers = []
260284

261-
async def go(app, *, port=None, **kwargs): # type: ignore[no-untyped-def]
285+
async def go(
286+
app: Application, *, port: Optional[int] = None, **kwargs: Any
287+
) -> TestServer:
262288
server = TestServer(app, port=port)
263289
await server.start_server(**kwargs)
264290
servers.append(server)

0 commit comments

Comments
 (0)