Coverage for postrfp/shared/decorators.py: 100%
37 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-22 21:34 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-22 21:34 +0000
1import logging
2from typing import TypeVar, Callable
4from pydantic import BaseModel
5from webob.response import Response
7from .exceptions import RoutingError
8from .constants import HANDLER_PREFIX
10log = logging.getLogger(__name__)
12T = TypeVar("T", bound=Callable[..., BaseModel | dict | list | Response | None])
15def _set_exposed(handler: T) -> None:
16 fname = handler.__name__
17 if not HANDLER_PREFIX.match(fname):
18 raise RoutingError(
19 f"{fname} is not a valid name for a handler function."
20 " Must start with get, post, put or delete"
21 )
22 setattr(handler, "http_exposed", True)
25def _check_docstring(handler: T) -> None:
26 """
27 Check that the http function has a docstring suitable for publication
28 in the resulting openapi spec.
29 """
30 docstring = handler.__doc__
31 if not docstring or len(docstring.strip()) < 10:
32 log.error(f'{handler.__module__} "{handler.__name__}" is missing a docstring.')
35def http(handler: T) -> T:
36 """
37 Decorator to set exposed=True on a handler callable
38 """
39 _set_exposed(handler)
40 _check_docstring(handler)
41 return handler
44def http_etag(handler: T) -> T:
45 """
46 Decorator to set attributes exposed=True and etag=True on a handler callable
47 """
48 _set_exposed(handler)
49 _check_docstring(handler)
50 setattr(handler, "etag", True)
51 return handler
54class http_auth:
55 """
56 Decorator to mark a handler as HTTP accessible and associate a permission name.
58 The permission name is stored on the handler as ``required_permission``.
59 """
61 def __init__(self, permission: str, deny_restricted: bool = False) -> None:
62 if not isinstance(permission, str) or not permission:
63 raise ValueError("permission must be a non-empty string")
64 self.permission = permission
65 self.deny_restricted = deny_restricted
67 def __call__(self, handler: T) -> T:
68 # Mark the handler as HTTP accessible and check for docstring
69 decorated = http(handler)
70 # Apply the permission attributes passed as decorator arguments
71 setattr(decorated, "required_permission", self.permission)
72 setattr(decorated, "deny_restricted", self.deny_restricted)
74 return decorated