diff --git a/CHANGES/4686.feature b/CHANGES/4686.feature new file mode 100644 index 0000000000..1b74265fb9 --- /dev/null +++ b/CHANGES/4686.feature @@ -0,0 +1 @@ +Add a request handler type alias ``aiohttp.typedefs.Handler``. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index f9544ce9b0..502efb67a2 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -47,6 +47,7 @@ Anton Zhdan-Pushkin Arseny Timoniq Artem Yushkovskiy Arthur Darcet +Austin Scola Ben Bader Ben Timby Benedikt Reinartz diff --git a/aiohttp/typedefs.py b/aiohttp/typedefs.py index ab2d7035f2..1b13a4dbd0 100644 --- a/aiohttp/typedefs.py +++ b/aiohttp/typedefs.py @@ -1,6 +1,15 @@ import json import os -from typing import TYPE_CHECKING, Any, Callable, Iterable, Mapping, Tuple, Union +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Iterable, + Mapping, + Tuple, + Union, +) from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy, istr from yarl import URL @@ -14,6 +23,8 @@ _MultiDict = MultiDict[str] _MultiDictProxy = MultiDictProxy[str] from http.cookies import BaseCookie, Morsel + + from .web import Request, StreamResponse else: _CIMultiDict = CIMultiDict _CIMultiDictProxy = CIMultiDictProxy @@ -37,5 +48,6 @@ "BaseCookie[str]", ] +Handler = Callable[["Request"], Awaitable["StreamResponse"]] PathLike = Union[str, "os.PathLike[str]"] diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py index 15d3c430e5..d78d603d38 100644 --- a/aiohttp/web_app.py +++ b/aiohttp/web_app.py @@ -46,10 +46,11 @@ if TYPE_CHECKING: # pragma: no cover + from .typedefs import Handler + _AppSignal = Signal[Callable[["Application"], Awaitable[None]]] _RespPrepareSignal = Signal[Callable[[Request, StreamResponse], Awaitable[None]]] - _Handler = Callable[[Request], Awaitable[StreamResponse]] - _Middleware = Callable[[Request, _Handler], Awaitable[StreamResponse]] + _Middleware = Callable[[Request, Handler], Awaitable[StreamResponse]] _Middlewares = FrozenList[_Middleware] _MiddlewaresHandlers = Sequence[_Middleware] _Subapps = List["Application"] diff --git a/aiohttp/web_middlewares.py b/aiohttp/web_middlewares.py index 3b040e8499..4d28ff7630 100644 --- a/aiohttp/web_middlewares.py +++ b/aiohttp/web_middlewares.py @@ -2,6 +2,7 @@ import warnings from typing import TYPE_CHECKING, Awaitable, Callable, Tuple, Type, TypeVar +from .typedefs import Handler from .web_exceptions import HTTPMove, HTTPPermanentRedirect from .web_request import Request from .web_response import StreamResponse @@ -41,8 +42,7 @@ def middleware(f: _Func) -> _Func: return f -_Handler = Callable[[Request], Awaitable[StreamResponse]] -_Middleware = Callable[[Request, _Handler], Awaitable[StreamResponse]] +_Middleware = Callable[[Request, Handler], Awaitable[StreamResponse]] def normalize_path_middleware( @@ -85,7 +85,7 @@ def normalize_path_middleware( correct_configuration = not (append_slash and remove_slash) assert correct_configuration, "Cannot both remove and append slash" - async def impl(request: Request, handler: _Handler) -> StreamResponse: + async def impl(request: Request, handler: Handler) -> StreamResponse: if isinstance(request.match_info.route, SystemRoute): paths_to_check = [] if "?" in request.raw_path: @@ -119,7 +119,7 @@ async def impl(request: Request, handler: _Handler) -> StreamResponse: def _fix_request_current_app(app: "Application") -> _Middleware: - async def impl(request: Request, handler: _Handler) -> StreamResponse: + async def impl(request: Request, handler: Handler) -> StreamResponse: with request.match_info.set_current_app(app): return await handler(request) diff --git a/aiohttp/web_routedef.py b/aiohttp/web_routedef.py index 3ecbd943fc..787d9cbdec 100644 --- a/aiohttp/web_routedef.py +++ b/aiohttp/web_routedef.py @@ -4,7 +4,6 @@ from typing import ( TYPE_CHECKING, Any, - Awaitable, Callable, Dict, Iterator, @@ -18,7 +17,7 @@ from . import hdrs from .abc import AbstractView -from .typedefs import PathLike +from .typedefs import Handler, PathLike if TYPE_CHECKING: # pragma: no cover from .web_request import Request @@ -52,8 +51,7 @@ def register(self, router: UrlDispatcher) -> List[AbstractRoute]: pass # pragma: no cover -_SimpleHandler = Callable[[Request], Awaitable[StreamResponse]] -_HandlerType = Union[Type[AbstractView], _SimpleHandler] +_HandlerType = Union[Type[AbstractView], Handler] @dataclasses.dataclass(frozen=True, repr=False) diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py index a229cd7e4e..3e9a2c2239 100644 --- a/aiohttp/web_urldispatcher.py +++ b/aiohttp/web_urldispatcher.py @@ -36,7 +36,7 @@ from .abc import AbstractMatchInfo, AbstractRouter, AbstractView from .helpers import DEBUG, iscoroutinefunction from .http import HttpVersion11 -from .typedefs import PathLike +from .typedefs import Handler, PathLike from .web_exceptions import ( HTTPException, HTTPExpectationFailed, @@ -81,7 +81,6 @@ PATH_SEP: Final[str] = re.escape("/") -_WebHandler = Callable[[Request], Awaitable[StreamResponse]] _ExpectHandler = Callable[[Request], Awaitable[None]] _Resolve = Tuple[Optional[AbstractMatchInfo], Set[str]] @@ -156,7 +155,7 @@ class AbstractRoute(abc.ABC): def __init__( self, method: str, - handler: Union[_WebHandler, Type[AbstractView]], + handler: Union[Handler, Type[AbstractView]], *, expect_handler: Optional[_ExpectHandler] = None, resource: Optional[AbstractResource] = None, @@ -193,7 +192,7 @@ def method(self) -> str: return self._method @property - def handler(self) -> _WebHandler: + def handler(self) -> Handler: return self._handler @property @@ -226,7 +225,7 @@ def __init__(self, match_dict: Dict[str, str], route: AbstractRoute): self._frozen = False @property - def handler(self) -> _WebHandler: + def handler(self) -> Handler: return self._route.handler @property @@ -321,7 +320,7 @@ def __init__(self, *, name: Optional[str] = None) -> None: def add_route( self, method: str, - handler: Union[Type[AbstractView], _WebHandler], + handler: Union[Type[AbstractView], Handler], *, expect_handler: Optional[_ExpectHandler] = None, ) -> "ResourceRoute": @@ -606,7 +605,7 @@ def get_info(self) -> _InfoDict: "routes": self._routes, } - def set_options_route(self, handler: _WebHandler) -> None: + def set_options_route(self, handler: Handler) -> None: if "OPTIONS" in self._routes: raise RuntimeError("OPTIONS route was set already") self._routes["OPTIONS"] = ResourceRoute( @@ -863,7 +862,7 @@ class ResourceRoute(AbstractRoute): def __init__( self, method: str, - handler: Union[_WebHandler, Type[AbstractView]], + handler: Union[Handler, Type[AbstractView]], resource: AbstractResource, *, expect_handler: Optional[_ExpectHandler] = None, @@ -1073,7 +1072,7 @@ def add_route( self, method: str, path: str, - handler: Union[_WebHandler, Type[AbstractView]], + handler: Union[Handler, Type[AbstractView]], *, name: Optional[str] = None, expect_handler: Optional[_ExpectHandler] = None, @@ -1115,15 +1114,13 @@ def add_static( self.register_resource(resource) return resource - def add_head(self, path: str, handler: _WebHandler, **kwargs: Any) -> AbstractRoute: + def add_head(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: """ Shortcut for add_route with method HEAD """ return self.add_route(hdrs.METH_HEAD, path, handler, **kwargs) - def add_options( - self, path: str, handler: _WebHandler, **kwargs: Any - ) -> AbstractRoute: + def add_options(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: """ Shortcut for add_route with method OPTIONS """ @@ -1132,7 +1129,7 @@ def add_options( def add_get( self, path: str, - handler: _WebHandler, + handler: Handler, *, name: Optional[str] = None, allow_head: bool = True, @@ -1147,29 +1144,25 @@ def add_get( resource.add_route(hdrs.METH_HEAD, handler, **kwargs) return resource.add_route(hdrs.METH_GET, handler, **kwargs) - def add_post(self, path: str, handler: _WebHandler, **kwargs: Any) -> AbstractRoute: + def add_post(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: """ Shortcut for add_route with method POST """ return self.add_route(hdrs.METH_POST, path, handler, **kwargs) - def add_put(self, path: str, handler: _WebHandler, **kwargs: Any) -> AbstractRoute: + def add_put(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: """ Shortcut for add_route with method PUT """ return self.add_route(hdrs.METH_PUT, path, handler, **kwargs) - def add_patch( - self, path: str, handler: _WebHandler, **kwargs: Any - ) -> AbstractRoute: + def add_patch(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: """ Shortcut for add_route with method PATCH """ return self.add_route(hdrs.METH_PATCH, path, handler, **kwargs) - def add_delete( - self, path: str, handler: _WebHandler, **kwargs: Any - ) -> AbstractRoute: + def add_delete(self, path: str, handler: Handler, **kwargs: Any) -> AbstractRoute: """ Shortcut for add_route with method DELETE """ diff --git a/examples/web_rewrite_headers_middleware.py b/examples/web_rewrite_headers_middleware.py index 7fc569bce7..149dc28285 100755 --- a/examples/web_rewrite_headers_middleware.py +++ b/examples/web_rewrite_headers_middleware.py @@ -2,18 +2,15 @@ """ Example for rewriting response headers by middleware. """ -from typing import Awaitable, Callable - from aiohttp import web - -_WebHandler = Callable[[web.Request], Awaitable[web.StreamResponse]] +from aiohttp.typedefs import Handler async def handler(request: web.Request) -> web.StreamResponse: return web.Response(text="Everything is fine") -async def middleware(request: web.Request, handler: _WebHandler) -> web.StreamResponse: +async def middleware(request: web.Request, handler: Handler) -> web.StreamResponse: try: response = await handler(request) except web.HTTPException as exc: diff --git a/tests/test_web_app.py b/tests/test_web_app.py index 91b4f69274..12c4029379 100644 --- a/tests/test_web_app.py +++ b/tests/test_web_app.py @@ -7,6 +7,7 @@ from aiohttp import log, web from aiohttp.test_utils import make_mocked_coro +from aiohttp.typedefs import Handler async def test_app_ctor() -> None: @@ -137,7 +138,7 @@ def test_app_run_middlewares() -> None: root.freeze() assert root._run_middlewares is False - async def middleware(request, handler): + async def middleware(request, handler: Handler): return await handler(request) root = web.Application(middlewares=[middleware]) diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py index d4be172482..6a3b7a3fee 100644 --- a/tests/test_web_functional.py +++ b/tests/test_web_functional.py @@ -17,6 +17,7 @@ from aiohttp import FormData, HttpVersion10, HttpVersion11, TraceConfig, multipart, web from aiohttp.hdrs import CONTENT_LENGTH, CONTENT_TYPE, TRANSFER_ENCODING from aiohttp.test_utils import make_mocked_coro +from aiohttp.typedefs import Handler try: import ssl @@ -1213,7 +1214,7 @@ async def handler(request): with pytest.warns(DeprecationWarning, match="Middleware decorator is deprecated"): @web.middleware - async def middleware(request, handler): + async def middleware(request, handler: Handler): order.append((1, request.app["name"])) resp = await handler(request) assert 200 == resp.status @@ -1353,7 +1354,7 @@ async def test_subapp_middleware_context( values = [] def show_app_context(appname): - async def middleware(request, handler): + async def middleware(request, handler: Handler): values.append("{}: {}".format(appname, request.app["my_value"])) return await handler(request) diff --git a/tests/test_web_log.py b/tests/test_web_log.py index b834f87310..fa5fb27f74 100644 --- a/tests/test_web_log.py +++ b/tests/test_web_log.py @@ -10,6 +10,7 @@ import aiohttp from aiohttp import web from aiohttp.abc import AbstractAccessLogger, AbstractAsyncAccessLogger +from aiohttp.typedefs import Handler from aiohttp.web_log import AccessLogger from aiohttp.web_response import Response @@ -232,7 +233,7 @@ async def test_contextvars_logger(aiohttp_server: Any, aiohttp_client: Any): async def handler(request): return web.Response() - async def middleware(request, handler): + async def middleware(request, handler: Handler): VAR.set("uuid") return await handler(request) diff --git a/tests/test_web_middleware.py b/tests/test_web_middleware.py index 33343b65de..aa50ec8ad0 100644 --- a/tests/test_web_middleware.py +++ b/tests/test_web_middleware.py @@ -5,13 +5,14 @@ from yarl import URL from aiohttp import web +from aiohttp.typedefs import Handler async def test_middleware_modifies_response(loop: Any, aiohttp_client: Any) -> None: async def handler(request): return web.Response(body=b"OK") - async def middleware(request, handler): + async def middleware(request, handler: Handler): resp = await handler(request) assert 200 == resp.status resp.set_status(201) @@ -32,7 +33,7 @@ async def test_middleware_handles_exception(loop: Any, aiohttp_client: Any) -> N async def handler(request): raise RuntimeError("Error text") - async def middleware(request, handler): + async def middleware(request, handler: Handler): with pytest.raises(RuntimeError) as ctx: await handler(request) return web.Response(status=501, text=str(ctx.value) + "[MIDDLEWARE]") @@ -59,7 +60,7 @@ async def handler2(request): middleware_annotation_seen_values = [] def make_middleware(num): - async def middleware(request, handler): + async def middleware(request, handler: Handler): middleware_annotation_seen_values.append( getattr(handler, "annotation", None) ) @@ -104,7 +105,7 @@ async def handler(request): middleware_annotation_seen_values = [] def make_middleware(num): - async def middleware(request, handler): + async def middleware(request, handler: Handler): annotation = getattr(handler, "annotation", None) if annotation is not None: middleware_annotation_seen_values.append(f"{annotation}/{num}") @@ -418,7 +419,7 @@ async def view_handler(request): with pytest.warns(DeprecationWarning, match="Middleware decorator is deprecated"): @web.middleware - async def middleware(request, handler): + async def middleware(request, handler: Handler): resp = await handler(request) assert 200 == resp.status resp.set_status(201) @@ -439,7 +440,7 @@ async def handler(request): return web.Response(body=b"OK") class Middleware: - async def __call__(self, request, handler): + async def __call__(self, request, handler: Handler): resp = await handler(request) assert 200 == resp.status resp.set_status(201) @@ -464,7 +465,7 @@ async def handler(request): return web.Response(body=b"OK") class Middleware: - async def call(self, request, handler): + async def call(self, request, handler: Handler): resp = await handler(request) assert 200 == resp.status resp.set_status(201)