From 6db8b67657b637249cd5a0aba67edfdfb00ea11f Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Sun, 18 Oct 2020 23:25:14 +0300 Subject: [PATCH] Better typing (#5078) --- aiohttp/client.py | 4 +-- aiohttp/client_proto.py | 2 +- aiohttp/client_reqrep.py | 2 +- aiohttp/tracing.py | 4 +-- aiohttp/web_app.py | 2 +- aiohttp/web_response.py | 8 ++--- aiohttp/web_routedef.py | 2 +- aiohttp/web_runner.py | 2 +- aiohttp/web_urldispatcher.py | 59 +++++++++++++++++++++++++----------- aiohttp/worker.py | 5 ++- setup.cfg | 2 +- 11 files changed, 60 insertions(+), 32 deletions(-) diff --git a/aiohttp/client.py b/aiohttp/client.py index d19855b350f..248df1499f7 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -299,7 +299,7 @@ async def _request( data: Any=None, json: Any=None, cookies: Optional[LooseCookies]=None, - headers: LooseHeaders=None, + headers: Optional[LooseHeaders]=None, skip_auto_headers: Optional[Iterable[str]]=None, auth: Optional[BasicAuth]=None, allow_redirects: bool=True, @@ -1089,7 +1089,7 @@ def request( params: Optional[Mapping[str, str]]=None, data: Any=None, json: Any=None, - headers: LooseHeaders=None, + headers: Optional[LooseHeaders]=None, skip_auto_headers: Optional[Iterable[str]]=None, auth: Optional[BasicAuth]=None, allow_redirects: bool=True, diff --git a/aiohttp/client_proto.py b/aiohttp/client_proto.py index f6d191725ef..1f1a65f88c7 100644 --- a/aiohttp/client_proto.py +++ b/aiohttp/client_proto.py @@ -140,7 +140,7 @@ def set_parser(self, parser: Any, payload: Any) -> None: data, self._tail = self._tail, b'' self.data_received(data) - def set_response_params(self, *, timer: BaseTimerContext=None, + def set_response_params(self, *, timer: Optional[BaseTimerContext]=None, skip_payload: bool=False, read_until_eof: bool=False, auto_decompress: bool=True, diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index a4aa3492a46..42142bf4718 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -978,7 +978,7 @@ async def text(self, return self._body.decode(encoding, errors=errors) # type: ignore - async def json(self, *, encoding: str=None, + async def json(self, *, encoding: Optional[str]=None, loads: JSONDecoder=DEFAULT_JSON_DECODER, content_type: Optional[str]='application/json') -> Any: """Read and decodes JSON response.""" diff --git a/aiohttp/tracing.py b/aiohttp/tracing.py index d78334dcf4f..2a9b2299202 100644 --- a/aiohttp/tracing.py +++ b/aiohttp/tracing.py @@ -1,5 +1,5 @@ from types import SimpleNamespace -from typing import TYPE_CHECKING, Awaitable, Type, TypeVar +from typing import TYPE_CHECKING, Awaitable, Optional, Type, TypeVar import attr from multidict import CIMultiDict # noqa @@ -92,7 +92,7 @@ def __init__( def trace_config_ctx( self, - trace_request_ctx: SimpleNamespace=None + trace_request_ctx: Optional[SimpleNamespace]=None ) -> SimpleNamespace: # noqa """ Return a new trace_config_ctx instance """ return self._trace_config_ctx_factory( diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index fe0f343746f..65d359ec3e4 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -77,7 +77,7 @@ class Application(MutableMapping[str, Any]): def __init__(self, *, logger: logging.Logger=web_logger, middlewares: Iterable[_Middleware]=(), - handler_args: Mapping[str, Any]=None, + handler_args: Optional[Mapping[str, Any]]=None, client_max_size: int=1024**2, debug: Any=... # mypy doesn't support ellipsis ) -> None: diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 57fc6342720..88b1bd355b7 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -507,7 +507,7 @@ def __init__(self, *, content_type: Optional[str]=None, charset: Optional[str]=None, zlib_executor_size: Optional[int]=None, - zlib_executor: Executor=None) -> None: + zlib_executor: Optional[Executor]=None) -> None: if body is not None and text is not None: raise ValueError("body and text are not allowed together") @@ -716,11 +716,11 @@ async def _do_start_compression(self, coding: ContentCoding) -> None: def json_response(data: Any=sentinel, *, - text: str=None, - body: bytes=None, + text: Optional[str]=None, + body: Optional[bytes]=None, status: int=200, reason: Optional[str]=None, - headers: LooseHeaders=None, + headers: Optional[LooseHeaders]=None, content_type: str='application/json', dumps: JSONEncoder=json.dumps) -> Response: if data is not sentinel: diff --git a/aiohttp/web_routedef.py b/aiohttp/web_routedef.py index e09b3c86763..972e940a80a 100644 --- a/aiohttp/web_routedef.py +++ b/aiohttp/web_routedef.py @@ -85,7 +85,7 @@ def __repr__(self) -> str: def register(self, router: UrlDispatcher) -> List[AbstractRoute]: resource = router.add_static(self.prefix, self.path, **self.kwargs) routes = resource.get_info().get('routes', {}) - return routes.values() + return list(routes.values()) def route(method: str, path: str, handler: _HandlerType, diff --git a/aiohttp/web_runner.py b/aiohttp/web_runner.py index 0fed3b86738..da5918051de 100644 --- a/aiohttp/web_runner.py +++ b/aiohttp/web_runner.py @@ -78,7 +78,7 @@ class TCPSite(BaseSite): __slots__ = ('_host', '_port', '_reuse_address', '_reuse_port') def __init__(self, runner: 'BaseRunner', - host: str=None, port: int=None, *, + host: Optional[str]=None, port: Optional[int]=None, *, shutdown_timeout: float=60.0, ssl_context: Optional[SSLContext]=None, backlog: int=128, reuse_address: Optional[bool]=None, diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index 957715d8812..8494e7aa52f 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -20,6 +20,7 @@ List, Mapping, Optional, + Pattern, Set, Sized, Tuple, @@ -28,6 +29,7 @@ cast, ) +from typing_extensions import TypedDict from yarl import URL from . import hdrs @@ -69,6 +71,25 @@ _Resolve = Tuple[Optional[AbstractMatchInfo], Set[str]] +class _InfoDict(TypedDict, total=False): + path: str + + formatter: str + pattern: Pattern[str] + + directory: Path + prefix: str + routes: Mapping[str, 'AbstractRoute'] + + app: 'Application' + + domain: str + + rule: 'AbstractRuleMatching' + + http_exception: HTTPException + + class AbstractResource(Sized, Iterable['AbstractRoute']): def __init__(self, *, name: Optional[str]=None) -> None: @@ -106,7 +127,7 @@ def add_prefix(self, prefix: str) -> None: """ @abc.abstractmethod - def get_info(self) -> Dict[str, Any]: + def get_info(self) -> _InfoDict: """Return a dict with additional info useful for introspection""" def freeze(self) -> None: @@ -121,8 +142,8 @@ class AbstractRoute(abc.ABC): def __init__(self, method: str, handler: Union[_WebHandler, Type[AbstractView]], *, - expect_handler: _ExpectHandler=None, - resource: AbstractResource=None) -> None: + expect_handler: Optional[_ExpectHandler]=None, + resource: Optional[AbstractResource]=None) -> None: if expect_handler is None: expect_handler = _default_expect_handler @@ -165,7 +186,7 @@ def resource(self) -> Optional[AbstractResource]: return self._resource @abc.abstractmethod - def get_info(self) -> Dict[str, Any]: + def get_info(self) -> _InfoDict: """Return a dict with additional info useful for introspection""" @abc.abstractmethod # pragma: no branch @@ -201,7 +222,7 @@ def expect_handler(self) -> _ExpectHandler: def http_exception(self) -> Optional[HTTPException]: return None - def get_info(self) -> Dict[str, str]: + def get_info(self) -> _InfoDict: # type: ignore return self._route.get_info() @property @@ -361,7 +382,7 @@ def _match(self, path: str) -> Optional[Dict[str, str]]: def raw_match(self, path: str) -> bool: return self._path == path - def get_info(self) -> Dict[str, Any]: + def get_info(self) -> _InfoDict: return {'path': self._path} def url_for(self) -> URL: # type: ignore @@ -436,7 +457,7 @@ def _match(self, path: str) -> Optional[Dict[str, str]]: def raw_match(self, path: str) -> bool: return self._formatter == path - def get_info(self) -> Dict[str, Any]: + def get_info(self) -> _InfoDict: return {'formatter': self._formatter, 'pattern': self._pattern} @@ -549,7 +570,7 @@ def _get_file_hash(byte_array: bytes) -> str: b64 = base64.urlsafe_b64encode(m.digest()) return b64.decode('ascii') - def get_info(self) -> Dict[str, Any]: + def get_info(self) -> _InfoDict: return {'directory': self._directory, 'prefix': self._prefix, 'routes': self._routes} @@ -677,7 +698,7 @@ def url_for(self, *args: str, **kwargs: str) -> URL: raise RuntimeError(".url_for() is not supported " "by sub-application root") - def get_info(self) -> Dict[str, Any]: + def get_info(self) -> _InfoDict: return {'app': self._app, 'prefix': self._prefix} @@ -710,7 +731,7 @@ async def match(self, request: Request) -> bool: """Return bool if the request satisfies the criteria""" @abc.abstractmethod # pragma: no branch - def get_info(self) -> Dict[str, Any]: + def get_info(self) -> _InfoDict: """Return a dict with additional info useful for introspection""" @property @@ -756,7 +777,7 @@ async def match(self, request: Request) -> bool: def match_domain(self, host: str) -> bool: return host.lower() == self._domain - def get_info(self) -> Dict[str, Any]: + def get_info(self) -> _InfoDict: return {'domain': self._domain} @@ -788,7 +809,7 @@ def __init__(self, rule: AbstractRuleMatching, app: 'Application') -> None: def canonical(self) -> str: return self._rule.canonical - def get_info(self) -> Dict[str, Any]: + def get_info(self) -> _InfoDict: return {'app': self._app, 'rule': self._rule} @@ -825,14 +846,18 @@ def __repr__(self) -> str: @property def name(self) -> Optional[str]: - return self._resource.name # type: ignore + if self._resource is None: + return None + return self._resource.name def url_for(self, *args: str, **kwargs: str) -> URL: """Construct url for route with additional params.""" - return self._resource.url_for(*args, **kwargs) # type: ignore + assert self._resource is not None + return self._resource.url_for(*args, **kwargs) - def get_info(self) -> Dict[str, Any]: - return self._resource.get_info() # type: ignore + def get_info(self) -> _InfoDict: + assert self._resource is not None + return self._resource.get_info() class SystemRoute(AbstractRoute): @@ -848,7 +873,7 @@ def url_for(self, *args: str, **kwargs: str) -> URL: def name(self) -> Optional[str]: return None - def get_info(self) -> Dict[str, Any]: + def get_info(self) -> _InfoDict: return {'http_exception': self._http_exception} async def _handle(self, request: Request) -> StreamResponse: diff --git a/aiohttp/worker.py b/aiohttp/worker.py index 49a26650193..088e6848f8b 100644 --- a/aiohttp/worker.py +++ b/aiohttp/worker.py @@ -121,7 +121,10 @@ def _wait_next_notify(self) -> 'asyncio.Future[bool]': return waiter - def _notify_waiter_done(self, waiter: 'asyncio.Future[bool]'=None) -> None: + def _notify_waiter_done( + self, + waiter: Optional['asyncio.Future[bool]']=None + ) -> None: if waiter is None: waiter = self._notify_waiter if waiter is not None: diff --git a/setup.cfg b/setup.cfg index e25cfa798ff..e75519c75c6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -49,6 +49,7 @@ xfail_strict = true follow_imports = silent strict_optional = True warn_redundant_casts = True +warn_unused_ignores = True # uncomment next lines # to enable strict mypy mode @@ -56,7 +57,6 @@ warn_redundant_casts = True check_untyped_defs = True disallow_any_generics = True disallow_untyped_defs = True -warn_unused_ignores = True [mypy-pytest]