Coverage for src/lexigram/web/routing/parameters.py: 48%
42 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"""Parameter decorators for request binding"""
3from __future__ import annotations
5from collections.abc import Callable
6from typing import Any
8from lexigram.web.routing.types import RoutableProtocol
11def _create_param_decorator(
12 param_type: str,
13 name: str | None = None,
14 default: Any = ...,
15 alias: str | None = None,
16 validation: Callable[..., Any] | None = None,
17 pipes: list[Any] | None = None,
18) -> Any:
19 """Create a parameter decorator that properly attaches metadata to parameters"""
21 metadata = {
22 "type": param_type,
23 "name": name,
24 "default": default,
25 "alias": alias,
26 "validation": validation,
27 "pipes": pipes,
28 }
30 def decorator(
31 param_annotation: Any = None,
32 ) -> RoutableProtocol | Callable[[RoutableProtocol], RoutableProtocol]:
33 # If used as @path, @query, etc. without parentheses
34 if callable(param_annotation):
35 # This is the function being decorated
36 func: RoutableProtocol = param_annotation
37 # For now, we'll store metadata on the function and match by position
38 # A more sophisticated implementation would use AST parsing
39 func_metadata = getattr(func, "_param_metadata", None)
40 if func_metadata is None:
41 func_metadata = []
42 func._param_metadata = func_metadata
43 func_metadata.append(metadata)
44 return func
46 # If used as @path(), @query(), etc. with parentheses
47 def param_decorator(func: RoutableProtocol) -> RoutableProtocol:
48 func_metadata = getattr(func, "_param_metadata", None)
49 if func_metadata is None:
50 func_metadata = []
51 func._param_metadata = func_metadata
52 # Merge annotation into metadata for this usage
53 usage_metadata = metadata.copy()
54 usage_metadata["annotation"] = param_annotation
55 func_metadata.append(usage_metadata)
56 return func
58 # Attach metadata to param_decorator as well
59 param_decorator._lexigram_param_info = metadata # type: ignore[attr-defined]
60 return param_decorator
62 # Attach metadata to the decorator function itself
63 decorator._lexigram_param_info = metadata # type: ignore[attr-defined]
64 return decorator
67def path(
68 alias: str | None = None,
69 validation: Callable[..., Any] | None = None,
70 pipes: list[Any] | None = None,
71) -> Any:
72 """Path parameter decorator."""
73 return _create_param_decorator(
74 "path",
75 alias=alias,
76 validation=validation,
77 pipes=pipes,
78 )
81# Query Parameters
82def query(
83 default: Any = ...,
84 alias: str | None = None,
85 validation: Callable[..., Any] | None = None,
86 pipes: list[Any] | None = None,
87) -> Any:
88 """Query parameter decorator."""
89 return _create_param_decorator(
90 "query",
91 default=default,
92 alias=alias,
93 validation=validation,
94 pipes=pipes,
95 )
98# Body Parameters
99def body(
100 default: Any = ...,
101 validation: Callable[..., Any] | None = None,
102 pipes: list[Any] | None = None,
103) -> Any:
104 """Request body parameter decorator."""
105 return _create_param_decorator(
106 "body", default=default, validation=validation, pipes=pipes
107 )
110# Header Parameters
111def header(
112 default: Any = ...,
113 alias: str | None = None,
114 validation: Callable[..., Any] | None = None,
115 pipes: list[Any] | None = None,
116) -> Any:
117 """Header parameter decorator."""
118 return _create_param_decorator(
119 "header",
120 default=default,
121 alias=alias,
122 validation=validation,
123 pipes=pipes,
124 )
127# Cookie Parameters
128def cookie(
129 default: Any = ...,
130 alias: str | None = None,
131 validation: Callable[..., Any] | None = None,
132 pipes: list[Any] | None = None,
133) -> Any:
134 """Cookie parameter decorator."""
135 return _create_param_decorator(
136 "cookie",
137 default=default,
138 alias=alias,
139 validation=validation,
140 pipes=pipes,
141 )
144# Form Parameters
145def form(
146 default: Any = ...,
147 alias: str | None = None,
148 validation: Callable[..., Any] | None = None,
149 pipes: list[Any] | None = None,
150) -> Any:
151 """Form parameter decorator."""
152 return _create_param_decorator(
153 "form",
154 default=default,
155 alias=alias,
156 validation=validation,
157 pipes=pipes,
158 )
161# File Parameters
162def file(
163 default: Any = ...,
164 validation: Callable[..., Any] | None = None,
165 pipes: list[Any] | None = None,
166) -> Any:
167 """File upload parameter decorator."""
168 return _create_param_decorator(
169 "file", default=default, validation=validation, pipes=pipes
170 )