Source code for fitrequest.method_decorator
import inspect
import re
from collections.abc import Callable
from typing import Annotated, Any
import makefun
from pydantic import BeforeValidator, PlainSerializer
from fitrequest.decorators.paginated import paginated
from fitrequest.decorators.retry import retry
from fitrequest.errors import InvalidMethodDecoratorError
#: Global dictionnary used to map ``json/yaml`` declared decorators to actual python decorators.
environment_decorators = {
'retry': retry,
'paginated': paginated,
}
[docs]
def eval_decorator_signature(signature: str, environment: dict) -> Callable | None:
"""
Evaluate the decorator signature and return the associated method from the environment.
Return None if the decorator is not found.
"""
# Decorator without args
if not re.match(r'^\w+\(.*\)$', signature):
return environment.get(signature)
# Decorator with args
deco_data = makefun.create_function(func_signature=signature, func_impl=lambda _: _)
deco_name = deco_data.__name__
deco_params = {field: value.default for field, value in inspect.signature(deco_data).parameters.items()}
if (decorator := environment.get(deco_name)) is None:
return None
return decorator(**deco_params)
[docs]
def validate_init_value(value: Any) -> Callable:
"""
Validates the provided decorator value.
If a string is given, attempts to retrieve the corresponding decorator from the global environment.
Raises an InvalidMethodDecoratorError if the value is invalid.
"""
if callable(value):
return value
if not isinstance(value, str):
raise InvalidMethodDecoratorError(provided_decorator=str(value))
if (decorator := eval_decorator_signature(value, environment=environment_decorators)) is None:
raise InvalidMethodDecoratorError(provided_decorator=value)
return decorator
ValidMethodDecorator = Annotated[
Callable | str,
BeforeValidator(validate_init_value),
PlainSerializer(lambda func: func.__qualname__, return_type=str),
]