tachyon-api 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. tachyon_api/__init__.py +59 -0
  2. tachyon_api/app.py +699 -0
  3. tachyon_api/background.py +72 -0
  4. tachyon_api/cache.py +270 -0
  5. tachyon_api/cli/__init__.py +9 -0
  6. tachyon_api/cli/__main__.py +8 -0
  7. tachyon_api/cli/commands/__init__.py +5 -0
  8. tachyon_api/cli/commands/generate.py +190 -0
  9. tachyon_api/cli/commands/lint.py +186 -0
  10. tachyon_api/cli/commands/new.py +82 -0
  11. tachyon_api/cli/commands/openapi.py +128 -0
  12. tachyon_api/cli/main.py +69 -0
  13. tachyon_api/cli/templates/__init__.py +8 -0
  14. tachyon_api/cli/templates/project.py +194 -0
  15. tachyon_api/cli/templates/service.py +330 -0
  16. tachyon_api/core/__init__.py +12 -0
  17. tachyon_api/core/lifecycle.py +106 -0
  18. tachyon_api/core/websocket.py +92 -0
  19. tachyon_api/di.py +86 -0
  20. tachyon_api/exceptions.py +39 -0
  21. tachyon_api/files.py +14 -0
  22. tachyon_api/middlewares/__init__.py +4 -0
  23. tachyon_api/middlewares/core.py +40 -0
  24. tachyon_api/middlewares/cors.py +159 -0
  25. tachyon_api/middlewares/logger.py +123 -0
  26. tachyon_api/models.py +73 -0
  27. tachyon_api/openapi.py +419 -0
  28. tachyon_api/params.py +268 -0
  29. tachyon_api/processing/__init__.py +14 -0
  30. tachyon_api/processing/dependencies.py +172 -0
  31. tachyon_api/processing/parameters.py +484 -0
  32. tachyon_api/processing/response_processor.py +93 -0
  33. tachyon_api/responses.py +92 -0
  34. tachyon_api/router.py +161 -0
  35. tachyon_api/security.py +295 -0
  36. tachyon_api/testing.py +110 -0
  37. tachyon_api/utils/__init__.py +15 -0
  38. tachyon_api/utils/type_converter.py +113 -0
  39. tachyon_api/utils/type_utils.py +162 -0
  40. tachyon_api-0.9.0.dist-info/METADATA +291 -0
  41. tachyon_api-0.9.0.dist-info/RECORD +44 -0
  42. tachyon_api-0.9.0.dist-info/WHEEL +4 -0
  43. tachyon_api-0.9.0.dist-info/entry_points.txt +3 -0
  44. tachyon_api-0.9.0.dist-info/licenses/LICENSE +17 -0
tachyon_api/openapi.py ADDED
@@ -0,0 +1,419 @@
1
+ from typing import Dict, Any, Optional, List, Type
2
+ from dataclasses import dataclass, field
3
+ import datetime
4
+ import uuid
5
+ import json
6
+
7
+ from .models import Struct
8
+ from .utils import TypeUtils, OPENAPI_TYPE_MAP
9
+
10
+
11
+ def _schema_for_python_type(
12
+ py_type: Type,
13
+ components: Dict[str, Dict[str, Any]],
14
+ visited: set[Type],
15
+ ) -> Dict[str, Any]:
16
+ """Return OpenAPI schema for a Python type, adding components for Structs if needed."""
17
+ # Check if Optional[T] using centralized utility
18
+ inner_type, is_optional = TypeUtils.unwrap_optional(py_type)
19
+ if is_optional:
20
+ schema = _schema_for_python_type(inner_type, components, visited)
21
+ schema["nullable"] = True
22
+ return schema
23
+
24
+ # Check if List[T] using centralized utility
25
+ is_list, item_type = TypeUtils.is_list_type(py_type)
26
+ if is_list:
27
+ item_schema = _schema_for_python_type(item_type, components, visited)
28
+ return {"type": "array", "items": item_schema}
29
+
30
+ # Struct subclass
31
+ if isinstance(py_type, type) and issubclass(py_type, Struct):
32
+ name = py_type.__name__
33
+ if py_type not in visited:
34
+ visited.add(py_type)
35
+ components[name] = _generate_struct_schema(py_type, components, visited)
36
+ return {"$ref": f"#/components/schemas/{name}"}
37
+
38
+ # Special formats
39
+ if py_type is uuid.UUID:
40
+ return {"type": "string", "format": "uuid"}
41
+ if py_type is datetime.datetime:
42
+ return {"type": "string", "format": "date-time"}
43
+ if py_type is datetime.date:
44
+ return {"type": "string", "format": "date"}
45
+
46
+ # Scalars - use centralized type mapping
47
+ return {"type": OPENAPI_TYPE_MAP.get(py_type, "string")}
48
+
49
+
50
+ def _generate_struct_schema(
51
+ struct_class: Type[Struct],
52
+ components: Dict[str, Dict[str, Any]],
53
+ visited: set[Type],
54
+ ) -> Dict[str, Any]:
55
+ """
56
+ Generate a JSON Schema dictionary for a msgspec Struct, populating components for nested Structs.
57
+ """
58
+ properties: Dict[str, Any] = {}
59
+ required: List[str] = []
60
+
61
+ annotations = getattr(struct_class, "__annotations__", {})
62
+ for field_name in getattr(struct_class, "__struct_fields__", annotations.keys()):
63
+ field_type = annotations.get(field_name, str)
64
+ # Use centralized TypeUtils instead of local _unwrap_optional
65
+ base_type, is_opt = TypeUtils.unwrap_optional(field_type)
66
+
67
+ # Build property schema
68
+ prop_schema = _schema_for_python_type(base_type, components, visited)
69
+ if is_opt:
70
+ prop_schema["nullable"] = True
71
+
72
+ properties[field_name] = prop_schema
73
+
74
+ # Determine required: mark non-Optional fields as required
75
+ if not is_opt:
76
+ required.append(field_name)
77
+
78
+ schema: Dict[str, Any] = {"type": "object", "properties": properties}
79
+ if required:
80
+ schema["required"] = required
81
+ return schema
82
+
83
+
84
+ def build_components_for_struct(
85
+ struct_class: Type[Struct],
86
+ ) -> Dict[str, Dict[str, Any]]:
87
+ """
88
+ Build components schemas for the given Struct and all nested Structs.
89
+
90
+ Returns a dict mapping component name to schema, including the top-level struct.
91
+ """
92
+ components: Dict[str, Dict[str, Any]] = {}
93
+ visited: set[Type] = set()
94
+ name = struct_class.__name__
95
+ components[name] = _generate_struct_schema(struct_class, components, visited)
96
+ return components
97
+
98
+
99
+ @dataclass
100
+ class Contact:
101
+ """Contact information for the API"""
102
+
103
+ name: Optional[str] = None
104
+ url: Optional[str] = None
105
+ email: Optional[str] = None
106
+
107
+ def to_dict(self) -> Dict[str, Any]:
108
+ """Convert to OpenAPI contact object"""
109
+ result = {}
110
+ if self.name:
111
+ result["name"] = self.name
112
+ if self.url:
113
+ result["url"] = self.url
114
+ if self.email:
115
+ result["email"] = self.email
116
+ return result
117
+
118
+
119
+ @dataclass
120
+ class License:
121
+ """License information for the API"""
122
+
123
+ name: str
124
+ url: Optional[str] = None
125
+
126
+ def to_dict(self) -> Dict[str, Any]:
127
+ """Convert to OpenAPI license object"""
128
+ result = {"name": self.name}
129
+ if self.url:
130
+ result["url"] = self.url
131
+ return result
132
+
133
+
134
+ @dataclass
135
+ class Info:
136
+ """General information about the API"""
137
+
138
+ title: str = "Tachyon API"
139
+ description: Optional[str] = "A fast API built with Tachyon"
140
+ version: str = "0.1.0"
141
+ terms_of_service: Optional[str] = None
142
+ contact: Optional[Contact] = None
143
+ license: Optional[License] = None
144
+
145
+ def to_dict(self) -> Dict[str, Any]:
146
+ """Convert to OpenAPI info object"""
147
+ result: Dict[str, Any] = {"title": self.title, "version": self.version}
148
+ if self.description:
149
+ result["description"] = self.description
150
+ if self.terms_of_service:
151
+ result["termsOfService"] = self.terms_of_service
152
+ if self.contact:
153
+ result["contact"] = self.contact.to_dict()
154
+ if self.license:
155
+ result["license"] = self.license.to_dict()
156
+ return result
157
+
158
+
159
+ @dataclass
160
+ class Server:
161
+ """Server information"""
162
+
163
+ url: str
164
+ description: Optional[str] = None
165
+
166
+ def to_dict(self) -> Dict[str, Any]:
167
+ """Convert to OpenAPI server object"""
168
+ result = {"url": self.url}
169
+ if self.description:
170
+ result["description"] = self.description
171
+ return result
172
+
173
+
174
+ @dataclass
175
+ class OpenAPIConfig:
176
+ """Configuration for OpenAPI/Swagger documentation"""
177
+
178
+ info: Info = field(default_factory=Info)
179
+ servers: List[Server] = field(default_factory=list)
180
+ openapi_version: str = "3.0.0"
181
+ docs_url: str = "/docs"
182
+ redoc_url: str = "/redoc"
183
+ openapi_url: str = "/openapi.json"
184
+ include_in_schema: bool = True
185
+ # Scalar configuration
186
+ scalar_js_url: str = "https://cdn.jsdelivr.net/npm/@scalar/api-reference"
187
+ scalar_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png"
188
+ # Swagger UI configuration (legacy support)
189
+ swagger_ui_oauth2_redirect_url: Optional[str] = None
190
+ swagger_ui_init_oauth: Optional[Dict[str, Any]] = None
191
+ swagger_ui_parameters: Optional[Dict[str, Any]] = None
192
+ swagger_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png"
193
+ swagger_js_url: str = (
194
+ "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js"
195
+ )
196
+ swagger_css_url: str = (
197
+ "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css"
198
+ )
199
+ redoc_js_url: str = (
200
+ "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"
201
+ )
202
+
203
+ def to_openapi_dict(self) -> Dict[str, Any]:
204
+ """Generate the complete OpenAPI dictionary"""
205
+ openapi_dict = {
206
+ "openapi": self.openapi_version,
207
+ "info": self.info.to_dict(),
208
+ "paths": {},
209
+ "components": {"schemas": {}},
210
+ }
211
+
212
+ if self.servers:
213
+ openapi_dict["servers"] = [server.to_dict() for server in self.servers]
214
+
215
+ return openapi_dict
216
+
217
+
218
+ class OpenAPIGenerator:
219
+ """Generator for OpenAPI documentation"""
220
+
221
+ def __init__(self, config: Optional[OpenAPIConfig] = None):
222
+ """
223
+ Initialize the OpenAPI generator.
224
+
225
+ Args:
226
+ config: Optional OpenAPI configuration. Uses defaults if not provided.
227
+ """
228
+ self.config = config or OpenAPIConfig()
229
+ self._openapi_schema: Optional[Dict[str, Any]] = None
230
+
231
+ def get_openapi_schema(self) -> Dict[str, Any]:
232
+ """Get the complete OpenAPI schema"""
233
+ if self._openapi_schema is None:
234
+ self._openapi_schema = self.config.to_openapi_dict()
235
+ return self._openapi_schema
236
+
237
+ def get_swagger_ui_html(self, openapi_url: str, title: str) -> str:
238
+ """Generate HTML for Swagger UI"""
239
+ swagger_ui_parameters = self.config.swagger_ui_parameters or {}
240
+
241
+ # Serialize parameters to JSON safely
242
+ params_json = json.dumps(swagger_ui_parameters)
243
+
244
+ html = f"""<!DOCTYPE html>
245
+ <html>
246
+ <head>
247
+ <link type="text/css" rel="stylesheet" href="{self.config.swagger_css_url}">
248
+ <link rel="shortcut icon" href="{self.config.swagger_favicon_url}">
249
+ <title>{title}</title>
250
+ </head>
251
+ <body>
252
+ <div id="swagger-ui"></div>
253
+ <script src="{self.config.swagger_js_url}"></script>
254
+ <script>
255
+ const ui = SwaggerUIBundle({{
256
+ url: '{openapi_url}',
257
+ dom_id: '#swagger-ui',
258
+ presets: [
259
+ SwaggerUIBundle.presets.apis,
260
+ SwaggerUIBundle.presets.standalone
261
+ ],
262
+ layout: "BaseLayout",
263
+ ...{params_json}
264
+ }})
265
+ </script>
266
+ </body>
267
+ </html>"""
268
+ return html
269
+
270
+ def get_redoc_html(self, openapi_url: str, title: str) -> str:
271
+ """Generate HTML for ReDoc"""
272
+ html = f"""<!DOCTYPE html>
273
+ <html>
274
+ <head>
275
+ <title>{title}</title>
276
+ <meta charset="utf-8"/>
277
+ <meta name="viewport" content="width=device-width, initial-scale=1">
278
+ <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
279
+ <style>
280
+ body {{
281
+ margin: 0;
282
+ padding: 0;
283
+ }}
284
+ </style>
285
+ </head>
286
+ <body>
287
+ <redoc spec-url='{openapi_url}'></redoc>
288
+ <script src="{self.config.redoc_js_url}"></script>
289
+ </body>
290
+ </html>"""
291
+ return html
292
+
293
+ def get_scalar_html(self, openapi_url: str, title: str) -> str:
294
+ """Generate HTML for Scalar API Reference"""
295
+ html = f"""<!DOCTYPE html>
296
+ <html>
297
+ <head>
298
+ <title>{title}</title>
299
+ <meta charset="utf-8" />
300
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
301
+ <link rel="shortcut icon" href="{self.config.scalar_favicon_url}">
302
+ <style>
303
+ body {{
304
+ margin: 0;
305
+ padding: 0;
306
+ }}
307
+ </style>
308
+ </head>
309
+ <body>
310
+ <script
311
+ id="api-reference"
312
+ data-url="{openapi_url}"
313
+ src="{self.config.scalar_js_url}"></script>
314
+ </body>
315
+ </html>"""
316
+ return html
317
+
318
+ def add_path(self, path: str, method: str, operation_data: Dict[str, Any]) -> None:
319
+ """
320
+ Add a path operation to the OpenAPI schema.
321
+
322
+ Args:
323
+ path: The URL path (e.g., "/items/{item_id}")
324
+ method: HTTP method (e.g., "get", "post")
325
+ operation_data: OpenAPI operation object
326
+ """
327
+ if self._openapi_schema is None:
328
+ self._openapi_schema = self.config.to_openapi_dict()
329
+ if path not in self._openapi_schema["paths"]:
330
+ self._openapi_schema["paths"][path] = {}
331
+
332
+ self._openapi_schema["paths"][path][method.lower()] = operation_data
333
+
334
+ def add_schema(self, name: str, schema_data: Dict[str, Any]) -> None:
335
+ """
336
+ Add a component schema to the OpenAPI specification.
337
+
338
+ Args:
339
+ name: Schema name (e.g., "Item", "User")
340
+ schema_data: OpenAPI schema object
341
+ """
342
+ if self._openapi_schema is None:
343
+ self._openapi_schema = self.config.to_openapi_dict()
344
+
345
+ self._openapi_schema["components"]["schemas"][name] = schema_data
346
+
347
+
348
+ def create_openapi_config(
349
+ title: str = "Tachyon API",
350
+ description: Optional[str] = "A fast API built with Tachyon",
351
+ version: str = "0.1.0",
352
+ openapi_version: str = "3.0.0",
353
+ docs_url: str = "/docs",
354
+ redoc_url: str = "/redoc",
355
+ openapi_url: str = "/openapi.json",
356
+ contact: Optional[Contact] = None,
357
+ license: Optional[License] = None,
358
+ servers: Optional[List[Server]] = None,
359
+ terms_of_service: Optional[str] = None,
360
+ # Scalar configuration
361
+ scalar_js_url: str = "https://cdn.jsdelivr.net/npm/@scalar/api-reference",
362
+ scalar_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png",
363
+ # Swagger UI configuration (legacy support)
364
+ swagger_ui_parameters: Optional[Dict[str, Any]] = None,
365
+ swagger_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png",
366
+ swagger_js_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",
367
+ swagger_css_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css",
368
+ redoc_js_url: str = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js",
369
+ ) -> OpenAPIConfig:
370
+ """
371
+ Create a customizable OpenAPI configuration similar to FastAPI.
372
+
373
+ Args:
374
+ title: API title
375
+ description: API description
376
+ version: API version
377
+ openapi_version: OpenAPI specification version
378
+ docs_url: URL for Scalar API Reference documentation (default)
379
+ redoc_url: URL for ReDoc documentation
380
+ openapi_url: URL for OpenAPI JSON schema
381
+ contact: Contact information
382
+ license: License information
383
+ servers: List of servers
384
+ terms_of_service: Terms of service URL
385
+ scalar_js_url: Scalar API Reference JavaScript URL
386
+ scalar_favicon_url: Favicon URL for Scalar
387
+ swagger_ui_parameters: Additional Swagger UI parameters
388
+ swagger_favicon_url: Favicon URL for Swagger UI
389
+ swagger_js_url: Swagger UI JavaScript URL
390
+ swagger_css_url: Swagger UI CSS URL
391
+ redoc_js_url: ReDoc JavaScript URL
392
+
393
+ Returns:
394
+ Configured OpenAPIConfig instance
395
+ """
396
+ info = Info(
397
+ title=title,
398
+ description=description,
399
+ version=version,
400
+ terms_of_service=terms_of_service,
401
+ contact=contact,
402
+ license=license,
403
+ )
404
+
405
+ return OpenAPIConfig(
406
+ info=info,
407
+ servers=servers or [],
408
+ openapi_version=openapi_version,
409
+ docs_url=docs_url,
410
+ redoc_url=redoc_url,
411
+ openapi_url=openapi_url,
412
+ scalar_js_url=scalar_js_url,
413
+ scalar_favicon_url=scalar_favicon_url,
414
+ swagger_ui_parameters=swagger_ui_parameters,
415
+ swagger_favicon_url=swagger_favicon_url,
416
+ swagger_js_url=swagger_js_url,
417
+ swagger_css_url=swagger_css_url,
418
+ redoc_js_url=redoc_js_url,
419
+ )
tachyon_api/params.py ADDED
@@ -0,0 +1,268 @@
1
+ """
2
+ Tachyon Web Framework - Parameter Definition Module
3
+
4
+ This module provides parameter marker classes for defining how endpoint function
5
+ parameters should be resolved from HTTP requests (query strings, path variables,
6
+ and request bodies).
7
+ """
8
+
9
+ from typing import Any, Optional
10
+
11
+
12
+ class Query:
13
+ """
14
+ Marker class for query string parameters.
15
+
16
+ Use this to define parameters that should be extracted from the URL query string
17
+ with optional default values and automatic type conversion.
18
+
19
+ Args:
20
+ default: Default value if parameter is not provided. Use ... for required parameters.
21
+ description: Optional description for OpenAPI documentation.
22
+
23
+ Example:
24
+ @app.get("/search")
25
+ def search(
26
+ q: str = Query(...), # Required query parameter
27
+ limit: int = Query(10), # Optional with default value
28
+ active: bool = Query(False) # Optional boolean parameter
29
+ ):
30
+ return {"query": q, "limit": limit, "active": active}
31
+
32
+ Note:
33
+ - Boolean parameters accept: "true", "1", "t", "yes" (case-insensitive) as True
34
+ - Type conversion is automatic based on parameter annotation
35
+ - Missing required parameters return 422 Unprocessable Entity
36
+ - Invalid type conversions return 422 Unprocessable Entity
37
+ """
38
+
39
+ def __init__(self, default: Any = ..., description: Optional[str] = None):
40
+ """
41
+ Initialize a Query parameter marker.
42
+
43
+ Args:
44
+ default: Default value for the parameter. Use ... (Ellipsis) for required parameters.
45
+ description: Optional description for API documentation.
46
+ """
47
+ self.default = default
48
+ self.description = description
49
+
50
+
51
+ class Path:
52
+ """
53
+ Marker class for path parameters.
54
+
55
+ Use this to define parameters that should be extracted from the URL path.
56
+ Path parameters are always required.
57
+
58
+ Args:
59
+ description: Optional description for OpenAPI documentation.
60
+ """
61
+
62
+ def __init__(self, description: Optional[str] = None):
63
+ """
64
+ Initialize a Path parameter marker.
65
+
66
+ Args:
67
+ description: Optional description for API documentation.
68
+ """
69
+ self.description = description
70
+
71
+
72
+ class Body:
73
+ """
74
+ Marker class for request body parameters.
75
+
76
+ Use this to define parameters that should be extracted and validated from
77
+ the JSON request body. The parameter type should be a Struct subclass.
78
+
79
+ Args:
80
+ description: Optional description for OpenAPI documentation.
81
+ """
82
+
83
+ def __init__(self, description: Optional[str] = None):
84
+ """
85
+ Initialize a Body parameter marker.
86
+
87
+ Args:
88
+ description: Optional description for API documentation.
89
+ """
90
+ self.description = description
91
+
92
+
93
+ class Header:
94
+ """
95
+ Marker class for HTTP header parameters.
96
+
97
+ Use this to define parameters that should be extracted from HTTP request headers.
98
+ Header names are case-insensitive per HTTP specification.
99
+
100
+ Args:
101
+ default: Default value if header is not provided. Use ... for required headers.
102
+ alias: Optional custom header name. If not provided, the parameter name is used
103
+ with underscores converted to hyphens.
104
+ description: Optional description for OpenAPI documentation.
105
+
106
+ Example:
107
+ @app.get("/protected")
108
+ def protected(
109
+ authorization: str = Header(...), # Required header
110
+ x_request_id: str = Header("default-id"), # Optional with default
111
+ token: str = Header(..., alias="X-Auth-Token") # Custom header name
112
+ ):
113
+ return {"auth": authorization, "id": x_request_id}
114
+
115
+ Note:
116
+ - Header names are matched case-insensitively
117
+ - Parameter names with underscores match headers with hyphens
118
+ (e.g., x_request_id matches X-Request-Id)
119
+ - Missing required headers return 422 Unprocessable Entity
120
+ """
121
+
122
+ def __init__(
123
+ self,
124
+ default: Any = ...,
125
+ alias: Optional[str] = None,
126
+ description: Optional[str] = None,
127
+ ):
128
+ """
129
+ Initialize a Header parameter marker.
130
+
131
+ Args:
132
+ default: Default value for the header. Use ... (Ellipsis) for required.
133
+ alias: Optional custom header name to use instead of parameter name.
134
+ description: Optional description for API documentation.
135
+ """
136
+ self.default = default
137
+ self.alias = alias
138
+ self.description = description
139
+
140
+
141
+ class Cookie:
142
+ """
143
+ Marker class for HTTP cookie parameters.
144
+
145
+ Use this to define parameters that should be extracted from HTTP cookies.
146
+
147
+ Args:
148
+ default: Default value if cookie is not provided. Use ... for required cookies.
149
+ alias: Optional custom cookie name.
150
+ description: Optional description for OpenAPI documentation.
151
+
152
+ Example:
153
+ @app.get("/profile")
154
+ def profile(session_id: str = Cookie(...)):
155
+ return {"session": session_id}
156
+
157
+ Note:
158
+ - Missing required cookies return 422 Unprocessable Entity
159
+ """
160
+
161
+ def __init__(
162
+ self,
163
+ default: Any = ...,
164
+ alias: Optional[str] = None,
165
+ description: Optional[str] = None,
166
+ ):
167
+ """
168
+ Initialize a Cookie parameter marker.
169
+
170
+ Args:
171
+ default: Default value for the cookie. Use ... (Ellipsis) for required.
172
+ alias: Optional custom cookie name to use instead of parameter name.
173
+ description: Optional description for API documentation.
174
+ """
175
+ self.default = default
176
+ self.alias = alias
177
+ self.description = description
178
+
179
+
180
+ class Form:
181
+ """
182
+ Marker class for form data parameters.
183
+
184
+ Use this to define parameters that should be extracted from
185
+ application/x-www-form-urlencoded or multipart/form-data request bodies.
186
+
187
+ Args:
188
+ default: Default value if field is not provided. Use ... for required fields.
189
+ alias: Optional custom field name.
190
+ description: Optional description for OpenAPI documentation.
191
+
192
+ Example:
193
+ @app.post("/login")
194
+ async def login(
195
+ username: str = Form(...),
196
+ password: str = Form(...),
197
+ ):
198
+ return {"username": username}
199
+
200
+ Note:
201
+ - Missing required form fields return 422 Unprocessable Entity
202
+ - Works with multipart/form-data for file uploads
203
+ """
204
+
205
+ def __init__(
206
+ self,
207
+ default: Any = ...,
208
+ alias: Optional[str] = None,
209
+ description: Optional[str] = None,
210
+ ):
211
+ """
212
+ Initialize a Form parameter marker.
213
+
214
+ Args:
215
+ default: Default value for the field. Use ... (Ellipsis) for required.
216
+ alias: Optional custom field name to use instead of parameter name.
217
+ description: Optional description for API documentation.
218
+ """
219
+ self.default = default
220
+ self.alias = alias
221
+ self.description = description
222
+
223
+
224
+ class File:
225
+ """
226
+ Marker class for file upload parameters.
227
+
228
+ Use this to define parameters that should be extracted from
229
+ multipart/form-data file uploads. The parameter type should be UploadFile.
230
+
231
+ Args:
232
+ default: Default value if file is not provided. Use ... for required files,
233
+ None for optional files.
234
+ description: Optional description for OpenAPI documentation.
235
+
236
+ Example:
237
+ from tachyon_api.files import UploadFile
238
+
239
+ @app.post("/upload")
240
+ async def upload(file: UploadFile = File(...)):
241
+ content = await file.read()
242
+ return {"filename": file.filename, "size": len(content)}
243
+
244
+ @app.post("/optional-upload")
245
+ async def optional(file: UploadFile = File(None)):
246
+ if file is None:
247
+ return {"uploaded": False}
248
+ return {"uploaded": True, "filename": file.filename}
249
+
250
+ Note:
251
+ - Missing required files return 422 Unprocessable Entity
252
+ - Use UploadFile type annotation for file parameters
253
+ """
254
+
255
+ def __init__(
256
+ self,
257
+ default: Any = ...,
258
+ description: Optional[str] = None,
259
+ ):
260
+ """
261
+ Initialize a File parameter marker.
262
+
263
+ Args:
264
+ default: Default value. Use ... for required, None for optional.
265
+ description: Optional description for API documentation.
266
+ """
267
+ self.default = default
268
+ self.description = description
@@ -0,0 +1,14 @@
1
+ """
2
+ Request/response processing for Tachyon applications.
3
+
4
+ This module contains components for:
5
+ - parameters: Extraction and validation of request parameters
6
+ - dependencies: Dependency injection resolution
7
+ - response_processor: Response validation and serialization
8
+ """
9
+
10
+ from .parameters import ParameterProcessor
11
+ from .dependencies import DependencyResolver
12
+ from .response_processor import ResponseProcessor
13
+
14
+ __all__ = ["ParameterProcessor", "DependencyResolver", "ResponseProcessor"]