diff --git a/.mypy.ini b/.mypy.ini new file mode 100644 index 00000000000..5cf92fa6297 --- /dev/null +++ b/.mypy.ini @@ -0,0 +1,24 @@ +[mypy] +warn_unused_configs = True +strict = True + +[mypy-aiodns] +ignore_missing_imports = True + +[mypy-brotli] +ignore_missing_imports = True + +[mypy-gunicorn.*] +ignore_missing_imports = True + +[mypy-uvloop] +ignore_missing_imports = True + +[mypy-cchardet] +ignore_missing_imports = True + +[mypy-tokio] +ignore_missing_imports = True + +[mypy-asynctest] +ignore_missing_imports = True diff --git a/CHANGES/3927.misc b/CHANGES/3927.misc new file mode 100644 index 00000000000..5c67ea64744 --- /dev/null +++ b/CHANGES/3927.misc @@ -0,0 +1 @@ +Use ``mypy --strict`` diff --git a/Makefile b/Makefile index 1a7ea0cd235..1682fe4893e 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,7 @@ fmt format: .PHONY: mypy mypy: - mypy aiohttp tests + mypy --show-error-codes aiohttp tests .develop: .install-deps $(call to-hash,$(PYS) $(CYS) $(CS)) pip install -e . diff --git a/aiohttp/client.py b/aiohttp/client.py index 7bb823fb147..ec71b67a458 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -133,7 +133,7 @@ try: from ssl import SSLContext except ImportError: # pragma: no cover - SSLContext = object # type: ignore + SSLContext = object # type: ignore[misc,assignment] @dataclasses.dataclass(frozen=True) @@ -248,7 +248,7 @@ def __init__( if timeout is sentinel: self._timeout = DEFAULT_TIMEOUT else: - self._timeout = timeout # type: ignore + self._timeout = timeout # type: ignore[assignment] self._raise_for_status = raise_for_status self._auto_decompress = auto_decompress self._trust_env = trust_env @@ -381,7 +381,7 @@ async def _request( real_timeout = self._timeout # type: ClientTimeout else: if not isinstance(timeout, ClientTimeout): - real_timeout = ClientTimeout(total=timeout) # type: ignore + real_timeout = ClientTimeout(total=timeout) # type: ignore[arg-type] else: real_timeout = timeout # timeout is cumulative for all request operations @@ -690,7 +690,7 @@ async def _ws_connect( DeprecationWarning, stacklevel=2, ) - ws_timeout = ClientWSTimeout(ws_close=timeout) # type: ignore + ws_timeout = ClientWSTimeout(ws_close=timeout) # type: ignore[arg-type] else: ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT if receive_timeout is not None: @@ -1053,7 +1053,7 @@ def __init__(self, coro: Coroutine["asyncio.Future[Any]", None, _RetType]) -> No def send(self, arg: None) -> "asyncio.Future[Any]": return self._coro.send(arg) - def throw(self, arg: BaseException) -> None: # type: ignore + def throw(self, arg: BaseException) -> None: # type: ignore[arg-type,override] self._coro.throw(arg) def close(self) -> None: diff --git a/aiohttp/client_exceptions.py b/aiohttp/client_exceptions.py index 12fffff64ab..808c1cc614e 100644 --- a/aiohttp/client_exceptions.py +++ b/aiohttp/client_exceptions.py @@ -11,7 +11,7 @@ SSLContext = ssl.SSLContext except ImportError: # pragma: no cover - ssl = SSLContext = None # type: ignore + ssl = SSLContext = None # type: ignore[assignment] if TYPE_CHECKING: # pragma: no cover @@ -270,11 +270,11 @@ class ClientSSLError(ClientConnectorError): ssl_error_bases = (ClientSSLError,) -class ClientConnectorSSLError(*ssl_error_bases): # type: ignore +class ClientConnectorSSLError(*ssl_error_bases): # type: ignore[misc] """Response ssl error.""" -class ClientConnectorCertificateError(*cert_errors_bases): # type: ignore +class ClientConnectorCertificateError(*cert_errors_bases): # type: ignore[misc] """Response certificate error.""" def __init__( diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 801bc6dcda7..c63b73bcdf8 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -64,13 +64,13 @@ import ssl from ssl import SSLContext except ImportError: # pragma: no cover - ssl = None # type: ignore - SSLContext = object # type: ignore + ssl = None # type: ignore[assignment] + SSLContext = object # type: ignore[misc,assignment] try: import cchardet as chardet except ImportError: # pragma: no cover - import chardet # type: ignore + import chardet # type: ignore[no-redef] __all__ = ("ClientRequest", "ClientResponse", "RequestInfo", "Fingerprint") @@ -333,9 +333,9 @@ def update_headers(self, headers: Optional[LooseHeaders]) -> None: if headers: if isinstance(headers, (dict, MultiDictProxy, MultiDict)): - headers = headers.items() # type: ignore + headers = headers.items() # type: ignore[assignment] - for key, value in headers: # type: ignore + for key, value in headers: # type: ignore[misc] # A special case for Host header if key.lower() == "host": self.headers[key] = value @@ -347,7 +347,7 @@ def update_auto_headers(self, skip_auto_headers: Iterable[str]) -> None: (hdr, None) for hdr in sorted(skip_auto_headers) ) used_headers = self.headers.copy() - used_headers.extend(self.skip_auto_headers) # type: ignore + used_headers.extend(self.skip_auto_headers) # type: ignore[arg-type] for hdr, val in self.DEFAULT_HEADERS.items(): if hdr not in used_headers: @@ -369,7 +369,7 @@ def update_cookies(self, cookies: Optional[LooseCookies]) -> None: if isinstance(cookies, Mapping): iter_cookies = cookies.items() else: - iter_cookies = cookies # type: ignore + iter_cookies = cookies # type: ignore[assignment] for name, value in iter_cookies: if isinstance(value, Morsel): # Preserve coded_value @@ -377,7 +377,7 @@ def update_cookies(self, cookies: Optional[LooseCookies]) -> None: mrsl_val.set(value.key, value.value, value.coded_value) c[name] = mrsl_val else: - c[name] = value # type: ignore + c[name] = value # type: ignore[assignment] self.headers[hdrs.COOKIE] = c.output(header="", sep=";").strip() @@ -519,10 +519,10 @@ async def write_bytes( await self.body.write(writer) else: if isinstance(self.body, (bytes, bytearray)): - self.body = (self.body,) # type: ignore + self.body = (self.body,) # type: ignore[assignment] for chunk in self.body: - await writer.write(chunk) # type: ignore + await writer.write(chunk) # type: ignore[arg-type] await writer.write_eof() except OSError as exc: @@ -806,7 +806,7 @@ def links(self) -> "MultiDictProxy[MultiDictProxy[Union[str, URL]]]": link.add(key, value) - key = link.get("rel", url) # type: ignore + key = link.get("rel", url) # type: ignore[assignment] link.add("url", self.url.join(URL(url))) @@ -824,7 +824,8 @@ async def start(self, connection: "Connection") -> "ClientResponse": while True: # read response try: - message, payload = await self._protocol.read() # type: ignore + protocol = self._protocol + message, payload = await protocol.read() # type: ignore[union-attr] except http.HttpProcessingError as exc: raise ClientResponseError( self.request_info, @@ -1011,7 +1012,7 @@ async def text(self, encoding: Optional[str] = None, errors: str = "strict") -> if encoding is None: encoding = self.get_encoding() - return self._body.decode(encoding, errors=errors) # type: ignore + return self._body.decode(encoding, errors=errors) # type: ignore[union-attr] async def json( self, @@ -1039,7 +1040,7 @@ async def json( if encoding is None: encoding = self.get_encoding() - return loads(self._body.decode(encoding)) # type: ignore + return loads(self._body.decode(encoding)) # type: ignore[union-attr] async def __aenter__(self) -> "ClientResponse": return self diff --git a/aiohttp/client_ws.py b/aiohttp/client_ws.py index f25c4dd072c..17690f2a076 100644 --- a/aiohttp/client_ws.py +++ b/aiohttp/client_ws.py @@ -2,7 +2,7 @@ import asyncio import dataclasses -from typing import Any, Optional +from typing import Any, Optional, cast import async_timeout from typing_extensions import Final @@ -68,10 +68,10 @@ def __init__( self._autoclose = autoclose self._autoping = autoping self._heartbeat = heartbeat - self._heartbeat_cb = None + self._heartbeat_cb: Optional[asyncio.TimerHandle] = None if heartbeat is not None: self._pong_heartbeat = heartbeat / 2.0 - self._pong_response_cb = None + self._pong_response_cb: Optional[asyncio.TimerHandle] = None self._loop = loop self._waiting = None # type: Optional[asyncio.Future[bool]] self._exception = None # type: Optional[BaseException] @@ -286,13 +286,13 @@ async def receive_str(self, *, timeout: Optional[float] = None) -> str: msg = await self.receive(timeout) if msg.type != WSMsgType.TEXT: raise TypeError(f"Received message {msg.type}:{msg.data!r} is not str") - return msg.data + return cast(str, msg.data) async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes: msg = await self.receive(timeout) if msg.type != WSMsgType.BINARY: raise TypeError(f"Received message {msg.type}:{msg.data!r} is not bytes") - return msg.data + return cast(bytes, msg.data) async def receive_json( self, diff --git a/aiohttp/connector.py b/aiohttp/connector.py index 46ecdb89792..58c528de6fb 100644 --- a/aiohttp/connector.py +++ b/aiohttp/connector.py @@ -55,8 +55,8 @@ SSLContext = ssl.SSLContext except ImportError: # pragma: no cover - ssl = None # type: ignore - SSLContext = object # type: ignore + ssl = None # type: ignore[assignment] + SSLContext = object # type: ignore[misc,assignment] __all__ = ("BaseConnector", "TCPConnector", "UnixConnector", "NamedPipeConnector") @@ -218,7 +218,7 @@ def __init__( self._force_close = force_close # {host_key: FIFO list of waiters} - self._waiters = defaultdict(deque) # type: ignore + self._waiters = defaultdict(deque) # type: ignore[var-annotated] self._loop = loop self._factory = functools.partial(ResponseHandler, loop=loop) @@ -226,10 +226,10 @@ def __init__( self.cookies = SimpleCookie() # type: SimpleCookie[str] # start keep-alive connection cleanup task - self._cleanup_handle = None + self._cleanup_handle: Optional[asyncio.TimerHandle] = None # start cleanup closed transports task - self._cleanup_closed_handle = None + self._cleanup_closed_handle: Optional[asyncio.TimerHandle] = None self._cleanup_closed_disabled = not enable_cleanup_closed self._cleanup_closed_transports = [] # type: List[Optional[asyncio.Transport]] self._cleanup_closed() @@ -744,7 +744,7 @@ def __init__( self._ssl = ssl if resolver is None: resolver = DefaultResolver() - self._resolver = resolver + self._resolver: AbstractResolver = resolver self._use_dns_cache = use_dns_cache self._cached_hosts = _DNSCacheTable(ttl=ttl_dns_cache) @@ -943,7 +943,7 @@ async def _wrap_create_connection( ) -> Tuple[asyncio.Transport, ResponseHandler]: try: async with ceil_timeout(timeout.sock_connect): - return await self._loop.create_connection(*args, **kwargs) # type: ignore # noqa + return await self._loop.create_connection(*args, **kwargs) # type: ignore[return-value] # noqa except cert_errors as exc: raise ClientConnectorCertificateError(req.connection_key, exc) from exc except ssl_errors as exc: @@ -1031,7 +1031,7 @@ async def _create_proxy_connection( ) -> Tuple[asyncio.Transport, ResponseHandler]: headers = {} # type: Dict[str, str] if req.proxy_headers is not None: - headers = req.proxy_headers # type: ignore + headers = req.proxy_headers # type: ignore[assignment] headers[hdrs.HOST] = req.headers[hdrs.HOST] url = req.proxy @@ -1202,7 +1202,9 @@ def __init__( limit=limit, limit_per_host=limit_per_host, ) - if not isinstance(self._loop, asyncio.ProactorEventLoop): # type: ignore + if not isinstance( + self._loop, asyncio.ProactorEventLoop # type: ignore[attr-defined] + ): raise RuntimeError( "Named Pipes only available in proactor " "loop under windows" ) @@ -1218,7 +1220,7 @@ async def _create_connection( ) -> ResponseHandler: try: async with ceil_timeout(timeout.sock_connect): - _, proto = await self._loop.create_pipe_connection( # type: ignore + _, proto = await self._loop.create_pipe_connection( # type: ignore[attr-defined] # noqa: E501 self._factory, self._path ) # the drain is required so that the connection_made is called diff --git a/aiohttp/cookiejar.py b/aiohttp/cookiejar.py index da76d2d8850..8d0a1edf49b 100644 --- a/aiohttp/cookiejar.py +++ b/aiohttp/cookiejar.py @@ -147,7 +147,7 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No for name, cookie in cookies: if not isinstance(cookie, Morsel): tmp = SimpleCookie() # type: SimpleCookie[str] - tmp[name] = cookie # type: ignore + tmp[name] = cookie # type: ignore[assignment] cookie = tmp[name] domain = cookie["domain"] diff --git a/aiohttp/formdata.py b/aiohttp/formdata.py index 7eaf122a528..ef1e697497d 100644 --- a/aiohttp/formdata.py +++ b/aiohttp/formdata.py @@ -92,14 +92,14 @@ def add_fields(self, *fields: Any) -> None: if isinstance(rec, io.IOBase): k = guess_filename(rec, "unknown") - self.add_field(k, rec) # type: ignore + self.add_field(k, rec) # type: ignore[arg-type] elif isinstance(rec, (MultiDictProxy, MultiDict)): to_add.extend(rec.items()) elif isinstance(rec, (list, tuple)) and len(rec) == 2: k, fp = rec - self.add_field(k, fp) # type: ignore + self.add_field(k, fp) # type: ignore[arg-type] else: raise TypeError( diff --git a/aiohttp/helpers.py b/aiohttp/helpers.py index 1b572fd3c0b..ed5387aaf65 100644 --- a/aiohttp/helpers.py +++ b/aiohttp/helpers.py @@ -410,8 +410,8 @@ def is_expected_content_type( return expected_content_type in response_content_type -class _TSelf(Protocol): - _cache: Dict[str, Any] +class _TSelf(Protocol, Generic[_T]): + _cache: Dict[str, _T] class reify(Generic[_T]): @@ -428,7 +428,7 @@ def __init__(self, wrapped: Callable[..., _T]) -> None: self.__doc__ = wrapped.__doc__ self.name = wrapped.__name__ - def __get__(self, inst: _TSelf, owner: Optional[Type[Any]] = None) -> _T: + def __get__(self, inst: _TSelf[_T], owner: Optional[Type[Any]] = None) -> _T: try: try: return inst._cache[self.name] @@ -441,7 +441,7 @@ def __get__(self, inst: _TSelf, owner: Optional[Type[Any]] = None) -> _T: return self raise - def __set__(self, inst: _TSelf, value: _T) -> None: + def __set__(self, inst: _TSelf[_T], value: _T) -> None: raise AttributeError("reified property is read-only") @@ -451,7 +451,7 @@ def __set__(self, inst: _TSelf, value: _T) -> None: from ._helpers import reify as reify_c if not NO_EXTENSIONS: - reify = reify_c # type: ignore + reify = reify_c # type: ignore[misc,assignment] except ImportError: pass @@ -547,7 +547,7 @@ def rfc822_formatted_time() -> str: return _cached_formatted_datetime -def _weakref_handle(info): # type: ignore +def _weakref_handle(info: "Tuple[weakref.ref[object], str]") -> None: ref, name = info ob = ref() if ob is not None: @@ -555,21 +555,27 @@ def _weakref_handle(info): # type: ignore getattr(ob, name)() -def weakref_handle(ob, name, timeout, loop): # type: ignore +def weakref_handle( + ob: object, name: str, timeout: float, loop: asyncio.AbstractEventLoop +) -> Optional[asyncio.TimerHandle]: if timeout is not None and timeout > 0: when = loop.time() + timeout if timeout >= 5: when = ceil(when) return loop.call_at(when, _weakref_handle, (weakref.ref(ob), name)) + return None -def call_later(cb, timeout, loop): # type: ignore +def call_later( + cb: Callable[[], Any], timeout: float, loop: asyncio.AbstractEventLoop +) -> Optional[asyncio.TimerHandle]: if timeout is not None and timeout > 0: when = loop.time() + timeout if timeout > 5: when = ceil(when) return loop.call_at(when, cb) + return None class TimeoutHandle: @@ -713,23 +719,25 @@ def _parse_content_type(self, raw: str) -> None: @property def content_type(self) -> str: """The value of content part for Content-Type HTTP header.""" - raw = self._headers.get(hdrs.CONTENT_TYPE) # type: ignore + raw = self._headers.get(hdrs.CONTENT_TYPE) # type: ignore[attr-defined] if self._stored_content_type != raw: self._parse_content_type(raw) - return self._content_type # type: ignore + return self._content_type # type: ignore[return-value] @property def charset(self) -> Optional[str]: """The value of charset part for Content-Type HTTP header.""" - raw = self._headers.get(hdrs.CONTENT_TYPE) # type: ignore + raw = self._headers.get(hdrs.CONTENT_TYPE) # type: ignore[attr-defined] if self._stored_content_type != raw: self._parse_content_type(raw) - return self._content_dict.get("charset") # type: ignore + return self._content_dict.get("charset") # type: ignore[union-attr] @property def content_length(self) -> Optional[int]: """The value of Content-Length HTTP header.""" - content_length = self._headers.get(hdrs.CONTENT_LENGTH) # type: ignore + content_length = self._headers.get( # type: ignore[attr-defined] + hdrs.CONTENT_LENGTH + ) if content_length is not None: return int(content_length) @@ -773,7 +781,7 @@ def get(self, key: str, default: Any = None) -> Any: def __len__(self) -> int: # reuses stored hash values if possible - return len(set().union(*self._maps)) # type: ignore + return len(set().union(*self._maps)) # type: ignore[arg-type] def __iter__(self) -> Iterator[str]: d = {} # type: Dict[str, Any] diff --git a/aiohttp/http_parser.py b/aiohttp/http_parser.py index 8fe60a82c79..9487378da05 100644 --- a/aiohttp/http_parser.py +++ b/aiohttp/http_parser.py @@ -10,6 +10,7 @@ Any, Generic, List, + NamedTuple, Optional, Pattern, Set, @@ -17,6 +18,7 @@ Type, TypeVar, Union, + cast, ) from multidict import CIMultiDict, CIMultiDictProxy, istr @@ -69,21 +71,19 @@ VERSRE: Final[Pattern[str]] = re.compile(r"HTTP/(\d+).(\d+)") HDRRE: Final[Pattern[bytes]] = re.compile(rb"[\x00-\x1F\x7F()<>@,;:\[\]={} \t\\\\\"]") -RawRequestMessage = collections.namedtuple( - "RawRequestMessage", - [ - "method", - "path", - "version", - "headers", - "raw_headers", - "should_close", - "compression", - "upgrade", - "chunked", - "url", - ], -) + +class RawRequestMessage(NamedTuple): + method: str + path: str + version: HttpVersion + headers: CIMultiDictProxy[str] + raw_headers: RawHeaders + should_close: bool + compression: Optional[str] + upgrade: bool + chunked: bool + url: URL + RawResponseMessage = collections.namedtuple( "RawResponseMessage", @@ -312,20 +312,27 @@ def feed_data( # \r\n\r\n found if self._lines[-1] == EMPTY: try: - msg = self.parse_message(self._lines) + msg: _MsgT = self.parse_message(self._lines) finally: self._lines.clear() - # payload length - length = msg.headers.get(CONTENT_LENGTH) - if length is not None: + def get_content_lenght() -> Optional[int]: + # payload length + length_hdr = msg.headers.get(CONTENT_LENGTH) + if length_hdr is None: + return None + try: - length = int(length) + length = int(length_hdr) except ValueError: raise InvalidHeader(CONTENT_LENGTH) + if length < 0: raise InvalidHeader(CONTENT_LENGTH) + return length + + length = get_content_lenght() # do not support old websocket spec if SEC_WEBSOCKET_KEY1 in msg.headers: raise InvalidHeader(SEC_WEBSOCKET_KEY1) @@ -839,12 +846,12 @@ def __init__(self) -> None: def decompress(self, data: bytes) -> bytes: if hasattr(self._obj, "decompress"): - return self._obj.decompress(data) - return self._obj.process(data) + return cast(bytes, self._obj.decompress(data)) + return cast(bytes, self._obj.process(data)) def flush(self) -> bytes: if hasattr(self._obj, "flush"): - return self._obj.flush() + return cast(bytes, self._obj.flush()) return b"" self.decompressor = BrotliDecoder() # type: Any @@ -909,7 +916,7 @@ def end_http_chunk_receiving(self) -> None: try: if not NO_EXTENSIONS: - from ._http_parser import ( # type: ignore + from ._http_parser import ( # type: ignore[import,no-redef] HttpRequestParser, HttpResponseParser, RawRequestMessage, diff --git a/aiohttp/http_websocket.py b/aiohttp/http_websocket.py index bff70f09685..ee877fd4def 100644 --- a/aiohttp/http_websocket.py +++ b/aiohttp/http_websocket.py @@ -9,7 +9,7 @@ import zlib from enum import IntEnum from struct import Struct -from typing import Any, Callable, List, Optional, Pattern, Set, Tuple, Union +from typing import Any, Callable, List, Optional, Pattern, Set, Tuple, Union, cast from typing_extensions import Final @@ -102,7 +102,7 @@ def __init__(self, code: int, message: str) -> None: super().__init__(code, message) def __str__(self) -> str: - return self.args[1] + return cast(str, self.args[1]) class WSHandshakeError(Exception): @@ -144,7 +144,7 @@ def _websocket_mask_python(mask: bytes, data: bytearray) -> None: _websocket_mask = _websocket_mask_python else: try: - from ._websocket import _websocket_mask_cython # type: ignore + from ._websocket import _websocket_mask_cython # type: ignore[import] _websocket_mask = _websocket_mask_cython except ImportError: # pragma: no cover diff --git a/aiohttp/http_writer.py b/aiohttp/http_writer.py index ffec6a756f9..f859790efdd 100644 --- a/aiohttp/http_writer.py +++ b/aiohttp/http_writer.py @@ -183,7 +183,7 @@ def _py_serialize_headers(status_line: str, headers: "CIMultiDict[str]") -> byte _serialize_headers = _py_serialize_headers try: - import aiohttp._http_writer as _http_writer # type: ignore + import aiohttp._http_writer as _http_writer # type: ignore[import] _c_serialize_headers = _http_writer._serialize_headers if not NO_EXTENSIONS: diff --git a/aiohttp/multipart.py b/aiohttp/multipart.py index 00ba01af7be..b5e78c835e6 100644 --- a/aiohttp/multipart.py +++ b/aiohttp/multipart.py @@ -11,6 +11,7 @@ TYPE_CHECKING, Any, AsyncIterator, + Deque, Dict, Iterator, List, @@ -20,6 +21,7 @@ Tuple, Type, Union, + cast, ) from urllib.parse import parse_qsl, unquote, urlencode @@ -267,13 +269,13 @@ def __init__( self._length = int(length) if length is not None else None self._read_bytes = 0 # TODO: typeing.Deque is not supported by Python 3.5 - self._unread = deque() # type: Any + self._unread: Deque[bytes] = deque() self._prev_chunk = None # type: Optional[bytes] self._content_eof = 0 self._cache = {} # type: Dict[str, Any] def __aiter__(self) -> AsyncIterator["BodyPartReader"]: - return self # type: ignore + return self # type: ignore[return-value] async def __anext__(self) -> bytes: part = await self.next() @@ -448,7 +450,7 @@ async def json(self, *, encoding: Optional[str] = None) -> Optional[Dict[str, An if not data: return None encoding = encoding or self.get_charset(default="utf-8") - return json.loads(data.decode(encoding)) + return cast(Dict[str, Any], json.loads(data.decode(encoding))) async def form(self, *, encoding: Optional[str] = None) -> List[Tuple[str, str]]: """Like read(), but assumes that body parts contains form @@ -585,7 +587,7 @@ def __init__( def __aiter__( self, ) -> AsyncIterator["BodyPartReader"]: - return self # type: ignore + return self # type: ignore[return-value] async def __anext__( self, @@ -886,7 +888,7 @@ def append_payload(self, payload: Payload) -> Payload: if size is not None and not (encoding or te_encoding): payload.headers[CONTENT_LENGTH] = str(size) - self._parts.append((payload, encoding, te_encoding)) # type: ignore + self._parts.append((payload, encoding, te_encoding)) # type: ignore[arg-type] return payload def append_json( @@ -951,7 +953,7 @@ async def write(self, writer: Any, close_boundary: bool = True) -> None: w.enable_compression(encoding) if te_encoding: w.enable_encoding(te_encoding) - await part.write(w) # type: ignore + await part.write(w) # type: ignore[arg-type] await w.write_eof() else: await part.write(writer) diff --git a/aiohttp/payload.py b/aiohttp/payload.py index b3f5f842e49..b88da2cd8ed 100644 --- a/aiohttp/payload.py +++ b/aiohttp/payload.py @@ -55,7 +55,6 @@ TOO_LARGE_BYTES_BODY: Final[int] = 2 ** 20 # 1 MB - if TYPE_CHECKING: # pragma: no cover from typing import List @@ -90,6 +89,10 @@ def __call__(self, factory: Type["Payload"]) -> Type["Payload"]: return factory +PayloadType = Type["Payload"] +_PayloadRegistryItem = Tuple[PayloadType, Any] + + class PayloadRegistry: """Payload registry. @@ -97,12 +100,16 @@ class PayloadRegistry: """ def __init__(self) -> None: - self._first = [] # type: List[Tuple[Type[Payload], Any]] - self._normal = [] # type: List[Tuple[Type[Payload], Any]] - self._last = [] # type: List[Tuple[Type[Payload], Any]] + self._first = [] # type: List[_PayloadRegistryItem] + self._normal = [] # type: List[_PayloadRegistryItem] + self._last = [] # type: List[_PayloadRegistryItem] def get( - self, data: Any, *args: Any, _CHAIN: Any = chain, **kwargs: Any + self, + data: Any, + *args: Any, + _CHAIN: "Type[chain[_PayloadRegistryItem]]" = chain, + **kwargs: Any, ) -> "Payload": if isinstance(data, Payload): return data @@ -113,7 +120,7 @@ def get( raise LookupError() def register( - self, factory: Type["Payload"], type: Any, *, order: Order = Order.normal + self, factory: PayloadType, type: Any, *, order: Order = Order.normal ) -> None: if order is Order.try_first: self._first.append((factory, type)) @@ -278,6 +285,8 @@ def __init__(self, value: IO[str], *args: Any, **kwargs: Any) -> None: class IOBasePayload(Payload): + _value: IO[Any] + def __init__( self, value: IO[Any], disposition: str = "attachment", *args: Any, **kwargs: Any ) -> None: @@ -302,6 +311,8 @@ async def write(self, writer: AbstractStreamWriter) -> None: class TextIOPayload(IOBasePayload): + _value: TextIO + def __init__( self, value: TextIO, @@ -342,7 +353,12 @@ async def write(self, writer: AbstractStreamWriter) -> None: try: chunk = await loop.run_in_executor(None, self._value.read, 2 ** 16) while chunk: - await writer.write(chunk.encode(self._encoding)) + data = ( + chunk.encode(encoding=self._encoding) + if self._encoding + else chunk.encode() + ) + await writer.write(data) chunk = await loop.run_in_executor(None, self._value.read, 2 ** 16) finally: await loop.run_in_executor(None, self._value.close) diff --git a/aiohttp/pytest_plugin.py b/aiohttp/pytest_plugin.py index 1c237132af7..50466410bc3 100644 --- a/aiohttp/pytest_plugin.py +++ b/aiohttp/pytest_plugin.py @@ -29,7 +29,7 @@ tokio = None -def pytest_addoption(parser): # type: ignore +def pytest_addoption(parser): # type: ignore[no-untyped-def] parser.addoption( "--aiohttp-fast", action="store_true", @@ -50,7 +50,7 @@ def pytest_addoption(parser): # type: ignore ) -def pytest_fixture_setup(fixturedef): # type: ignore +def pytest_fixture_setup(fixturedef): # type: ignore[no-untyped-def] """ Allow fixtures to be coroutines. Run coroutine fixtures in an event loop. """ @@ -71,7 +71,7 @@ def pytest_fixture_setup(fixturedef): # type: ignore fixturedef.argnames += ("request",) strip_request = True - def wrapper(*args, **kwargs): # type: ignore + def wrapper(*args, **kwargs): # type: ignore[no-untyped-def] request = kwargs["request"] if strip_request: del kwargs["request"] @@ -92,7 +92,7 @@ def wrapper(*args, **kwargs): # type: ignore # then advance it again in a finalizer gen = func(*args, **kwargs) - def finalizer(): # type: ignore + def finalizer(): # type: ignore[no-untyped-def] try: return _loop.run_until_complete(gen.__anext__()) except StopAsyncIteration: @@ -107,19 +107,19 @@ def finalizer(): # type: ignore @pytest.fixture -def fast(request): # type: ignore +def fast(request): # type: ignore[no-untyped-def] """--fast config option""" return request.config.getoption("--aiohttp-fast") @pytest.fixture -def loop_debug(request): # type: ignore +def loop_debug(request): # type: ignore[no-untyped-def] """--enable-loop-debug config option""" return request.config.getoption("--aiohttp-enable-loop-debug") @contextlib.contextmanager -def _runtime_warning_context(): # type: ignore +def _runtime_warning_context(): # type: ignore[no-untyped-def] """ Context manager which checks for RuntimeWarnings, specifically to avoid "coroutine 'X' was never awaited" warnings being missed. @@ -148,7 +148,7 @@ def _runtime_warning_context(): # type: ignore @contextlib.contextmanager -def _passthrough_loop_context(loop, fast=False): # type: ignore +def _passthrough_loop_context(loop, fast=False): # type: ignore[no-untyped-def] """ setups and tears down a loop unless one is passed in via the loop argument when it's passed straight through. @@ -163,7 +163,7 @@ def _passthrough_loop_context(loop, fast=False): # type: ignore teardown_test_loop(loop, fast=fast) -def pytest_pycollect_makeitem(collector, name, obj): # type: ignore +def pytest_pycollect_makeitem(collector, name, obj): # type: ignore[no-untyped-def] """ Fix pytest collecting for coroutines. """ @@ -171,7 +171,7 @@ def pytest_pycollect_makeitem(collector, name, obj): # type: ignore return list(collector._genfunctions(name, obj)) -def pytest_pyfunc_call(pyfuncitem): # type: ignore +def pytest_pyfunc_call(pyfuncitem): # type: ignore[no-untyped-def] """ Run coroutines in an event loop instead of a normal function call. """ @@ -191,7 +191,7 @@ def pytest_pyfunc_call(pyfuncitem): # type: ignore return True -def pytest_generate_tests(metafunc): # type: ignore +def pytest_generate_tests(metafunc): # type: ignore[no-untyped-def] if "loop_factory" not in metafunc.fixturenames: return @@ -207,7 +207,7 @@ def pytest_generate_tests(metafunc): # type: ignore if loops == "all": loops = "pyloop,uvloop?,tokio?" - factories = {} # type: ignore + factories = {} # type: ignore[var-annotated] for name in loops.split(","): required = not name.endswith("?") name = name.strip(" ?") @@ -226,7 +226,7 @@ def pytest_generate_tests(metafunc): # type: ignore @pytest.fixture -def loop(loop_factory, fast, loop_debug): # type: ignore +def loop(loop_factory, fast, loop_debug): # type: ignore[no-untyped-def] """Return an instance of the event loop.""" policy = loop_factory() asyncio.set_event_loop_policy(policy) @@ -238,8 +238,8 @@ def loop(loop_factory, fast, loop_debug): # type: ignore @pytest.fixture -def proactor_loop(): # type: ignore - policy = asyncio.WindowsProactorEventLoopPolicy() # type: ignore +def proactor_loop(): # type: ignore[no-untyped-def] + policy = asyncio.WindowsProactorEventLoopPolicy() # type: ignore[attr-defined] asyncio.set_event_loop_policy(policy) with loop_context(policy.new_event_loop) as _loop: @@ -248,20 +248,20 @@ def proactor_loop(): # type: ignore @pytest.fixture -def aiohttp_unused_port(): # type: ignore +def aiohttp_unused_port(): # type: ignore[no-untyped-def] """Return a port that is unused on the current host.""" return _unused_port @pytest.fixture -def aiohttp_server(loop): # type: ignore +def aiohttp_server(loop): # type: ignore[no-untyped-def] """Factory to create a TestServer instance, given an app. aiohttp_server(app, **kwargs) """ servers = [] - async def go(app, *, port=None, **kwargs): # type: ignore + async def go(app, *, port=None, **kwargs): # type: ignore[no-untyped-def] server = TestServer(app, port=port) await server.start_server(**kwargs) servers.append(server) @@ -269,7 +269,7 @@ async def go(app, *, port=None, **kwargs): # type: ignore yield go - async def finalize(): # type: ignore + async def finalize() -> None: while servers: await servers.pop().close() @@ -277,14 +277,14 @@ async def finalize(): # type: ignore @pytest.fixture -def aiohttp_raw_server(loop): # type: ignore +def aiohttp_raw_server(loop): # type: ignore[no-untyped-def] """Factory to create a RawTestServer instance, given a web handler. aiohttp_raw_server(handler, **kwargs) """ servers = [] - async def go(handler, *, port=None, **kwargs): # type: ignore + async def go(handler, *, port=None, **kwargs): # type: ignore[no-untyped-def] server = RawTestServer(handler, port=port) await server.start_server(**kwargs) servers.append(server) @@ -292,7 +292,7 @@ async def go(handler, *, port=None, **kwargs): # type: ignore yield go - async def finalize(): # type: ignore + async def finalize() -> None: while servers: await servers.pop().close() @@ -300,7 +300,7 @@ async def finalize(): # type: ignore @pytest.fixture -def aiohttp_client_cls(): # type: ignore +def aiohttp_client_cls(): # type: ignore[no-untyped-def] """ Client class to use in ``aiohttp_client`` factory. @@ -327,7 +327,7 @@ def test_login(aiohttp_client): @pytest.fixture -def aiohttp_client(loop, aiohttp_client_cls): # type: ignore +def aiohttp_client(loop, aiohttp_client_cls): # type: ignore[no-untyped-def] """Factory to create a TestClient instance. aiohttp_client(app, **kwargs) @@ -336,7 +336,9 @@ def aiohttp_client(loop, aiohttp_client_cls): # type: ignore """ clients = [] - async def go(__param, *, server_kwargs=None, **kwargs): # type: ignore + async def go( # type: ignore[no-untyped-def] + __param, *, server_kwargs=None, **kwargs + ): if isinstance(__param, Application): server_kwargs = server_kwargs or {} server = TestServer(__param, **server_kwargs) @@ -352,7 +354,7 @@ async def go(__param, *, server_kwargs=None, **kwargs): # type: ignore yield go - async def finalize(): # type: ignore + async def finalize() -> None: while clients: await clients.pop().close() diff --git a/aiohttp/resolver.py b/aiohttp/resolver.py index f1689915bbb..a3a362c0293 100644 --- a/aiohttp/resolver.py +++ b/aiohttp/resolver.py @@ -37,7 +37,7 @@ async def resolve( hosts = [] for family, _, proto, _, address in infos: - if family == socket.AF_INET6 and address[3]: # type: ignore + if family == socket.AF_INET6 and address[3]: # type: ignore[misc] # This is essential for link-local IPv6 addresses. # LL IPv6 is a VERY rare case. Strictly speaking, we should use # getnameinfo() unconditionally, but performance makes sense. @@ -101,7 +101,7 @@ async def resolve( return hosts async def close(self) -> None: - return self._resolver.cancel() + self._resolver.cancel() DefaultResolver = AsyncResolver if aiodns_default else ThreadedResolver diff --git a/aiohttp/streams.py b/aiohttp/streams.py index d07b16e61ca..a077b81b82d 100644 --- a/aiohttp/streams.py +++ b/aiohttp/streams.py @@ -62,14 +62,16 @@ async def __anext__(self) -> Tuple[bytes, bool]: class AsyncStreamReaderMixin: def __aiter__(self) -> AsyncStreamIterator[bytes]: - return AsyncStreamIterator(self.readline) # type: ignore + return AsyncStreamIterator(self.readline) # type: ignore[attr-defined] def iter_chunked(self, n: int) -> AsyncStreamIterator[bytes]: """Returns an asynchronous iterator that yields chunks of size n. Python-3.5 available for Python 3.5+ only """ - return AsyncStreamIterator(lambda: self.read(n)) # type: ignore + return AsyncStreamIterator( + lambda: self.read(n) # type: ignore[attr-defined,no-any-return] + ) def iter_any(self) -> AsyncStreamIterator[bytes]: """Returns an asynchronous iterator that yields all the available @@ -77,7 +79,7 @@ def iter_any(self) -> AsyncStreamIterator[bytes]: Python-3.5 available for Python 3.5+ only """ - return AsyncStreamIterator(self.readany) # type: ignore + return AsyncStreamIterator(self.readany) # type: ignore[attr-defined] def iter_chunks(self) -> ChunkTupleAsyncStreamIterator: """Returns an asynchronous iterator that yields chunks of data @@ -86,7 +88,7 @@ def iter_chunks(self) -> ChunkTupleAsyncStreamIterator: Python-3.5 available for Python 3.5+ only """ - return ChunkTupleAsyncStreamIterator(self) # type: ignore + return ChunkTupleAsyncStreamIterator(self) # type: ignore[arg-type] class StreamReader(AsyncStreamReaderMixin): diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py index 35f4e0a79ec..dd6c91c368e 100644 --- a/aiohttp/test_utils.py +++ b/aiohttp/test_utils.py @@ -11,7 +11,17 @@ import sys from abc import ABC, abstractmethod from types import TracebackType -from typing import TYPE_CHECKING, Any, Callable, Iterator, List, Optional, Type, Union +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Iterator, + List, + Optional, + Type, + Union, + cast, +) from unittest import mock from aiosignal import Signal @@ -47,7 +57,7 @@ if PY_38: from unittest import IsolatedAsyncioTestCase as TestCase else: - from asynctest import TestCase # type: ignore + from asynctest import TestCase # type: ignore[no-redef] REUSE_ADDRESS = os.name == "posix" and sys.platform != "cygwin" @@ -75,7 +85,7 @@ def unused_port() -> int: """Return a port that is unused on the current host.""" with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind(("127.0.0.1", 0)) - return s.getsockname()[1] + return cast(int, s.getsockname()[1]) class BaseTestServer(ABC): @@ -277,8 +287,8 @@ def server(self) -> BaseTestServer: return self._server @property - def app(self) -> Application: - return getattr(self._server, "app", None) + def app(self) -> Optional[Application]: + return cast(Optional[Application], getattr(self._server, "app", None)) @property def session(self) -> ClientSession: @@ -600,7 +610,7 @@ def make_mocked_request( headers, raw_hdrs, closing, - False, + None, False, chunked, URL(path), diff --git a/aiohttp/web.py b/aiohttp/web.py index 8a5030cc261..6521e29e1c8 100644 --- a/aiohttp/web.py +++ b/aiohttp/web.py @@ -278,7 +278,7 @@ try: from ssl import SSLContext except ImportError: # pragma: no cover - SSLContext = Any # type: ignore + SSLContext = Any # type: ignore[misc,assignment] HostSequence = TypingIterable[str] @@ -304,7 +304,7 @@ async def _run_app( ) -> None: # An internal function to actually do all dirty job for application running if asyncio.iscoroutine(app): - app = await app # type: ignore + app = await app # type: ignore[misc] app = cast(Application, app) diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index d716ad31243..15d3c430e5e 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -334,7 +334,7 @@ async def _handle(self, request: Request) -> StreamResponse: match_info.freeze() resp = None - request._match_info = match_info # type: ignore + request._match_info = match_info # type: ignore[assignment] expect = request.headers.get(hdrs.EXPECT) if expect: resp = await match_info.expect_handler(request) @@ -367,7 +367,7 @@ def __bool__(self) -> bool: class CleanupError(RuntimeError): @property def exceptions(self) -> List[BaseException]: - return self.args[1] + return cast(List[BaseException], self.args[1]) if TYPE_CHECKING: # pragma: no cover diff --git a/aiohttp/web_fileresponse.py b/aiohttp/web_fileresponse.py index 6e475010fc2..ff904dd57b0 100644 --- a/aiohttp/web_fileresponse.py +++ b/aiohttp/web_fileresponse.py @@ -213,12 +213,12 @@ async def prepare(self, request: "BaseRequest") -> Optional[AbstractStreamWriter self.set_status(status) if should_set_ct: - self.content_type = ct # type: ignore + self.content_type = ct # type: ignore[assignment] if encoding: self.headers[hdrs.CONTENT_ENCODING] = encoding if gzip: self.headers[hdrs.VARY] = hdrs.ACCEPT_ENCODING - self.last_modified = st.st_mtime # type: ignore + self.last_modified = st.st_mtime # type: ignore[assignment] self.content_length = count self.headers[hdrs.ACCEPT_RANGES] = "bytes" diff --git a/aiohttp/web_log.py b/aiohttp/web_log.py index 2e898080ea9..2672a8b1981 100644 --- a/aiohttp/web_log.py +++ b/aiohttp/web_log.py @@ -200,10 +200,10 @@ def log(self, request: BaseRequest, response: StreamResponse, time: float) -> No if key.__class__ is str: extra[key] = value else: - k1, k2 = key # type: ignore - dct = extra.get(k1, {}) # type: ignore - dct[k2] = value # type: ignore - extra[k1] = dct # type: ignore + k1, k2 = key # type: ignore[misc] + dct = extra.get(k1, {}) # type: ignore[var-annotated,has-type] + dct[k2] = value # type: ignore[index,has-type] + extra[k1] = dct # type: ignore[has-type,assignment] self.logger.info(self._log_format % tuple(values), extra=extra) except Exception: diff --git a/aiohttp/web_middlewares.py b/aiohttp/web_middlewares.py index 1c30b93f9f0..ad6284a5081 100644 --- a/aiohttp/web_middlewares.py +++ b/aiohttp/web_middlewares.py @@ -22,7 +22,7 @@ async def _check_request_resolves(request: Request, path: str) -> Tuple[bool, Re alt_request = request.clone(rel_url=path) match_info = await request.app.router.resolve(alt_request) - alt_request._match_info = match_info # type: ignore + alt_request._match_info = match_info # type: ignore[assignment] if match_info.http_exception is None: return True, alt_request diff --git a/aiohttp/web_protocol.py b/aiohttp/web_protocol.py index d5f53335b7d..f2f0d8866c3 100644 --- a/aiohttp/web_protocol.py +++ b/aiohttp/web_protocol.py @@ -65,7 +65,16 @@ ] ERROR = RawRequestMessage( - "UNKNOWN", "/", HttpVersion10, {}, {}, True, False, False, False, yarl.URL("/") + "UNKNOWN", + "/", + HttpVersion10, + {}, # type: ignore[arg-type] + {}, # type: ignore[arg-type] + True, + None, + False, + False, + yarl.URL("/"), ) diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py index 7493b4ac58f..4ecbdc98884 100644 --- a/aiohttp/web_request.py +++ b/aiohttp/web_request.py @@ -218,12 +218,14 @@ def clone( if method is not sentinel: dct["method"] = method if rel_url is not sentinel: - new_url: URL = URL(rel_url) # type: ignore + new_url: URL = URL(rel_url) # type: ignore[arg-type] dct["url"] = new_url dct["path"] = str(new_url) if headers is not sentinel: # a copy semantic - new_headers = CIMultiDictProxy(CIMultiDict(headers)) # type: ignore + new_headers = CIMultiDictProxy( + CIMultiDict(headers) # type: ignore[arg-type] + ) dct["headers"] = new_headers dct["raw_headers"] = tuple( (k.encode("utf-8"), v.encode("utf-8")) for k, v in new_headers.items() @@ -233,11 +235,11 @@ def clone( kwargs: Dict[str, str] = {} if scheme is not sentinel: - kwargs["scheme"] = scheme # type: ignore + kwargs["scheme"] = scheme # type: ignore[assignment] if host is not sentinel: - kwargs["host"] = host # type: ignore + kwargs["host"] = host # type: ignore[assignment] if remote is not sentinel: - kwargs["remote"] = remote # type: ignore + kwargs["remote"] = remote # type: ignore[assignment] return self.__class__( message, @@ -403,8 +405,7 @@ def host(self) -> str: host = self._message.headers.get(hdrs.HOST) if host is not None: return host - else: - return socket.getfqdn() + return socket.getfqdn() @reify def remote(self) -> Optional[str]: @@ -415,10 +416,11 @@ def remote(self) -> Optional[str]: - overridden value by .clone(remote=new_remote) call. - peername of opened socket """ + if self._transport_peername is None: + return None if isinstance(self._transport_peername, (list, tuple)): - return self._transport_peername[0] - else: - return self._transport_peername + return str(self._transport_peername[0]) + return str(self._transport_peername) @reify def url(self) -> URL: @@ -451,9 +453,9 @@ def raw_path(self) -> str: return self._message.path @reify - def query(self) -> "MultiDictProxy[str]": + def query(self) -> MultiDictProxy[str]: """A multidict with all the variables in the query string.""" - return self._rel_url.query + return MultiDictProxy(self._rel_url.query) @reify def query_string(self) -> str: diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index ac3ee266243..ce3d4fda21d 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -53,7 +53,7 @@ if not PY_38: # allow samesite to be used in python < 3.8 # already permitted in python 3.8, see https://bugs.python.org/issue29613 - Morsel._reserved["samesite"] = "SameSite" # type: ignore + Morsel._reserved["samesite"] = "SameSite" # type: ignore[attr-defined] class ContentCoding(enum.Enum): @@ -124,8 +124,11 @@ def prepared(self) -> bool: return self._payload_writer is not None @property - def task(self) -> "asyncio.Task[None]": - return getattr(self._req, "task", None) + def task(self) -> "Optional[asyncio.Task[None]]": + if self._req: + return self._req.task + else: + return None @property def status(self) -> int: diff --git a/aiohttp/web_routedef.py b/aiohttp/web_routedef.py index 9e66de5453b..3ecbd943fc3 100644 --- a/aiohttp/web_routedef.py +++ b/aiohttp/web_routedef.py @@ -170,7 +170,7 @@ def __getitem__(self, index: int) -> AbstractRouteDef: def __getitem__(self, index: slice) -> List[AbstractRouteDef]: ... - def __getitem__(self, index): # type: ignore + def __getitem__(self, index): # type: ignore[no-untyped-def] return self._items[index] def __iter__(self) -> Iterator[AbstractRouteDef]: diff --git a/aiohttp/web_runner.py b/aiohttp/web_runner.py index 4bcb9a2d469..c5294ffe295 100644 --- a/aiohttp/web_runner.py +++ b/aiohttp/web_runner.py @@ -18,7 +18,7 @@ try: from ssl import SSLContext except ImportError: - SSLContext = object # type: ignore + SSLContext = object # type: ignore[misc,assignment] __all__ = ( @@ -177,7 +177,9 @@ def __init__( self, runner: "BaseRunner", path: str, *, shutdown_timeout: float = 60.0 ) -> None: loop = asyncio.get_event_loop() - if not isinstance(loop, asyncio.ProactorEventLoop): # type: ignore + if not isinstance( + loop, asyncio.ProactorEventLoop # type: ignore[attr-defined] + ): raise RuntimeError( "Named Pipes only available in proactor" "loop under windows" ) @@ -193,7 +195,9 @@ async def start(self) -> None: loop = asyncio.get_event_loop() server = self._runner.server assert server is not None - _server = await loop.start_serving_pipe(server, self._path) # type: ignore + _server = await loop.start_serving_pipe( # type: ignore[attr-defined] + server, self._path + ) self._server = _server[0] @@ -396,7 +400,7 @@ async def _make_server(self) -> Server: self._app.freeze() return Server( - self._app._handle, # type: ignore + self._app._handle, # type: ignore[arg-type] request_factory=self._make_request, **self._kwargs, ) diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 28e9232911d..a229cd7e4ed 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -30,7 +30,7 @@ ) from typing_extensions import Final, TypedDict -from yarl import URL, __version__ as yarl_version # type: ignore +from yarl import URL, __version__ as yarl_version # type: ignore[attr-defined] from . import hdrs from .abc import AbstractMatchInfo, AbstractRouter, AbstractView @@ -241,7 +241,7 @@ def expect_handler(self) -> _ExpectHandler: def http_exception(self) -> Optional[HTTPException]: return None - def get_info(self) -> _InfoDict: # type: ignore + def get_info(self) -> _InfoDict: # type: ignore[override] return self._route.get_info() @property @@ -406,7 +406,7 @@ def raw_match(self, path: str) -> bool: def get_info(self) -> _InfoDict: return {"path": self._path} - def url_for(self) -> URL: # type: ignore + def url_for(self) -> URL: # type: ignore[override] return URL.build(path=self._path, encoded=True) def __repr__(self) -> str: @@ -554,7 +554,7 @@ def __init__( ), } - def url_for( # type: ignore + def url_for( # type: ignore[override] self, *, filename: Union[str, Path], @@ -927,7 +927,9 @@ class View(AbstractView): async def _iter(self) -> StreamResponse: if self.request.method not in hdrs.METH_ALL: self._raise_allowed_methods() - method = getattr(self, self.request.method.lower(), None) + method: Callable[[], Awaitable[StreamResponse]] = getattr( + self, self.request.method.lower(), None + ) if method is None: self._raise_allowed_methods() resp = await method() diff --git a/aiohttp/web_ws.py b/aiohttp/web_ws.py index f23313d77bd..2303faa2c6a 100644 --- a/aiohttp/web_ws.py +++ b/aiohttp/web_ws.py @@ -4,7 +4,7 @@ import dataclasses import hashlib import json -from typing import Any, Iterable, Optional, Tuple +from typing import Any, Iterable, Optional, Tuple, cast import async_timeout from multidict import CIMultiDict @@ -106,10 +106,10 @@ def __init__( self._autoclose = autoclose self._autoping = autoping self._heartbeat = heartbeat - self._heartbeat_cb = None + self._heartbeat_cb: Optional[asyncio.TimerHandle] = None if heartbeat is not None: self._pong_heartbeat = heartbeat / 2.0 - self._pong_response_cb = None + self._pong_response_cb: Optional[asyncio.TimerHandle] = None self._compress = compress self._max_msg_size = max_msg_size @@ -126,16 +126,18 @@ def _reset_heartbeat(self) -> None: self._cancel_heartbeat() if self._heartbeat is not None: + assert self._loop is not None self._heartbeat_cb = call_later( self._send_heartbeat, self._heartbeat, self._loop ) def _send_heartbeat(self) -> None: if self._heartbeat is not None and not self._closed: + assert self._loop is not None # fire-and-forget a task is not perfect but maybe ok for # sending ping. Otherwise we need a long-living heartbeat # task in the class. - self._loop.create_task(self._writer.ping()) # type: ignore + self._loop.create_task(self._writer.ping()) # type: ignore[union-attr] if self._pong_response_cb is not None: self._pong_response_cb.cancel() @@ -217,9 +219,9 @@ def _handshake( accept_val = base64.b64encode( hashlib.sha1(key.encode() + WS_KEY).digest() ).decode() - response_headers = CIMultiDict( # type: ignore + response_headers = CIMultiDict( # type: ignore[var-annotated] { - hdrs.UPGRADE: "websocket", # type: ignore + hdrs.UPGRADE: "websocket", # type: ignore[arg-type] hdrs.CONNECTION: "upgrade", hdrs.SEC_WEBSOCKET_ACCEPT: accept_val, } @@ -240,7 +242,12 @@ def _handshake( if protocol: response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol - return (response_headers, protocol, compress, notakeover) # type: ignore + return ( + response_headers, + protocol, + compress, + notakeover, + ) # type: ignore[return-value] def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]: self._loop = request._loop @@ -339,7 +346,7 @@ async def send_json( ) -> None: await self.send_str(dumps(data), compress=compress) - async def write_eof(self) -> None: # type: ignore + async def write_eof(self) -> None: # type: ignore[override] if self._eof_sent: return if self._payload_writer is None: @@ -472,13 +479,13 @@ async def receive_str(self, *, timeout: Optional[float] = None) -> str: msg.type, msg.data ) ) - return msg.data + return cast(str, msg.data) async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes: msg = await self.receive(timeout) if msg.type != WSMsgType.BINARY: raise TypeError(f"Received message {msg.type}:{msg.data!r} is not bytes") - return msg.data + return cast(bytes, msg.data) async def receive_json( self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None diff --git a/aiohttp/worker.py b/aiohttp/worker.py index ba4d805d3e7..a249b9b0af5 100644 --- a/aiohttp/worker.py +++ b/aiohttp/worker.py @@ -22,14 +22,14 @@ SSLContext = ssl.SSLContext except ImportError: # pragma: no cover - ssl = None # type: ignore - SSLContext = object # type: ignore + ssl = None # type: ignore[assignment] + SSLContext = object # type: ignore[misc,assignment] __all__ = ("GunicornWebWorker", "GunicornUVLoopWebWorker", "GunicornTokioWebWorker") -class GunicornWebWorker(base.Worker): +class GunicornWebWorker(base.Worker): # type: ignore[misc] DEFAULT_AIOHTTP_LOG_FORMAT = AccessLogger.LOG_FORMAT DEFAULT_GUNICORN_LOG_FORMAT = GunicornAccessLogFormat.default @@ -97,7 +97,7 @@ async def _run(self) -> None: # If our parent changed then we shut down. pid = os.getpid() try: - while self.alive: # type: ignore + while self.alive: # type: ignore[has-type] self.notify() cnt = server.requests_count diff --git a/requirements/lint.txt b/requirements/lint.txt index 74b7aaa5504..c926c1e86de 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -4,3 +4,4 @@ flake8-pyi==20.10.0 isort==5.7.0 mypy==0.790; implementation_name=="cpython" pre-commit==2.9.3 +pytest==6.1.2 diff --git a/tests/test_payload.py b/tests/test_payload.py index 5f4fc998835..d3ca69861e0 100644 --- a/tests/test_payload.py +++ b/tests/test_payload.py @@ -34,7 +34,9 @@ class TestProvider: pass with pytest.raises(ValueError): - payload.register_payload(Payload, TestProvider, order=object()) # type: ignore + payload.register_payload( + Payload, TestProvider, order=object() # type: ignore[arg-type] + ) def test_payload_ctor() -> None: @@ -63,7 +65,7 @@ def test_bytes_payload_explicit_content_type() -> None: def test_bytes_payload_bad_type() -> None: with pytest.raises(TypeError): - payload.BytesPayload(object()) # type: ignore + payload.BytesPayload(object()) # type: ignore[arg-type] def test_bytes_payload_memoryview_correct_size() -> None: @@ -115,4 +117,4 @@ async def gen() -> AsyncIterator[bytes]: def test_async_iterable_payload_not_async_iterable() -> None: with pytest.raises(TypeError): - payload.AsyncIterablePayload(object()) # type: ignore + payload.AsyncIterablePayload(object()) # type: ignore[arg-type] diff --git a/tests/test_web_sendfile.py b/tests/test_web_sendfile.py index 5eafc3d2eb5..aa366c22891 100644 --- a/tests/test_web_sendfile.py +++ b/tests/test_web_sendfile.py @@ -23,7 +23,7 @@ def test_using_gzip_if_header_present_and_file_available(loop: Any) -> None: filepath.with_name.return_value = gz_filepath file_sender = FileResponse(filepath) - file_sender._sendfile = make_mocked_coro(None) # type: ignore + file_sender._sendfile = make_mocked_coro(None) # type: ignore[assignment] loop.run_until_complete(file_sender.prepare(request)) @@ -46,7 +46,7 @@ def test_gzip_if_header_not_present_and_file_available(loop: Any) -> None: filepath.stat.st_size = 1024 file_sender = FileResponse(filepath) - file_sender._sendfile = make_mocked_coro(None) # type: ignore + file_sender._sendfile = make_mocked_coro(None) # type: ignore[assignment] loop.run_until_complete(file_sender.prepare(request)) @@ -69,7 +69,7 @@ def test_gzip_if_header_not_present_and_file_not_available(loop: Any) -> None: filepath.stat.st_size = 1024 file_sender = FileResponse(filepath) - file_sender._sendfile = make_mocked_coro(None) # type: ignore + file_sender._sendfile = make_mocked_coro(None) # type: ignore[assignment] loop.run_until_complete(file_sender.prepare(request)) @@ -94,7 +94,7 @@ def test_gzip_if_header_present_and_file_not_available(loop: Any) -> None: filepath.stat.st_size = 1024 file_sender = FileResponse(filepath) - file_sender._sendfile = make_mocked_coro(None) # type: ignore + file_sender._sendfile = make_mocked_coro(None) # type: ignore[assignment] loop.run_until_complete(file_sender.prepare(request)) @@ -112,7 +112,7 @@ def test_status_controlled_by_user(loop: Any) -> None: filepath.stat.st_size = 1024 file_sender = FileResponse(filepath, status=203) - file_sender._sendfile = make_mocked_coro(None) # type: ignore + file_sender._sendfile = make_mocked_coro(None) # type: ignore[assignment] loop.run_until_complete(file_sender.prepare(request))