From 2e3171f85825929708c384e2e1e22189d45d24c2 Mon Sep 17 00:00:00 2001 From: Igor Mozharovsky Date: Thu, 6 Sep 2018 11:29:02 +0300 Subject: [PATCH] Add types for Application and Response --- CHANGES/1749.feature | 1 + CONTRIBUTORS.txt | 1 + aiohttp/web_app.py | 46 +++++++++++++++++++++++++++++------------ aiohttp/web_response.py | 26 ++++++++++++++++------- 4 files changed, 54 insertions(+), 20 deletions(-) create mode 100644 CHANGES/1749.feature diff --git a/CHANGES/1749.feature b/CHANGES/1749.feature new file mode 100644 index 00000000000..4f975783e15 --- /dev/null +++ b/CHANGES/1749.feature @@ -0,0 +1 @@ +Add type hints to Application and Response diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 3c5ebb9fe50..1e0e71b3716 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -93,6 +93,7 @@ Hugo Herter Hynek Schlawack Igor Alexandrov Igor Davydenko +Igor Mozharovsky Igor Pavlov Ingmar Steen Jacob Champion diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index 1ad0ecb8651..e0c8f595fd5 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -1,8 +1,10 @@ import asyncio +import logging import warnings from collections import MutableMapping from functools import partial -from typing import TYPE_CHECKING, Awaitable, Callable +from typing import (TYPE_CHECKING, Any, Awaitable, Callable, List, Mapping, + Optional, Sequence, Tuple, Union) from . import hdrs from .abc import AbstractAccessLogger, AbstractMatchInfo, AbstractRouter @@ -24,10 +26,23 @@ _AppSignal = Signal[Callable[['Application'], Awaitable[None]]] _RespPrepareSignal = Signal[Callable[[Request, StreamResponse], Awaitable[None]]] + _Handler = Callable[[Request], Awaitable[StreamResponse]] + _Middleware = Union[Callable[[Request, _Handler], + Awaitable[StreamResponse]], + Callable[['Application', _Handler], # old-style + Awaitable[_Handler]]] + _Middlewares = FrozenList[_Middleware] + _MiddlewaresHandlers = Optional[Sequence[Tuple[_Middleware, bool]]] + _Subapps = List['Application'] else: # No type checker mode, skip types _AppSignal = Signal _RespPrepareSignal = Signal + _Handler = Callable + _Middleware = Callable + _Middlewares = FrozenList + _MiddlewaresHandlers = Optional[Sequence] + _Subapps = List class Application(MutableMapping): @@ -39,13 +54,14 @@ class Application(MutableMapping): '_on_cleanup', '_client_max_size', '_cleanup_ctx']) def __init__(self, *, - logger=web_logger, - router=None, - middlewares=(), - handler_args=None, - client_max_size=1024**2, - loop=None, - debug=...): + logger: logging.Logger=web_logger, + router: Optional[UrlDispatcher]=None, + middlewares: Sequence[_Middleware]=(), + handler_args: Mapping[str, Any]=None, + client_max_size: int=1024**2, + loop: Optional[asyncio.AbstractEventLoop]=None, + debug=..., # type: ignore + ) -> None: if router is None: router = UrlDispatcher() else: @@ -63,12 +79,16 @@ def __init__(self, *, self._handler_args = handler_args self.logger = logger - self._middlewares = FrozenList(middlewares) - self._middlewares_handlers = None # initialized on freezing - self._run_middlewares = None # initialized on freezing - self._state = {} + self._middlewares = FrozenList(middlewares) # type: _Middlewares + + # initialized on freezing + self._middlewares_handlers = None # type: _MiddlewaresHandlers + # initialized on freezing + self._run_middlewares = None # type: Optional[bool] + + self._state = {} # type: Mapping self._frozen = False - self._subapps = [] + self._subapps = [] # type: _Subapps self._on_response_prepare = Signal(self) # type: _RespPrepareSignal self._on_startup = Signal(self) # type: _AppSignal diff --git a/aiohttp/web_response.py b/aiohttp/web_response.py index 89835c63342..c6e9afc71d0 100644 --- a/aiohttp/web_response.py +++ b/aiohttp/web_response.py @@ -8,12 +8,14 @@ import zlib from email.utils import parsedate from http.cookies import SimpleCookie +from typing import TYPE_CHECKING, Any, Mapping, Optional, cast # noqa from multidict import CIMultiDict, CIMultiDictProxy from . import hdrs, payload from .helpers import HeadersMixin, rfc822_formatted_time, sentinel from .http import RESPONSES, SERVER_SOFTWARE, HttpVersion10, HttpVersion11 +from .typedefs import LooseHeaders __all__ = ('ContentCoding', 'StreamResponse', 'Response', 'json_response') @@ -38,7 +40,10 @@ class StreamResponse(collections.MutableMapping, HeadersMixin): _length_check = True - def __init__(self, *, status=200, reason=None, headers=None): + def __init__(self, *, + status: int=200, + reason: Optional[str]=None, + headers: Optional[LooseHeaders]=None) -> None: self._body = None self._keep_alive = None self._chunked = False @@ -50,12 +55,12 @@ def __init__(self, *, status=200, reason=None, headers=None): self._payload_writer = None self._eof_sent = False self._body_length = 0 - self._state = {} + self._state = {} # type: Mapping if headers is not None: - self._headers = CIMultiDict(headers) + self._headers = CIMultiDict(headers) # type: CIMultiDict else: - self._headers = CIMultiDict() + self._headers = CIMultiDict() # type: CIMultiDict self.set_status(status, reason) @@ -438,9 +443,14 @@ def __eq__(self, other): class Response(StreamResponse): - def __init__(self, *, body=None, status=200, - reason=None, text=None, headers=None, content_type=None, - charset=None): + def __init__(self, *, + body: Any=None, + status: int=200, + reason: Optional[str]=None, + text: Optional[str]=None, + headers: Optional[LooseHeaders]=None, + content_type: Optional[str]=None, + charset: Optional[str]=None) -> None: if body is not None and text is not None: raise ValueError("body and text are not allowed together") @@ -448,6 +458,8 @@ def __init__(self, *, body=None, status=200, headers = CIMultiDict() elif not isinstance(headers, (CIMultiDict, CIMultiDictProxy)): headers = CIMultiDict(headers) + else: + headers = cast(CIMultiDict, headers) if content_type is not None and "charset" in content_type: raise ValueError("charset must not be in content_type "