Source code for asgi_tools.view

from __future__ import annotations

import inspect
from typing import TYPE_CHECKING, Final, Optional

if TYPE_CHECKING:
    from collections.abc import Awaitable

    from http_router.types import TMethods

    from .request import Request
    from .router import Router

HTTP_METHODS: Final = {
    "GET",
    "HEAD",
    "POST",
    "PUT",
    "DELETE",
    "CONNECT",
    "OPTIONS",
    "TRACE",
    "PATCH",
}


[docs]class HTTPView: """Class-based view pattern for handling HTTP method dispatching. .. code-block:: python @app.route('/custom') class CustomEndpoint(HTTPView): async def get(self, request): return 'Hello from GET' async def post(self, request): return 'Hello from POST' # ... async def test_my_endpoint(client): response = await client.get('/custom') assert await response.text() == 'Hello from GET' response = await client.post('/custom') assert await response.text() == 'Hello from POST' response = await client.put('/custom') assert response.status_code == 405 """ def __new__(cls, request: Request, **opts): """Init the class and call it.""" self = super().__new__(cls) return self(request, **opts) @classmethod def __route__(cls, router: Router, *paths: str, methods: Optional[TMethods] = None, **params): """Bind the class view to the given router.""" view_methods = dict(inspect.getmembers(cls, inspect.isfunction)) methods = methods or [m for m in HTTP_METHODS if m.lower() in view_methods] return router.bind(cls, *paths, methods=methods, **params) def __call__(self, request: Request, **opts) -> Awaitable: """Dispatch the given request by HTTP method.""" method = getattr(self, request.method.lower()) return method(request, **opts)