Source code for minos.networks.decorators.callables.handlers

from __future__ import (
    annotations,
)

from asyncio import (
    iscoroutinefunction,
)
from collections.abc import (
    Awaitable,
    Callable,
    Iterable,
)
from functools import (
    partial,
    wraps,
)
from inspect import (
    isawaitable,
)
from typing import (
    TYPE_CHECKING,
    Optional,
    Protocol,
    Type,
    Union,
    runtime_checkable,
)

from cached_property import (
    cached_property,
)

from ...exceptions import (
    MinosMultipleEnrouteDecoratorKindsException,
    NotSatisfiedCheckerException,
)
from ...requests import (
    Request,
    Response,
    ResponseException,
)
from .checkers import (
    CheckerMeta,
)

if TYPE_CHECKING:
    from ..definitions import (
        CheckDecorator,
        EnrouteDecorator,
    )

Handler = Callable[[Request], Union[Optional[Response], Awaitable[Optional[Response]]]]


[docs]@runtime_checkable class HandlerWrapper(Protocol): """Handler Wrapper class.""" meta: HandlerMeta check: Type[CheckDecorator] __call__: Handler
[docs]class HandlerMeta: """Handler Meta class.""" func: Handler decorators: set[EnrouteDecorator] checkers: set[CheckerMeta]
[docs] def __init__( self, func: Handler, decorators: Optional[set[EnrouteDecorator]] = None, checkers: Optional[set[CheckerMeta]] = None, ): if decorators is None: decorators = set() if checkers is None: checkers = set() self.func = func self.decorators = decorators self.checkers = checkers
@property def wrapper(self) -> HandlerWrapper: """Get the ``HandlerWrapper`` instance. :return: A ``HandlerWrapper`` instance. """ if iscoroutinefunction(self.func): return self.async_wrapper else: return self.sync_wrapper @cached_property def async_wrapper(self) -> HandlerWrapper: """Get the async ``HandlerWrapper`` instance. :return: A ``HandlerWrapper`` instance. """ @wraps(self.func) async def _wrapper(*args, **kwargs) -> Optional[Response]: try: await CheckerMeta.run_async(self.checkers, *args, **kwargs) except NotSatisfiedCheckerException as exc: raise ResponseException(f"There was an exception during check step: {exc}") response = self.func(*args, **kwargs) if isawaitable(response): response = await response return response _wrapper.meta = self _wrapper.check = self.check _wrapper.__decorators__ = self.decorators # FIXME: This attribute should be removed in future versions. return _wrapper @cached_property def sync_wrapper(self) -> HandlerWrapper: """Get the sync ``HandlerWrapper`` instance. :return: A ``HandlerWrapper`` instance. """ if iscoroutinefunction(self.func): raise ValueError(f"{self.func!r} cannot be awaitable.") @wraps(self.func) def _wrapper(*args, **kwargs) -> Optional[Response]: try: CheckerMeta.run_sync(self.checkers, *args, **kwargs) except NotSatisfiedCheckerException as exc: raise ResponseException(f"There was an exception during check step: {exc}") return self.func(*args, **kwargs) _wrapper.meta = self _wrapper.check = self.check _wrapper.__decorators__ = self.decorators # FIXME: This attribute should be removed in future versions. return _wrapper
[docs] def add_decorator(self, decorator: EnrouteDecorator) -> None: """Add a new decorator to the ``decorators`` set. :param decorator: The decorator to be added. :return: This method does not return anything. """ another = next(iter(self.decorators), None) if another is not None and another.KIND != decorator.KIND: raise MinosMultipleEnrouteDecoratorKindsException( f"There are multiple kinds but only one is allowed: {(another.KIND, decorator.KIND)}" ) self.decorators.add(decorator)
@cached_property def check(self) -> Type[CheckDecorator]: """Get the check decorator. :return: A ``CheckDecorator`` type. """ from ..definitions import ( CheckDecorator, ) # noinspection PyTypeChecker return partial(CheckDecorator, handler_meta=self) def __repr__(self): args = ", ".join(map(repr, self)) return f"{type(self).__name__}({args})" def __eq__(self, other: CheckerMeta) -> bool: return isinstance(other, type(self)) and tuple(self) == tuple(other) def __hash__(self) -> int: return hash(self.func) def __iter__(self) -> Iterable: yield from ( self.func, self.decorators, self.checkers, )