Coverage for src/lexigram/web/routing/decorators.py: 81%
27 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 04:37 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 04:37 +0800
1"""HTTP route decorators for defining endpoints.
3Provides decorator functions for each HTTP method to register route handlers
4with the Lexigram router. These decorators store route configuration on the
5decorated function for later discovery by the routing system.
7Example:
8 >>> from lexigram.web.routing import get, post
9 >>>
10 >>> @get("/users")
11 >>> async def list_users(request):
12 ... return {"users": []}
13 >>>
14 >>> @post("/users")
15 >>> async def create_user(request):
16 ... data = await request.json()
17 ... return {"id": 1, "data": data}
18"""
20from __future__ import annotations
22from collections.abc import Callable
23from typing import Any, TypeVar, cast
25F = TypeVar("F", bound=Callable[..., Any])
28def route(method: str, path: str, **kwargs: Any) -> Callable[[F], F]:
29 """Create a route decorator for the specified HTTP method.
31 Args:
32 method: HTTP method (GET, POST, PUT, DELETE, etc.).
33 path: URL path pattern for the route.
34 **kwargs: Additional route configuration options.
36 Returns:
37 A decorator function that configures the route handler.
38 """
40 def decorator(func: F) -> F:
41 # Use setattr to avoid ruff auto-converting to direct attribute access
42 # which fails because RoutableProtocol is a Callable and ruff/mypy don't like
43 # dynamic attributes on it without setattr.
44 cast("Any", func)._route_config = {"method": method, "path": path, **kwargs}
45 return func
47 return decorator
50def get(path: str, **kwargs: Any) -> Callable[[F], F]:
51 """Register a GET route handler.
53 Args:
54 path: URL path pattern for the route.
55 **kwargs: Additional route configuration.
57 Returns:
58 Configured route handler function.
60 Example:
61 >>> @get("/users")
62 >>> async def list_users(request):
63 ... return {"users": []}
64 """
65 return route("GET", path, **kwargs)
68def post(path: str, **kwargs: Any) -> Callable[[F], F]:
69 """Register a POST route handler.
71 Parameters annotated with a Pydantic ``BaseModel`` or ``DomainModel``
72 subclass are automatically deserialised from the JSON request body by
73 :class:`~lexigram.web.routing.parameter_binder.ParameterBinder` — no
74 ``body()`` decorator is required.
76 Args:
77 path: URL path pattern for the route.
78 **kwargs: Additional route configuration.
80 Returns:
81 Configured route handler function.
83 Example:
84 >>> @post("/users")
85 >>> async def create_user(request):
86 ... data = await request.json()
87 ... return {"id": 1, "data": data}
88 """
89 return route("POST", path, **kwargs)
92def put(path: str, **kwargs: Any) -> Callable[[F], F]:
93 """Register a PUT route handler.
95 Args:
96 path: URL path pattern for the route.
97 **kwargs: Additional route configuration.
99 Returns:
100 Configured route handler function.
101 """
102 return route("PUT", path, **kwargs)
105def delete(path: str, **kwargs: Any) -> Callable[[F], F]:
106 """Register a DELETE route handler.
108 Args:
109 path: URL path pattern for the route.
110 **kwargs: Additional route configuration.
112 Returns:
113 Configured route handler function.
114 """
115 return route("DELETE", path, **kwargs)
118def patch(path: str, **kwargs: Any) -> Callable[[F], F]:
119 """Register a PATCH route handler.
121 Args:
122 path: URL path pattern for the route.
123 **kwargs: Additional route configuration.
125 Returns:
126 Configured route handler function.
127 """
128 return route("PATCH", path, **kwargs)
131def head(path: str, **kwargs: Any) -> Callable[[F], F]:
132 """Register a HEAD route handler.
134 Args:
135 path: URL path pattern for the route.
136 **kwargs: Additional route configuration.
138 Returns:
139 Configured route handler function.
140 """
141 return route("HEAD", path, **kwargs)
144def options(path: str, **kwargs: Any) -> Callable[[F], F]:
145 """Register an OPTIONS route handler.
147 Args:
148 path: URL path pattern for the route.
149 **kwargs: Additional route configuration.
151 Returns:
152 Configured route handler function.
153 """
154 return route("OPTIONS", path, **kwargs)
157def trace(path: str, **kwargs: Any) -> Callable[[F], F]:
158 """Register a TRACE route handler.
160 Args:
161 path: URL path pattern for the route.
162 **kwargs: Additional route configuration.
164 Returns:
165 Configured route handler function.
166 """
167 return route("TRACE", path, **kwargs)
170def websocket(path: str, **kwargs: Any) -> Callable[[F], F]:
171 """Register a WebSocket route handler.
173 Args:
174 path: URL path pattern for the WebSocket endpoint.
175 **kwargs: Additional route configuration.
177 Returns:
178 Configured WebSocket handler function.
179 """
180 return route("WEBSOCKET", path, **kwargs)
183# NOTE: Parameter decorators were moved to `parameters.py` which provides a
184# full implementation (metadata storage and support for annotations).
185# Keep imports local to the routing package to preserve backward compatibility: