Source code for faust.web.views

"""Class-based views."""
import inspect
from typing import (
    Any,
    Awaitable,
    Callable,
    List,
    Mapping,
    Optional,
    Type,
    cast,
)

from faust.types import AppT
from faust.types.web import PageArg, ViewGetHandler

from .base import Request, Response, Web

__all__ = ['View', 'Site']

CommandDecorator = Callable[[PageArg], Type['Site']]

_bytes = bytes   # need alias for method named `bytes`


[docs]class View: """View (HTTP endpoint).""" methods: Mapping[str, Callable[[Request], Awaitable]]
[docs] @classmethod def from_handler(cls, fun: ViewGetHandler) -> Type['View']: if not callable(fun): raise TypeError(f'View handler must be callable, not {fun!r}') return type(fun.__name__, (cls,), { 'get': fun, '__doc__': fun.__doc__, '__module__': fun.__module__, })
def __init__(self, app: AppT, web: Web) -> None: self.app = app self.web = web self.methods = { 'get': self.get, 'post': self.post, 'patch': self.patch, 'delete': self.delete, 'put': self.put, } async def __call__(self, request: Any) -> Any: return await self.dispatch(request)
[docs] async def dispatch(self, request: Any) -> Any: method = request.method.lower() return await self.methods[method](cast(Request, request))
[docs] async def get(self, request: Request) -> Any: ...
[docs] async def post(self, request: Request) -> Any: ...
[docs] async def put(self, request: Request) -> Any: ...
[docs] async def patch(self, request: Request) -> Any: ...
[docs] async def delete(self, request: Request) -> Any: ...
[docs] def text(self, value: str, *, content_type: str = None, status: int = 200) -> Response: return self.web.text(value, content_type=content_type, status=status)
[docs] def html(self, value: str, *, status: int = 200) -> Response: return self.web.html(value, status=status)
[docs] def json(self, value: Any, *, status: int = 200) -> Response: return self.web.json(value, status=status)
[docs] def bytes(self, value: _bytes, *, content_type: str = None, status: int = 200) -> Response: return self.web.bytes(value, content_type=content_type, status=status)
[docs] def route(self, pattern: str, handler: Callable) -> Any: self.web.route(pattern, handler) return handler
[docs] def notfound(self, reason: str = 'Not Found', **kwargs: Any) -> Response: return self.error(404, reason, **kwargs)
[docs] def error(self, status: int, reason: str, **kwargs: Any) -> Response: return self.json({'error': reason, **kwargs}, status=status)
[docs]class Site: """Collection of HTTP endpoints (views).""" views: Mapping[str, Type[View]] def __init__(self, app: AppT) -> None: self.app = app
[docs] def enable(self, web: Web, *, prefix: str = '') -> List[View]: return [ self._route(web, view_cls, prefix + pattern) for pattern, view_cls in self.views.items() ]
def _route(self, web: Web, view_cls: Type[View], pattern: str) -> View: view = view_cls(self.app, web) web.route(pattern, view) return view
[docs] @classmethod def from_handler(cls, path: str, *, base: Type[View] = None) -> CommandDecorator: view_base: Type[View] = base if base is not None else View def _decorator(fun: PageArg) -> Type[Site]: view: Optional[Type[View]] = None if inspect.isclass(fun): view = cast(Type[View], fun) if not issubclass(view, View): raise TypeError( 'When decorating class, it must be subclass of View') if view is None: view = view_base.from_handler(cast(ViewGetHandler, fun)) return type('Site', (cls,), { 'views': { path: view, }, '__module__': fun.__module__, }) return _decorator