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.

tachyon_api/__init__.py CHANGED
@@ -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 .params import Query, Body, Path
19
- from .di import injectable, Depends
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,7 @@
1
+ """
2
+ Core Tachyon API components.
3
+ """
4
+
5
+ from .app import Tachyon
6
+
7
+ __all__ = ["Tachyon"]
@@ -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,8 @@
1
+ """
2
+ Tachyon API dependency injection system.
3
+ """
4
+
5
+ from .injection import injectable, Depends, _registry
6
+ from .resolver import DependencyResolver
7
+
8
+ __all__ = ["injectable", "Depends", "_registry", "DependencyResolver"]
@@ -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__ = []
@@ -1,4 +1,16 @@
1
+ """
2
+ Tachyon API middleware system.
3
+ """
4
+
5
+ from .manager import StarletteMiddlewareManager, MiddlewareManager
6
+ from .core import create_decorated_middleware_class
1
7
  from .cors import CORSMiddleware
2
8
  from .logger import LoggerMiddleware
3
9
 
4
- __all__ = ["CORSMiddleware", "LoggerMiddleware"]
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
@@ -0,0 +1,27 @@
1
+ """
2
+ Tachyon API OpenAPI documentation system.
3
+ """
4
+
5
+ from .builder import OpenAPIBuilder
6
+ from .schema import (
7
+ OpenAPIGenerator,
8
+ OpenAPIConfig,
9
+ create_openapi_config,
10
+ build_components_for_struct,
11
+ Info,
12
+ Contact,
13
+ License,
14
+ Server,
15
+ )
16
+
17
+ __all__ = [
18
+ "OpenAPIBuilder",
19
+ "OpenAPIGenerator",
20
+ "OpenAPIConfig",
21
+ "create_openapi_config",
22
+ "build_components_for_struct",
23
+ "Info",
24
+ "Contact",
25
+ "License",
26
+ "Server",
27
+ ]