tachyon-api 0.5.10__tar.gz → 0.6.0__tar.gz
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.
- {tachyon_api-0.5.10 → tachyon_api-0.6.0}/PKG-INFO +8 -5
- {tachyon_api-0.5.10 → tachyon_api-0.6.0}/README.md +5 -3
- {tachyon_api-0.5.10 → tachyon_api-0.6.0}/pyproject.toml +2 -2
- {tachyon_api-0.5.10 → tachyon_api-0.6.0}/tachyon_api/__init__.py +6 -6
- tachyon_api-0.6.0/tachyon_api/core/__init__.py +7 -0
- tachyon_api-0.6.0/tachyon_api/core/app.py +355 -0
- tachyon_api-0.6.0/tachyon_api/dependencies/__init__.py +8 -0
- tachyon_api-0.6.0/tachyon_api/dependencies/resolver.py +78 -0
- tachyon_api-0.6.0/tachyon_api/features/__init__.py +30 -0
- tachyon_api-0.6.0/tachyon_api/middlewares/__init__.py +16 -0
- tachyon_api-0.6.0/tachyon_api/middlewares/manager.py +70 -0
- tachyon_api-0.6.0/tachyon_api/openapi/__init__.py +27 -0
- tachyon_api-0.6.0/tachyon_api/openapi/builder.py +315 -0
- tachyon_api-0.5.10/tachyon_api/openapi.py → tachyon_api-0.6.0/tachyon_api/openapi/schema.py +1 -1
- tachyon_api-0.6.0/tachyon_api/processing/__init__.py +8 -0
- tachyon_api-0.6.0/tachyon_api/processing/parameters.py +308 -0
- tachyon_api-0.6.0/tachyon_api/processing/responses.py +144 -0
- tachyon_api-0.6.0/tachyon_api/routing/__init__.py +8 -0
- {tachyon_api-0.5.10/tachyon_api → tachyon_api-0.6.0/tachyon_api/routing}/router.py +1 -1
- tachyon_api-0.6.0/tachyon_api/routing/routes.py +243 -0
- tachyon_api-0.6.0/tachyon_api/schemas/__init__.py +17 -0
- {tachyon_api-0.5.10 → tachyon_api-0.6.0}/tachyon_api/utils/type_converter.py +1 -1
- tachyon_api-0.5.10/tachyon_api/app.py +0 -820
- tachyon_api-0.5.10/tachyon_api/middlewares/__init__.py +0 -4
- {tachyon_api-0.5.10 → tachyon_api-0.6.0}/LICENSE +0 -0
- /tachyon_api-0.5.10/tachyon_api/di.py → /tachyon_api-0.6.0/tachyon_api/dependencies/injection.py +0 -0
- {tachyon_api-0.5.10/tachyon_api → tachyon_api-0.6.0/tachyon_api/features}/cache.py +0 -0
- {tachyon_api-0.5.10 → tachyon_api-0.6.0}/tachyon_api/middlewares/core.py +0 -0
- {tachyon_api-0.5.10 → tachyon_api-0.6.0}/tachyon_api/middlewares/cors.py +0 -0
- {tachyon_api-0.5.10 → tachyon_api-0.6.0}/tachyon_api/middlewares/logger.py +0 -0
- {tachyon_api-0.5.10/tachyon_api → tachyon_api-0.6.0/tachyon_api/schemas}/models.py +0 -0
- /tachyon_api-0.5.10/tachyon_api/params.py → /tachyon_api-0.6.0/tachyon_api/schemas/parameters.py +0 -0
- {tachyon_api-0.5.10/tachyon_api → tachyon_api-0.6.0/tachyon_api/schemas}/responses.py +0 -0
- {tachyon_api-0.5.10 → tachyon_api-0.6.0}/tachyon_api/utils/__init__.py +0 -0
- {tachyon_api-0.5.10 → tachyon_api-0.6.0}/tachyon_api/utils/type_utils.py +0 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: tachyon-api
|
|
3
|
-
Version: 0.
|
|
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
|
|
7
7
|
Author-email: jm.panozzozenere@gmail.com
|
|
8
|
-
Requires-Python: >=3.
|
|
8
|
+
Requires-Python: >=3.9,<3.14
|
|
9
9
|
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -21,7 +22,7 @@ Description-Content-Type: text/markdown
|
|
|
21
22
|
|
|
22
23
|
# 🚀 Tachyon API
|
|
23
24
|
|
|
24
|
-

|
|
25
26
|

|
|
26
27
|

|
|
27
28
|

|
|
@@ -30,9 +31,10 @@ Description-Content-Type: text/markdown
|
|
|
30
31
|
|
|
31
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.
|
|
32
33
|
|
|
34
|
+
**✨ v0.6.0 introduces Starlette-Native Architecture**: Maximum Starlette compatibility for seamless future Rust migration while maintaining all Tachyon features.
|
|
35
|
+
|
|
33
36
|
```python
|
|
34
|
-
from tachyon_api import Tachyon
|
|
35
|
-
from tachyon_api.models import Struct
|
|
37
|
+
from tachyon_api import Tachyon, Struct
|
|
36
38
|
|
|
37
39
|
app = Tachyon()
|
|
38
40
|
|
|
@@ -62,6 +64,7 @@ def create_user(user: User):
|
|
|
62
64
|
- 🧰 Default JSON response (TachyonJSONResponse)
|
|
63
65
|
- 🔒 End-to-end safety: request Body validation + typed response_model
|
|
64
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
|
|
65
68
|
|
|
66
69
|
## 🧪 Test-Driven Development
|
|
67
70
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 🚀 Tachyon API
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|

|
|
5
5
|

|
|
6
6
|

|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
|
|
10
10
|
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.
|
|
11
11
|
|
|
12
|
+
**✨ v0.6.0 introduces Starlette-Native Architecture**: Maximum Starlette compatibility for seamless future Rust migration while maintaining all Tachyon features.
|
|
13
|
+
|
|
12
14
|
```python
|
|
13
|
-
from tachyon_api import Tachyon
|
|
14
|
-
from tachyon_api.models import Struct
|
|
15
|
+
from tachyon_api import Tachyon, Struct
|
|
15
16
|
|
|
16
17
|
app = Tachyon()
|
|
17
18
|
|
|
@@ -41,6 +42,7 @@ def create_user(user: User):
|
|
|
41
42
|
- 🧰 Default JSON response (TachyonJSONResponse)
|
|
42
43
|
- 🔒 End-to-end safety: request Body validation + typed response_model
|
|
43
44
|
- 📘 Deep OpenAPI schemas: nested Structs, Optional/List (nullable/array), formats (uuid, date-time)
|
|
45
|
+
- 🏗️ **Starlette-Native Architecture** (v0.6.0): Maximum compatibility for future Rust migration
|
|
44
46
|
|
|
45
47
|
## 🧪 Test-Driven Development
|
|
46
48
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "tachyon-api"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.0"
|
|
4
4
|
description = "A lightweight, FastAPI-inspired web framework"
|
|
5
5
|
authors = ["Juan Manuel Panozzo Zénere <jm.panozzozenere@gmail.com>"]
|
|
6
6
|
license = "GPL-3.0-or-later"
|
|
7
7
|
readme = "README.md"
|
|
8
8
|
|
|
9
9
|
[tool.poetry.dependencies]
|
|
10
|
-
python = ">=3.
|
|
10
|
+
python = ">=3.9,<3.14"
|
|
11
11
|
starlette = "^0.47.2"
|
|
12
12
|
uvicorn = "^0.35.0"
|
|
13
13
|
msgspec = "^0.19.0"
|
|
@@ -13,12 +13,12 @@ the Free Software Foundation, either version 3 of the License.
|
|
|
13
13
|
For more information, see the documentation and examples.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
from .app import Tachyon
|
|
17
|
-
from .models import Struct
|
|
18
|
-
from .
|
|
19
|
-
from .
|
|
20
|
-
from .router import Router
|
|
21
|
-
from .cache import (
|
|
16
|
+
from .core.app import Tachyon
|
|
17
|
+
from .schemas.models import Struct
|
|
18
|
+
from .schemas.parameters import Query, Body, Path
|
|
19
|
+
from .dependencies.injection import injectable, Depends
|
|
20
|
+
from .routing.router import Router
|
|
21
|
+
from .features.cache import (
|
|
22
22
|
cache,
|
|
23
23
|
CacheConfig,
|
|
24
24
|
create_cache_config,
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tachyon Web Framework - Main Application Module
|
|
3
|
+
|
|
4
|
+
This module contains the core Tachyon class that provides a lightweight,
|
|
5
|
+
FastAPI-inspired web framework with built-in dependency injection,
|
|
6
|
+
parameter validation, and automatic type conversion.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import inspect
|
|
10
|
+
from functools import partial
|
|
11
|
+
from typing import Any, Type, Callable
|
|
12
|
+
|
|
13
|
+
from starlette.applications import Starlette
|
|
14
|
+
from starlette.responses import JSONResponse
|
|
15
|
+
from starlette.routing import Route
|
|
16
|
+
|
|
17
|
+
from ..dependencies.injection import Depends, _registry
|
|
18
|
+
from ..openapi.schema import (
|
|
19
|
+
OpenAPIGenerator,
|
|
20
|
+
OpenAPIConfig,
|
|
21
|
+
create_openapi_config,
|
|
22
|
+
)
|
|
23
|
+
from ..middlewares import create_decorated_middleware_class
|
|
24
|
+
|
|
25
|
+
from ..dependencies.resolver import DependencyResolver
|
|
26
|
+
from ..processing.parameters import ParameterProcessor, _NotProcessed
|
|
27
|
+
from ..processing.responses import ResponseProcessor
|
|
28
|
+
from ..openapi.builder import OpenAPIBuilder
|
|
29
|
+
from ..routing.routes import TachyonRouter
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
from ..features.cache import set_cache_config
|
|
33
|
+
except ImportError:
|
|
34
|
+
set_cache_config = None # type: ignore
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Tachyon:
|
|
38
|
+
"""
|
|
39
|
+
Main Tachyon application class.
|
|
40
|
+
|
|
41
|
+
Provides a web framework with automatic parameter validation, dependency injection,
|
|
42
|
+
and type conversion. Built on top of Starlette for ASGI compatibility.
|
|
43
|
+
|
|
44
|
+
Attributes:
|
|
45
|
+
_router: Internal Starlette application instance
|
|
46
|
+
routes: List of registered routes for introspection
|
|
47
|
+
_instances_cache: Cache for dependency injection singleton instances
|
|
48
|
+
openapi_config: Configuration for OpenAPI documentation
|
|
49
|
+
openapi_generator: Generator for OpenAPI schema and documentation
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, openapi_config: OpenAPIConfig = None, cache_config=None):
|
|
53
|
+
"""
|
|
54
|
+
Initialize a new Tachyon application instance.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
openapi_config: Optional OpenAPI configuration. If not provided,
|
|
58
|
+
uses default configuration similar to FastAPI.
|
|
59
|
+
cache_config: Optional cache configuration (tachyon_api.cache.CacheConfig).
|
|
60
|
+
If provided, it will be set as the active cache configuration.
|
|
61
|
+
"""
|
|
62
|
+
# Create dependency resolver and router
|
|
63
|
+
self._dependency_resolver = DependencyResolver()
|
|
64
|
+
self._tachyon_router = TachyonRouter(self._dependency_resolver)
|
|
65
|
+
|
|
66
|
+
# Create Starlette app with our routes
|
|
67
|
+
self._router = Starlette(routes=self._tachyon_router.get_starlette_routes())
|
|
68
|
+
self.routes = []
|
|
69
|
+
|
|
70
|
+
# Initialize OpenAPI configuration and generator
|
|
71
|
+
self.openapi_config = openapi_config or create_openapi_config()
|
|
72
|
+
self.openapi_generator = OpenAPIGenerator(self.openapi_config)
|
|
73
|
+
self._openapi_builder = OpenAPIBuilder(
|
|
74
|
+
self.openapi_config, self.openapi_generator
|
|
75
|
+
)
|
|
76
|
+
self._docs_setup = False
|
|
77
|
+
|
|
78
|
+
# Apply cache configuration if provided
|
|
79
|
+
self.cache_config = cache_config
|
|
80
|
+
if cache_config is not None and set_cache_config is not None:
|
|
81
|
+
try:
|
|
82
|
+
set_cache_config(cache_config)
|
|
83
|
+
except Exception:
|
|
84
|
+
# Do not break app initialization if cache setup fails
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
# Dynamically create HTTP method decorators (get, post, put, delete, etc.)
|
|
88
|
+
http_methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"]
|
|
89
|
+
|
|
90
|
+
for method in http_methods:
|
|
91
|
+
setattr(
|
|
92
|
+
self,
|
|
93
|
+
method.lower(),
|
|
94
|
+
partial(self._create_decorator, http_method=method),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def _resolve_dependency(self, cls: Type) -> Any:
|
|
98
|
+
"""
|
|
99
|
+
Resolve a dependency using the dependency resolver.
|
|
100
|
+
|
|
101
|
+
This is a convenience method that delegates to the DependencyResolver instance.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
cls: The class type to resolve and instantiate
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
An instance of the requested class with all dependencies resolved
|
|
108
|
+
"""
|
|
109
|
+
return self._dependency_resolver.resolve_dependency(cls)
|
|
110
|
+
|
|
111
|
+
def _add_route(self, path: str, endpoint_func: Callable, method: str, **kwargs):
|
|
112
|
+
"""
|
|
113
|
+
Register a route with the application.
|
|
114
|
+
|
|
115
|
+
This method now uses the Starlette-native TachyonRouter for better
|
|
116
|
+
compatibility with future Starlette/Rust versions.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
path: URL path pattern (e.g., "/users/{user_id}")
|
|
120
|
+
endpoint_func: The endpoint function to handle requests
|
|
121
|
+
method: HTTP method (GET, POST, PUT, DELETE, etc.)
|
|
122
|
+
**kwargs: Additional route metadata
|
|
123
|
+
"""
|
|
124
|
+
# Add route to our Tachyon router
|
|
125
|
+
route = self._tachyon_router.add_route(
|
|
126
|
+
path=path, endpoint=endpoint_func, methods=[method], **kwargs
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Store route info for backward compatibility and OpenAPI generation
|
|
130
|
+
self.routes.append(route.get_endpoint_info())
|
|
131
|
+
|
|
132
|
+
# Generate OpenAPI documentation for this route
|
|
133
|
+
include_in_schema = kwargs.get("include_in_schema", True)
|
|
134
|
+
if include_in_schema:
|
|
135
|
+
self._openapi_builder.generate_openapi_for_route(
|
|
136
|
+
path, method, endpoint_func, **kwargs
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def _create_decorator(self, path: str, *, http_method: str, **kwargs):
|
|
140
|
+
"""
|
|
141
|
+
Create a decorator for the specified HTTP method.
|
|
142
|
+
|
|
143
|
+
This factory method creates method-specific decorators (e.g., @app.get, @app.post)
|
|
144
|
+
that register endpoint functions with the application.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
path: URL path pattern (supports path parameters with {param} syntax)
|
|
148
|
+
http_method: HTTP method name (GET, POST, PUT, DELETE, etc.)
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
A decorator function that registers the endpoint
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
def decorator(endpoint_func: Callable):
|
|
155
|
+
self._add_route(path, endpoint_func, http_method, **kwargs)
|
|
156
|
+
return endpoint_func
|
|
157
|
+
|
|
158
|
+
return decorator
|
|
159
|
+
|
|
160
|
+
def _add_route(self, path: str, endpoint_func: Callable, method: str, **kwargs):
|
|
161
|
+
"""
|
|
162
|
+
Register a route with the application and create an async handler.
|
|
163
|
+
|
|
164
|
+
This is the core method that handles parameter injection, validation, and
|
|
165
|
+
type conversion. It creates an async handler that processes requests and
|
|
166
|
+
automatically injects dependencies, path parameters, query parameters, and
|
|
167
|
+
request body data into the endpoint function.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
path: URL path pattern (e.g., "/users/{user_id}")
|
|
171
|
+
endpoint_func: The endpoint function to handle requests
|
|
172
|
+
method: HTTP method (GET, POST, PUT, DELETE, etc.)
|
|
173
|
+
|
|
174
|
+
Note:
|
|
175
|
+
The created handler processes parameters in the following order:
|
|
176
|
+
1. Dependencies (explicit with Depends() or implicit via @injectable)
|
|
177
|
+
2. Body parameters (JSON request body validated against Struct models)
|
|
178
|
+
3. Query parameters (URL query string with type conversion)
|
|
179
|
+
4. Path parameters (both explicit with Path() and implicit from URL)
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
response_model = kwargs.get("response_model")
|
|
183
|
+
|
|
184
|
+
async def handler(request):
|
|
185
|
+
"""
|
|
186
|
+
Async request handler that processes parameters and calls the endpoint.
|
|
187
|
+
|
|
188
|
+
This handler analyzes the endpoint function signature and automatically
|
|
189
|
+
injects the appropriate values based on parameter annotations and defaults.
|
|
190
|
+
"""
|
|
191
|
+
kwargs_to_inject = {}
|
|
192
|
+
sig = inspect.signature(endpoint_func)
|
|
193
|
+
query_params = request.query_params
|
|
194
|
+
path_params = request.path_params
|
|
195
|
+
_raw_body = None
|
|
196
|
+
|
|
197
|
+
# Process each parameter in the endpoint function signature
|
|
198
|
+
for param in sig.parameters.values():
|
|
199
|
+
# Determine if this parameter is a dependency
|
|
200
|
+
is_explicit_dependency = isinstance(param.default, Depends)
|
|
201
|
+
is_implicit_dependency = (
|
|
202
|
+
param.default is inspect.Parameter.empty
|
|
203
|
+
and param.annotation in _registry
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Process dependencies (explicit and implicit)
|
|
207
|
+
if is_explicit_dependency or is_implicit_dependency:
|
|
208
|
+
target_class = param.annotation
|
|
209
|
+
kwargs_to_inject[param.name] = self._resolve_dependency(
|
|
210
|
+
target_class
|
|
211
|
+
)
|
|
212
|
+
continue
|
|
213
|
+
|
|
214
|
+
# Process other parameter types using ParameterProcessor
|
|
215
|
+
result = await ParameterProcessor.process_parameter(
|
|
216
|
+
param,
|
|
217
|
+
request,
|
|
218
|
+
path_params,
|
|
219
|
+
query_params,
|
|
220
|
+
_raw_body,
|
|
221
|
+
is_explicit_dependency,
|
|
222
|
+
is_implicit_dependency,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# If parameter was processed and returned a value/error, handle it
|
|
226
|
+
if not isinstance(result, _NotProcessed):
|
|
227
|
+
if isinstance(result, JSONResponse):
|
|
228
|
+
return result # Error response
|
|
229
|
+
kwargs_to_inject[param.name] = result
|
|
230
|
+
|
|
231
|
+
# Process the response using ResponseProcessor
|
|
232
|
+
return await ResponseProcessor.process_response(
|
|
233
|
+
endpoint_func, kwargs_to_inject, response_model
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# Register the route with Starlette
|
|
237
|
+
route = Route(path, endpoint=handler, methods=[method])
|
|
238
|
+
self._router.routes.append(route)
|
|
239
|
+
self.routes.append(
|
|
240
|
+
{"path": path, "method": method, "func": endpoint_func, **kwargs}
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Generate OpenAPI documentation for this route
|
|
244
|
+
include_in_schema = kwargs.get("include_in_schema", True)
|
|
245
|
+
if include_in_schema:
|
|
246
|
+
self._openapi_builder.generate_openapi_for_route(
|
|
247
|
+
path, method, endpoint_func, **kwargs
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
def _setup_docs(self):
|
|
251
|
+
"""
|
|
252
|
+
Setup OpenAPI documentation endpoints.
|
|
253
|
+
|
|
254
|
+
This method registers the routes for serving OpenAPI JSON schema,
|
|
255
|
+
Swagger UI, and ReDoc documentation interfaces.
|
|
256
|
+
"""
|
|
257
|
+
if self._docs_setup:
|
|
258
|
+
return
|
|
259
|
+
|
|
260
|
+
self._docs_setup = True
|
|
261
|
+
self._openapi_builder.setup_docs(self)
|
|
262
|
+
|
|
263
|
+
async def __call__(self, scope, receive, send):
|
|
264
|
+
"""
|
|
265
|
+
ASGI application entry point.
|
|
266
|
+
|
|
267
|
+
Delegates request handling to the internal Starlette application.
|
|
268
|
+
This makes Tachyon compatible with ASGI servers like Uvicorn.
|
|
269
|
+
"""
|
|
270
|
+
# Setup documentation endpoints on first request
|
|
271
|
+
if not self._docs_setup:
|
|
272
|
+
self._setup_docs()
|
|
273
|
+
await self._router(scope, receive, send)
|
|
274
|
+
|
|
275
|
+
def include_router(self, router, **kwargs):
|
|
276
|
+
"""
|
|
277
|
+
Include a Router instance in the application.
|
|
278
|
+
|
|
279
|
+
This method registers all routes from the router with the main application,
|
|
280
|
+
applying the router's prefix, tags, and dependencies.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
router: The Router instance to include
|
|
284
|
+
**kwargs: Additional options (currently reserved for future use)
|
|
285
|
+
"""
|
|
286
|
+
from ..routing.router import Router
|
|
287
|
+
|
|
288
|
+
if not isinstance(router, Router):
|
|
289
|
+
raise TypeError("Expected Router instance")
|
|
290
|
+
|
|
291
|
+
# Register all routes from the router
|
|
292
|
+
for route_info in router.routes:
|
|
293
|
+
# Get the full path with prefix
|
|
294
|
+
full_path = router.get_full_path(route_info["path"])
|
|
295
|
+
|
|
296
|
+
# Create a copy of route info with the full path
|
|
297
|
+
route_kwargs = route_info.copy()
|
|
298
|
+
route_kwargs.pop("path", None)
|
|
299
|
+
route_kwargs.pop("method", None)
|
|
300
|
+
route_kwargs.pop("func", None)
|
|
301
|
+
|
|
302
|
+
# Register the route with the main app
|
|
303
|
+
self._add_route(
|
|
304
|
+
full_path, route_info["func"], route_info["method"], **route_kwargs
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
def add_middleware(self, middleware_class, **options):
|
|
308
|
+
"""
|
|
309
|
+
Add a middleware to the Starlette application.
|
|
310
|
+
|
|
311
|
+
This method directly uses Starlette's native middleware API for maximum
|
|
312
|
+
compatibility with future Starlette/Rust versions.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
middleware_class: The middleware class.
|
|
316
|
+
**options: Options to be passed to the middleware constructor.
|
|
317
|
+
"""
|
|
318
|
+
# Use Starlette's native middleware API directly
|
|
319
|
+
self._router.add_middleware(middleware_class, **options)
|
|
320
|
+
|
|
321
|
+
def middleware(self, middleware_type="http"):
|
|
322
|
+
"""
|
|
323
|
+
Decorator for adding a middleware to the application.
|
|
324
|
+
Similar to route decorators (@app.get, etc.)
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
middleware_type: Type of middleware ('http' by default)
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
A decorator that registers the decorated function as middleware.
|
|
331
|
+
"""
|
|
332
|
+
|
|
333
|
+
def decorator(middleware_func):
|
|
334
|
+
# Create a middleware class from the decorated function
|
|
335
|
+
DecoratedMiddleware = create_decorated_middleware_class(
|
|
336
|
+
middleware_func, middleware_type
|
|
337
|
+
)
|
|
338
|
+
# Register the middleware using Starlette's native API
|
|
339
|
+
self.add_middleware(DecoratedMiddleware)
|
|
340
|
+
return middleware_func
|
|
341
|
+
|
|
342
|
+
return decorator
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def middleware_stack(self):
|
|
346
|
+
"""
|
|
347
|
+
Get the current middleware stack for introspection.
|
|
348
|
+
|
|
349
|
+
This property provides access to Starlette's middleware stack for
|
|
350
|
+
debugging and testing purposes.
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
List of middleware classes configured on the Starlette app
|
|
354
|
+
"""
|
|
355
|
+
return getattr(self._router, "user_middleware", [])
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dependency Injection System for Tachyon API
|
|
3
|
+
|
|
4
|
+
This module handles the resolution and injection of dependencies for the Tachyon framework.
|
|
5
|
+
It provides both explicit and implicit dependency injection capabilities.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Type, Dict
|
|
9
|
+
import inspect
|
|
10
|
+
|
|
11
|
+
from .injection import _registry
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DependencyResolver:
|
|
15
|
+
"""
|
|
16
|
+
Handles dependency resolution and injection for the Tachyon framework.
|
|
17
|
+
|
|
18
|
+
This class implements a singleton pattern for dependency caching and supports
|
|
19
|
+
both @injectable decorated classes and simple classes for dependency injection.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self):
|
|
23
|
+
"""Initialize the dependency resolver with an empty cache."""
|
|
24
|
+
self._instances_cache: Dict[Type, Any] = {}
|
|
25
|
+
|
|
26
|
+
def resolve_dependency(self, cls: Type) -> Any:
|
|
27
|
+
"""
|
|
28
|
+
Resolve a dependency and its sub-dependencies recursively.
|
|
29
|
+
|
|
30
|
+
This method implements dependency injection with singleton pattern,
|
|
31
|
+
automatically resolving constructor dependencies and caching instances.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
cls: The class type to resolve and instantiate
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
An instance of the requested class with all dependencies resolved
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
TypeError: If the class cannot be instantiated or is not marked as injectable
|
|
41
|
+
|
|
42
|
+
Note:
|
|
43
|
+
- Uses singleton pattern - instances are cached and reused
|
|
44
|
+
- Supports both @injectable decorated classes and simple classes
|
|
45
|
+
- Recursively resolves constructor dependencies
|
|
46
|
+
"""
|
|
47
|
+
# Return cached instance if available (singleton pattern)
|
|
48
|
+
if cls in self._instances_cache:
|
|
49
|
+
return self._instances_cache[cls]
|
|
50
|
+
|
|
51
|
+
# For non-injectable classes, try to create without arguments
|
|
52
|
+
if cls not in _registry:
|
|
53
|
+
try:
|
|
54
|
+
# Works for classes without __init__ or with no-arg __init__
|
|
55
|
+
return cls()
|
|
56
|
+
except TypeError:
|
|
57
|
+
raise TypeError(
|
|
58
|
+
f"Cannot resolve dependency '{cls.__name__}'. "
|
|
59
|
+
f"Did you forget to mark it with @injectable?"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# For injectable classes, resolve constructor dependencies
|
|
63
|
+
sig = inspect.signature(cls)
|
|
64
|
+
dependencies = {}
|
|
65
|
+
|
|
66
|
+
# Recursively resolve each constructor parameter
|
|
67
|
+
for param in sig.parameters.values():
|
|
68
|
+
if param.name != "self":
|
|
69
|
+
dependencies[param.name] = self.resolve_dependency(param.annotation)
|
|
70
|
+
|
|
71
|
+
# Create instance with resolved dependencies and cache it
|
|
72
|
+
instance = cls(**dependencies)
|
|
73
|
+
self._instances_cache[cls] = instance
|
|
74
|
+
return instance
|
|
75
|
+
|
|
76
|
+
def clear_cache(self):
|
|
77
|
+
"""Clear the dependency cache. Useful for testing."""
|
|
78
|
+
self._instances_cache.clear()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tachyon API features and extensions.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
from .cache import (
|
|
7
|
+
cache,
|
|
8
|
+
CacheConfig,
|
|
9
|
+
create_cache_config,
|
|
10
|
+
set_cache_config,
|
|
11
|
+
get_cache_config,
|
|
12
|
+
InMemoryCacheBackend,
|
|
13
|
+
BaseCacheBackend,
|
|
14
|
+
RedisCacheBackend,
|
|
15
|
+
MemcachedCacheBackend,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"cache",
|
|
20
|
+
"CacheConfig",
|
|
21
|
+
"create_cache_config",
|
|
22
|
+
"set_cache_config",
|
|
23
|
+
"get_cache_config",
|
|
24
|
+
"InMemoryCacheBackend",
|
|
25
|
+
"BaseCacheBackend",
|
|
26
|
+
"RedisCacheBackend",
|
|
27
|
+
"MemcachedCacheBackend",
|
|
28
|
+
]
|
|
29
|
+
except ImportError:
|
|
30
|
+
__all__ = []
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tachyon API middleware system.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .manager import StarletteMiddlewareManager, MiddlewareManager
|
|
6
|
+
from .core import create_decorated_middleware_class
|
|
7
|
+
from .cors import CORSMiddleware
|
|
8
|
+
from .logger import LoggerMiddleware
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"StarletteMiddlewareManager",
|
|
12
|
+
"MiddlewareManager",
|
|
13
|
+
"create_decorated_middleware_class",
|
|
14
|
+
"CORSMiddleware",
|
|
15
|
+
"LoggerMiddleware",
|
|
16
|
+
]
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Starlette-Native Middleware Management for Tachyon API
|
|
3
|
+
|
|
4
|
+
This module provides middleware management that closely follows Starlette's patterns
|
|
5
|
+
while maintaining backward compatibility with existing Tachyon functionality.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Type
|
|
9
|
+
from starlette.applications import Starlette
|
|
10
|
+
|
|
11
|
+
from .core import create_decorated_middleware_class
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class StarletteMiddlewareManager:
|
|
15
|
+
"""
|
|
16
|
+
Starlette-native middleware manager that follows Starlette's patterns.
|
|
17
|
+
|
|
18
|
+
This manager provides a thin wrapper around Starlette's middleware system
|
|
19
|
+
while maintaining compatibility with Tachyon's decorator-based middleware.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, router: Starlette):
|
|
23
|
+
"""
|
|
24
|
+
Initialize the middleware manager.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
router: The Starlette application instance to apply middlewares to
|
|
28
|
+
"""
|
|
29
|
+
self._router = router
|
|
30
|
+
|
|
31
|
+
def add_middleware(self, middleware_class: Type, **options):
|
|
32
|
+
"""
|
|
33
|
+
Add a middleware to the Starlette application.
|
|
34
|
+
|
|
35
|
+
This method directly uses Starlette's middleware API for maximum compatibility
|
|
36
|
+
with future Starlette/Rust versions.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
middleware_class: The middleware class.
|
|
40
|
+
**options: Options to be passed to the middleware constructor.
|
|
41
|
+
"""
|
|
42
|
+
# Use Starlette's native middleware API
|
|
43
|
+
self._router.add_middleware(middleware_class, **options)
|
|
44
|
+
|
|
45
|
+
def middleware(self, middleware_type: str = "http"):
|
|
46
|
+
"""
|
|
47
|
+
Decorator for adding a middleware to the application.
|
|
48
|
+
Similar to route decorators (@app.get, etc.)
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
middleware_type: Type of middleware ('http' by default)
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
A decorator that registers the decorated function as middleware.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def decorator(middleware_func):
|
|
58
|
+
# Create a middleware class from the decorated function
|
|
59
|
+
DecoratedMiddleware = create_decorated_middleware_class(
|
|
60
|
+
middleware_func, middleware_type
|
|
61
|
+
)
|
|
62
|
+
# Register the middleware using Starlette's native API
|
|
63
|
+
self.add_middleware(DecoratedMiddleware)
|
|
64
|
+
return middleware_func
|
|
65
|
+
|
|
66
|
+
return decorator
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# Backward compatibility alias
|
|
70
|
+
MiddlewareManager = StarletteMiddlewareManager
|