tachyon-api 0.5.11__py3-none-any.whl → 0.6.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.

Potentially problematic release.


This version of tachyon-api might be problematic. Click here for more details.

@@ -0,0 +1,144 @@
1
+ """
2
+ Response Processing System for Tachyon API
3
+
4
+ This module handles the processing and serialization of endpoint responses,
5
+ including validation against response models, Struct conversion, and final JSON serialization.
6
+ """
7
+
8
+ import asyncio
9
+ from typing import Any, Callable, Dict, Union
10
+ from starlette.responses import Response
11
+
12
+ import msgspec
13
+
14
+ from ..schemas.models import Struct
15
+ from ..schemas.responses import (
16
+ TachyonJSONResponse,
17
+ response_validation_error_response,
18
+ internal_server_error_response,
19
+ )
20
+
21
+
22
+ class ResponseProcessor:
23
+ """
24
+ Handles processing and serialization of endpoint responses.
25
+
26
+ This class manages the complete response pipeline:
27
+ - Calling endpoint functions (sync/async)
28
+ - Response model validation and conversion
29
+ - Struct to dict conversion
30
+ - Final JSON serialization
31
+ """
32
+
33
+ @staticmethod
34
+ async def process_response(
35
+ endpoint_func: Callable,
36
+ kwargs_to_inject: Dict[str, Any],
37
+ response_model: Any = None,
38
+ ) -> Union[Response, TachyonJSONResponse]:
39
+ """
40
+ Process the complete response pipeline for an endpoint.
41
+
42
+ Args:
43
+ endpoint_func: The endpoint function to call
44
+ kwargs_to_inject: Arguments to inject into the endpoint function
45
+ response_model: Optional response model for validation
46
+
47
+ Returns:
48
+ Processed response ready for client
49
+ """
50
+ try:
51
+ # Call the endpoint function
52
+ payload = await ResponseProcessor._call_endpoint_function(
53
+ endpoint_func, kwargs_to_inject
54
+ )
55
+
56
+ # If the endpoint already returned a Response object, return it directly
57
+ if isinstance(payload, Response):
58
+ return payload
59
+
60
+ # Validate/convert response against response_model if provided
61
+ if response_model is not None:
62
+ validation_result = ResponseProcessor._validate_response_model(
63
+ payload, response_model
64
+ )
65
+ if isinstance(validation_result, TachyonJSONResponse):
66
+ return validation_result # Error response
67
+ payload = validation_result
68
+
69
+ # Convert Struct objects to dictionaries for JSON serialization
70
+ payload = ResponseProcessor._convert_structs_to_dicts(payload)
71
+
72
+ return TachyonJSONResponse(payload)
73
+
74
+ except Exception:
75
+ # Fallback: prevent unhandled exceptions from leaking to the client
76
+ return internal_server_error_response()
77
+
78
+ @staticmethod
79
+ async def _call_endpoint_function(
80
+ endpoint_func: Callable, kwargs_to_inject: Dict[str, Any]
81
+ ) -> Any:
82
+ """
83
+ Call the endpoint function with injected parameters.
84
+
85
+ Handles both sync and async endpoint functions.
86
+
87
+ Args:
88
+ endpoint_func: The endpoint function to call
89
+ kwargs_to_inject: Arguments to inject
90
+
91
+ Returns:
92
+ Result of calling the endpoint function
93
+ """
94
+ if asyncio.iscoroutinefunction(endpoint_func):
95
+ payload = await endpoint_func(**kwargs_to_inject)
96
+ else:
97
+ payload = endpoint_func(**kwargs_to_inject)
98
+ return payload
99
+
100
+ @staticmethod
101
+ def _validate_response_model(
102
+ payload: Any, response_model: Any
103
+ ) -> Union[Any, TachyonJSONResponse]:
104
+ """
105
+ Validate and convert response against the response model.
106
+
107
+ Args:
108
+ payload: The response payload to validate
109
+ response_model: The expected response model
110
+
111
+ Returns:
112
+ Validated and converted payload, or error response on validation failure
113
+ """
114
+ try:
115
+ return msgspec.convert(payload, response_model)
116
+ except Exception as e:
117
+ return response_validation_error_response(str(e))
118
+
119
+ @staticmethod
120
+ def _convert_structs_to_dicts(payload: Any) -> Any:
121
+ """
122
+ Convert Struct objects to dictionaries for JSON serialization.
123
+
124
+ Handles both single Struct objects and dictionaries containing Struct values.
125
+
126
+ Args:
127
+ payload: The payload that may contain Struct objects
128
+
129
+ Returns:
130
+ Payload with Struct objects converted to dictionaries
131
+ """
132
+ if isinstance(payload, Struct):
133
+ return msgspec.to_builtins(payload)
134
+ elif isinstance(payload, dict):
135
+ # Convert any Struct values in the dictionary
136
+ converted_payload = {}
137
+ for key, value in payload.items():
138
+ if isinstance(value, Struct):
139
+ converted_payload[key] = msgspec.to_builtins(value)
140
+ else:
141
+ converted_payload[key] = value
142
+ return converted_payload
143
+ else:
144
+ return payload
@@ -0,0 +1,8 @@
1
+ """
2
+ Tachyon API routing system.
3
+ """
4
+
5
+ from .routes import TachyonRoute, TachyonRouter
6
+ from .router import Router
7
+
8
+ __all__ = ["TachyonRoute", "TachyonRouter", "Router"]
@@ -8,7 +8,7 @@ allowing for better organization of routes with common prefixes, tags, and depen
8
8
  from functools import partial
9
9
  from typing import List, Optional, Any, Callable, Dict
10
10
 
11
- from .di import Depends
11
+ from ..dependencies.injection import Depends
12
12
 
13
13
 
14
14
  class Router:
@@ -0,0 +1,243 @@
1
+ """
2
+ Starlette-Native Routes for Tachyon API
3
+
4
+ This module provides route classes that extend Starlette's routing system
5
+ while maintaining Tachyon's functionality for dependency injection and parameter processing.
6
+ """
7
+
8
+ import inspect
9
+ from typing import Any, Callable
10
+ from starlette.responses import JSONResponse
11
+ from starlette.routing import Route
12
+
13
+ from ..dependencies.injection import _registry, Depends
14
+ from ..processing.parameters import ParameterProcessor, _NotProcessed
15
+ from ..processing.responses import ResponseProcessor
16
+ from ..dependencies.resolver import DependencyResolver
17
+
18
+
19
+ class TachyonRoute(Route):
20
+ """
21
+ Starlette-native route that encapsulates Tachyon's functionality.
22
+
23
+ This route extends Starlette's Route class while providing:
24
+ - Dependency injection
25
+ - Parameter processing (Body, Query, Path)
26
+ - Response processing
27
+ - Error handling
28
+
29
+ This makes the routing system more Starlette-compatible while maintaining
30
+ all of Tachyon's features.
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ path: str,
36
+ endpoint: Callable,
37
+ *,
38
+ methods: list = None,
39
+ name: str = None,
40
+ include_in_schema: bool = True,
41
+ response_model: Any = None,
42
+ dependency_resolver: DependencyResolver = None,
43
+ **kwargs,
44
+ ):
45
+ """
46
+ Initialize a Tachyon route.
47
+
48
+ Args:
49
+ path: URL path pattern
50
+ endpoint: Endpoint function
51
+ methods: HTTP methods (defaults to ['GET'])
52
+ name: Route name
53
+ include_in_schema: Whether to include in OpenAPI schema
54
+ response_model: Response model for validation
55
+ dependency_resolver: Dependency resolver instance
56
+ **kwargs: Additional route options
57
+ """
58
+ # Store Tachyon-specific configuration
59
+ self.endpoint = endpoint
60
+ self.response_model = response_model
61
+ self.include_in_schema = include_in_schema
62
+ self._dependency_resolver = dependency_resolver or DependencyResolver()
63
+ self._route_kwargs = kwargs
64
+
65
+ # Create the actual handler function
66
+ handler = self._create_handler()
67
+
68
+ # Initialize parent Route with our handler
69
+ super().__init__(
70
+ path=path,
71
+ endpoint=handler,
72
+ methods=methods or ["GET"],
73
+ name=name,
74
+ )
75
+
76
+ def _create_handler(self) -> Callable:
77
+ """
78
+ Create the async handler function that will be used by Starlette.
79
+
80
+ This handler encapsulates all of Tachyon's functionality:
81
+ - Parameter processing
82
+ - Dependency injection
83
+ - Response processing
84
+ - Error handling
85
+
86
+ Returns:
87
+ Async handler function compatible with Starlette
88
+ """
89
+
90
+ async def handler(request):
91
+ """
92
+ Async request handler that processes parameters and calls the endpoint.
93
+
94
+ This handler analyzes the endpoint function signature and automatically
95
+ injects the appropriate values based on parameter annotations and defaults.
96
+ """
97
+ try:
98
+ # Process function signature and inject parameters
99
+ kwargs_to_inject = await self._process_parameters(request)
100
+
101
+ # Process the response using ResponseProcessor
102
+ return await ResponseProcessor.process_response(
103
+ self.endpoint, kwargs_to_inject, self.response_model
104
+ )
105
+
106
+ except Exception:
107
+ # Fallback: prevent unhandled exceptions from leaking to the client
108
+ from .responses import internal_server_error_response
109
+
110
+ return internal_server_error_response()
111
+
112
+ return handler
113
+
114
+ async def _process_parameters(self, request) -> dict:
115
+ """
116
+ Process endpoint parameters and inject dependencies.
117
+
118
+ Args:
119
+ request: Starlette request object
120
+
121
+ Returns:
122
+ Dictionary of parameters to inject into the endpoint function
123
+ """
124
+ kwargs_to_inject = {}
125
+ sig = inspect.signature(self.endpoint)
126
+ query_params = request.query_params
127
+ path_params = request.path_params
128
+ _raw_body = None
129
+
130
+ # Process each parameter in the endpoint function signature
131
+ for param in sig.parameters.values():
132
+ # Determine if this parameter is a dependency
133
+ is_explicit_dependency = isinstance(param.default, Depends)
134
+ is_implicit_dependency = (
135
+ param.default is inspect.Parameter.empty
136
+ and param.annotation in _registry
137
+ )
138
+
139
+ # Process dependencies (explicit and implicit)
140
+ if is_explicit_dependency or is_implicit_dependency:
141
+ target_class = param.annotation
142
+ kwargs_to_inject[param.name] = (
143
+ self._dependency_resolver.resolve_dependency(target_class)
144
+ )
145
+ continue
146
+
147
+ # Process other parameter types using ParameterProcessor
148
+ result = await ParameterProcessor.process_parameter(
149
+ param,
150
+ request,
151
+ path_params,
152
+ query_params,
153
+ _raw_body,
154
+ is_explicit_dependency,
155
+ is_implicit_dependency,
156
+ )
157
+
158
+ # If parameter was processed and returned a value/error, handle it
159
+ if not isinstance(result, _NotProcessed):
160
+ if isinstance(result, JSONResponse):
161
+ raise Exception(
162
+ "Parameter processing error"
163
+ ) # Will be caught by handler
164
+ kwargs_to_inject[param.name] = result
165
+
166
+ return kwargs_to_inject
167
+
168
+ def get_endpoint_info(self) -> dict:
169
+ """
170
+ Get endpoint information for OpenAPI generation and introspection.
171
+
172
+ Returns:
173
+ Dictionary with endpoint metadata
174
+ """
175
+ return {
176
+ "path": self.path,
177
+ "method": self.methods[0] if self.methods else "GET",
178
+ "func": self.endpoint,
179
+ "response_model": self.response_model,
180
+ "include_in_schema": self.include_in_schema,
181
+ **self._route_kwargs,
182
+ }
183
+
184
+
185
+ class TachyonRouter:
186
+ """
187
+ Starlette-native router that manages Tachyon routes.
188
+
189
+ This router provides a clean interface for registering routes
190
+ while maintaining full compatibility with Starlette's routing system.
191
+ """
192
+
193
+ def __init__(self, dependency_resolver: DependencyResolver = None):
194
+ """
195
+ Initialize the Tachyon router.
196
+
197
+ Args:
198
+ dependency_resolver: Dependency resolver instance (optional)
199
+ """
200
+ self.routes = []
201
+ self._dependency_resolver = dependency_resolver or DependencyResolver()
202
+
203
+ def add_route(
204
+ self,
205
+ path: str,
206
+ endpoint: Callable,
207
+ methods: list = None,
208
+ name: str = None,
209
+ **kwargs,
210
+ ) -> TachyonRoute:
211
+ """
212
+ Add a route to the router.
213
+
214
+ Args:
215
+ path: URL path pattern
216
+ endpoint: Endpoint function
217
+ methods: HTTP methods
218
+ name: Route name
219
+ **kwargs: Additional route options
220
+
221
+ Returns:
222
+ Created TachyonRoute instance
223
+ """
224
+ route = TachyonRoute(
225
+ path=path,
226
+ endpoint=endpoint,
227
+ methods=methods,
228
+ name=name,
229
+ dependency_resolver=self._dependency_resolver,
230
+ **kwargs,
231
+ )
232
+
233
+ self.routes.append(route)
234
+ return route
235
+
236
+ def get_starlette_routes(self) -> list:
237
+ """
238
+ Get all routes in Starlette-compatible format.
239
+
240
+ Returns:
241
+ List of Starlette Route objects
242
+ """
243
+ return self.routes
@@ -0,0 +1,17 @@
1
+ """
2
+ Tachyon API schemas and data models.
3
+ """
4
+
5
+ from .models import Struct
6
+ from .parameters import Query, Body, Path
7
+ from .responses import TachyonJSONResponse, success_response, error_response
8
+
9
+ __all__ = [
10
+ "Struct",
11
+ "Query",
12
+ "Body",
13
+ "Path",
14
+ "TachyonJSONResponse",
15
+ "success_response",
16
+ "error_response",
17
+ ]
@@ -9,7 +9,7 @@ to typed values expected by endpoint functions.
9
9
  from typing import Type, Union, Any
10
10
  from starlette.responses import JSONResponse
11
11
 
12
- from ..responses import validation_error_response
12
+ from ..schemas.responses import validation_error_response
13
13
  from .type_utils import TypeUtils
14
14
 
15
15
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: tachyon-api
3
- Version: 0.5.11
3
+ Version: 0.6.0
4
4
  Summary: A lightweight, FastAPI-inspired web framework
5
5
  License: GPL-3.0-or-later
6
6
  Author: Juan Manuel Panozzo Zénere
@@ -22,7 +22,7 @@ Description-Content-Type: text/markdown
22
22
 
23
23
  # 🚀 Tachyon API
24
24
 
25
- ![Version](https://img.shields.io/badge/version-0.5.7-blue.svg)
25
+ ![Version](https://img.shields.io/badge/version-0.6.0-blue.svg)
26
26
  ![Python](https://img.shields.io/badge/python-3.10+-brightgreen.svg)
27
27
  ![License](https://img.shields.io/badge/license-GPL--3.0-orange.svg)
28
28
  ![Status](https://img.shields.io/badge/status-stable-brightgreen.svg)
@@ -31,9 +31,10 @@ Description-Content-Type: text/markdown
31
31
 
32
32
  Tachyon API combines the intuitive decorator-based syntax you love with minimal dependencies and maximal performance. Built with Test-Driven Development from the ground up, it offers a cleaner, faster alternative with full ASGI compatibility.
33
33
 
34
+ **✨ v0.6.0 introduces Starlette-Native Architecture**: Maximum Starlette compatibility for seamless future Rust migration while maintaining all Tachyon features.
35
+
34
36
  ```python
35
- from tachyon_api import Tachyon
36
- from tachyon_api.models import Struct
37
+ from tachyon_api import Tachyon, Struct
37
38
 
38
39
  app = Tachyon()
39
40
 
@@ -63,6 +64,7 @@ def create_user(user: User):
63
64
  - 🧰 Default JSON response (TachyonJSONResponse)
64
65
  - 🔒 End-to-end safety: request Body validation + typed response_model
65
66
  - 📘 Deep OpenAPI schemas: nested Structs, Optional/List (nullable/array), formats (uuid, date-time)
67
+ - 🏗️ **Starlette-Native Architecture** (v0.6.0): Maximum compatibility for future Rust migration
66
68
 
67
69
  ## 🧪 Test-Driven Development
68
70
 
@@ -0,0 +1,33 @@
1
+ tachyon_api/__init__.py,sha256=Z9ylLDxU416uUdyDRilmUOAxNiVeiLybvVbqIuyssMc,1269
2
+ tachyon_api/core/__init__.py,sha256=G9SXqh-lbVCnFlcV83aM4T05HNGlzMr-3WVF3irx4_M,86
3
+ tachyon_api/core/app.py,sha256=-HYRhbLeXA4YOjh-0Gl2c1RtSFwGmIHft0Y8Wmr-F_c,13521
4
+ tachyon_api/dependencies/__init__.py,sha256=MXXhWXIbREA9lgNLyObjj3G9g4q9PF6e76lIpvls3MQ,217
5
+ tachyon_api/dependencies/injection.py,sha256=FyFghfUjU0OaE_lW2BqItgsZWXFbLvwFsvVywqFjl6c,1505
6
+ tachyon_api/dependencies/resolver.py,sha256=HsFdndwa4wd2jdTaV_ztz1Vr3rrBgUngIuOJmwgXhS0,2795
7
+ tachyon_api/features/__init__.py,sha256=ERv3157ho83DLwiyB3hUF_hqTf7CigfcndOq-0tv5HM,622
8
+ tachyon_api/features/cache.py,sha256=IIdg9yTiNuc9CEgV33yLXz9ssmHqI9IiqMilRbM-aZ8,8312
9
+ tachyon_api/middlewares/__init__.py,sha256=MkO5xgCMScI_lz1mdFv4oGauAfVYiNGsCbE22FgGiFg,390
10
+ tachyon_api/middlewares/core.py,sha256=-dJTrI6KJbQzatopCUC5lau_JIZpl063697qdE4W0SY,1440
11
+ tachyon_api/middlewares/cors.py,sha256=IzaedBdUCOYsQpMhZRN3mAzDevvQVnKDYzMrqY56bNA,6068
12
+ tachyon_api/middlewares/logger.py,sha256=TcYcfBuc8iBGZZIW6aMvJ_foFD7by9vM_EsHnhH9E_Y,4908
13
+ tachyon_api/middlewares/manager.py,sha256=Mm0uSfio0OqhOsOGDCwVJmgPJMrvHxxen4n9mm81zWA,2280
14
+ tachyon_api/openapi/__init__.py,sha256=qDMsg68WUVhScRlkkOjpMZUX0U0JVsxdT9f-j3DHSNo,462
15
+ tachyon_api/openapi/builder.py,sha256=jLOwYywk_OeMKY2ffSYHKk_rrl23UvV24Wz45gNNfnA,11955
16
+ tachyon_api/openapi/schema.py,sha256=GFkehWZo2ndm7URD3rqa0SAcf4g427k1Nt-0JlkKc-w,14502
17
+ tachyon_api/processing/__init__.py,sha256=_DMpR6UHgfmUBZU-zBD9OazVAie1Hnv-ZkDE7zDA_s4,221
18
+ tachyon_api/processing/parameters.py,sha256=-d7cCNiObs396XUw1hseg1XV-fH5OEOTC9us-CV6aEA,11702
19
+ tachyon_api/processing/responses.py,sha256=hml1S7iqA2bTjVQf0arN4RE03bZZ4IhI6uxmR_u9lNs,4824
20
+ tachyon_api/routing/__init__.py,sha256=d47zBabWzT85kWnFJsDz_0_884OjwePJAPpa0zo_4oY,167
21
+ tachyon_api/routing/router.py,sha256=xXPkwHkdgfBDk-9RK3ZYBTM1nymT7BBzGuXo1peVyRY,4225
22
+ tachyon_api/routing/routes.py,sha256=-jaIpdP5tRV0JP_n7zkruRq6URWoCRutJRXZbCGWpAE,7802
23
+ tachyon_api/schemas/__init__.py,sha256=3zi89F161Na0fNJI_DG7k3A2GNjqcdi8d2N4JqWHtR4,331
24
+ tachyon_api/schemas/models.py,sha256=9pA5_ddMAcWW2dErAgeyG926NFezfCU68uuhW0qGMs4,2224
25
+ tachyon_api/schemas/parameters.py,sha256=gLGIsFEGr33_G0kakpqOFHRrf86w3egFJAjquZp3Lo0,2810
26
+ tachyon_api/schemas/responses.py,sha256=vFH8RsQxsuOoaSE6hBh1_UhXC_jdFyBdeYyMYDtIuDg,2945
27
+ tachyon_api/utils/__init__.py,sha256=O-GmW5oTbG10TRz2q3NCzAQD3FBZrUn65qf8mywM47M,347
28
+ tachyon_api/utils/type_converter.py,sha256=cV2FXdG6Pz7Eev0sPOF5-63YR4NFRUmuu2KxEwFmm7Y,4159
29
+ tachyon_api/utils/type_utils.py,sha256=Bovpofif-gH6Q-ehmZrauMMHWSypQgiu6tsjHV3UtTU,3351
30
+ tachyon_api-0.6.0.dist-info/LICENSE,sha256=UZXUTSuWBt8V377-2_YE4ug9t6rcBAKrfOVhIBQ8zhk,712
31
+ tachyon_api-0.6.0.dist-info/METADATA,sha256=vMAfI6OES3Jbhu0qWODJks7sMqHIPiKZl2fb4UxQ0Bg,5549
32
+ tachyon_api-0.6.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
33
+ tachyon_api-0.6.0.dist-info/RECORD,,