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.
- tachyon_api/__init__.py +59 -0
- tachyon_api/app.py +699 -0
- tachyon_api/background.py +72 -0
- tachyon_api/cache.py +270 -0
- tachyon_api/cli/__init__.py +9 -0
- tachyon_api/cli/__main__.py +8 -0
- tachyon_api/cli/commands/__init__.py +5 -0
- tachyon_api/cli/commands/generate.py +190 -0
- tachyon_api/cli/commands/lint.py +186 -0
- tachyon_api/cli/commands/new.py +82 -0
- tachyon_api/cli/commands/openapi.py +128 -0
- tachyon_api/cli/main.py +69 -0
- tachyon_api/cli/templates/__init__.py +8 -0
- tachyon_api/cli/templates/project.py +194 -0
- tachyon_api/cli/templates/service.py +330 -0
- tachyon_api/core/__init__.py +12 -0
- tachyon_api/core/lifecycle.py +106 -0
- tachyon_api/core/websocket.py +92 -0
- tachyon_api/di.py +86 -0
- tachyon_api/exceptions.py +39 -0
- tachyon_api/files.py +14 -0
- tachyon_api/middlewares/__init__.py +4 -0
- tachyon_api/middlewares/core.py +40 -0
- tachyon_api/middlewares/cors.py +159 -0
- tachyon_api/middlewares/logger.py +123 -0
- tachyon_api/models.py +73 -0
- tachyon_api/openapi.py +419 -0
- tachyon_api/params.py +268 -0
- tachyon_api/processing/__init__.py +14 -0
- tachyon_api/processing/dependencies.py +172 -0
- tachyon_api/processing/parameters.py +484 -0
- tachyon_api/processing/response_processor.py +93 -0
- tachyon_api/responses.py +92 -0
- tachyon_api/router.py +161 -0
- tachyon_api/security.py +295 -0
- tachyon_api/testing.py +110 -0
- tachyon_api/utils/__init__.py +15 -0
- tachyon_api/utils/type_converter.py +113 -0
- tachyon_api/utils/type_utils.py +162 -0
- tachyon_api-0.9.0.dist-info/METADATA +291 -0
- tachyon_api-0.9.0.dist-info/RECORD +44 -0
- tachyon_api-0.9.0.dist-info/WHEEL +4 -0
- tachyon_api-0.9.0.dist-info/entry_points.txt +3 -0
- tachyon_api-0.9.0.dist-info/licenses/LICENSE +17 -0
tachyon_api/testing.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tachyon Testing Utilities
|
|
3
|
+
|
|
4
|
+
Provides test clients and utilities for testing Tachyon applications.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
from starlette.testclient import TestClient
|
|
9
|
+
from httpx import AsyncClient, ASGITransport
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TachyonTestClient(TestClient):
|
|
13
|
+
"""
|
|
14
|
+
Synchronous test client for Tachyon applications.
|
|
15
|
+
|
|
16
|
+
A wrapper around Starlette's TestClient that provides a convenient
|
|
17
|
+
interface for testing Tachyon applications.
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
from tachyon_api.testing import TachyonTestClient
|
|
21
|
+
|
|
22
|
+
def test_hello():
|
|
23
|
+
app = Tachyon()
|
|
24
|
+
|
|
25
|
+
@app.get("/hello")
|
|
26
|
+
def hello():
|
|
27
|
+
return {"message": "Hello!"}
|
|
28
|
+
|
|
29
|
+
client = TachyonTestClient(app)
|
|
30
|
+
response = client.get("/hello")
|
|
31
|
+
assert response.status_code == 200
|
|
32
|
+
assert response.json() == {"message": "Hello!"}
|
|
33
|
+
|
|
34
|
+
Note:
|
|
35
|
+
For async testing, use AsyncTachyonTestClient instead.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
app,
|
|
41
|
+
base_url: str = "http://test",
|
|
42
|
+
raise_server_exceptions: bool = True,
|
|
43
|
+
**kwargs,
|
|
44
|
+
):
|
|
45
|
+
"""
|
|
46
|
+
Initialize the test client.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
app: The Tachyon application instance
|
|
50
|
+
base_url: Base URL for requests (default: "http://test")
|
|
51
|
+
raise_server_exceptions: Whether to raise exceptions from the app
|
|
52
|
+
**kwargs: Additional arguments passed to TestClient
|
|
53
|
+
"""
|
|
54
|
+
super().__init__(
|
|
55
|
+
app,
|
|
56
|
+
base_url=base_url,
|
|
57
|
+
raise_server_exceptions=raise_server_exceptions,
|
|
58
|
+
**kwargs,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class AsyncTachyonTestClient:
|
|
63
|
+
"""
|
|
64
|
+
Async test client for Tachyon applications.
|
|
65
|
+
|
|
66
|
+
Wraps httpx.AsyncClient with ASGITransport for async testing.
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
from tachyon_api.testing import AsyncTachyonTestClient
|
|
70
|
+
|
|
71
|
+
@pytest.mark.asyncio
|
|
72
|
+
async def test_hello():
|
|
73
|
+
app = Tachyon()
|
|
74
|
+
|
|
75
|
+
@app.get("/hello")
|
|
76
|
+
async def hello():
|
|
77
|
+
return {"message": "Hello!"}
|
|
78
|
+
|
|
79
|
+
async with AsyncTachyonTestClient(app) as client:
|
|
80
|
+
response = await client.get("/hello")
|
|
81
|
+
assert response.status_code == 200
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def __init__(self, app, base_url: str = "http://test", **kwargs):
|
|
85
|
+
"""
|
|
86
|
+
Initialize the async test client.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
app: The Tachyon application instance
|
|
90
|
+
base_url: Base URL for requests (default: "http://test")
|
|
91
|
+
**kwargs: Additional arguments passed to AsyncClient
|
|
92
|
+
"""
|
|
93
|
+
self._app = app
|
|
94
|
+
self._base_url = base_url
|
|
95
|
+
self._kwargs = kwargs
|
|
96
|
+
self._client: Optional[AsyncClient] = None
|
|
97
|
+
|
|
98
|
+
async def __aenter__(self) -> AsyncClient:
|
|
99
|
+
"""Enter async context manager."""
|
|
100
|
+
self._client = AsyncClient(
|
|
101
|
+
transport=ASGITransport(app=self._app),
|
|
102
|
+
base_url=self._base_url,
|
|
103
|
+
**self._kwargs,
|
|
104
|
+
)
|
|
105
|
+
return self._client
|
|
106
|
+
|
|
107
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
108
|
+
"""Exit async context manager."""
|
|
109
|
+
if self._client:
|
|
110
|
+
await self._client.aclose()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tachyon API - Utilities Module
|
|
3
|
+
|
|
4
|
+
This package contains utility functions and classes that provide common functionality
|
|
5
|
+
across the Tachyon framework, including type conversion, validation, and helper functions.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .type_utils import TypeUtils, OPENAPI_TYPE_MAP
|
|
9
|
+
from .type_converter import TypeConverter
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"TypeUtils",
|
|
13
|
+
"TypeConverter",
|
|
14
|
+
"OPENAPI_TYPE_MAP",
|
|
15
|
+
]
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tachyon API - Type Converter
|
|
3
|
+
|
|
4
|
+
This module provides functionality for converting string values to appropriate Python types
|
|
5
|
+
with proper error handling. Used primarily for converting URL parameters and query strings
|
|
6
|
+
to typed values expected by endpoint functions.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Type, Union, Any
|
|
10
|
+
from starlette.responses import JSONResponse
|
|
11
|
+
|
|
12
|
+
from ..responses import validation_error_response
|
|
13
|
+
from .type_utils import TypeUtils
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TypeConverter:
|
|
17
|
+
"""
|
|
18
|
+
Handles conversion of string values to target Python types.
|
|
19
|
+
|
|
20
|
+
This class provides methods to convert string representations of values
|
|
21
|
+
(typically from URL parameters or query strings) to their appropriate
|
|
22
|
+
Python types with comprehensive error handling.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def convert_value(
|
|
27
|
+
value_str: str,
|
|
28
|
+
target_type: Type,
|
|
29
|
+
param_name: str,
|
|
30
|
+
is_path_param: bool = False,
|
|
31
|
+
) -> Union[Any, JSONResponse]:
|
|
32
|
+
"""
|
|
33
|
+
Convert a string value to the target type with appropriate error handling.
|
|
34
|
+
|
|
35
|
+
This method handles type conversion for query and path parameters,
|
|
36
|
+
including special handling for boolean values and proper error responses.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
value_str: The string value to convert
|
|
40
|
+
target_type: The target Python type to convert to
|
|
41
|
+
param_name: Name of the parameter (for error messages)
|
|
42
|
+
is_path_param: Whether this is a path parameter (affects error response)
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
The converted value, or a JSONResponse with appropriate error code
|
|
46
|
+
|
|
47
|
+
Note:
|
|
48
|
+
- Boolean conversion accepts: "true", "1", "t", "yes" (case-insensitive)
|
|
49
|
+
- Path parameter errors return 404, query parameter errors return 422
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
>>> TypeConverter.convert_value("123", int, "limit")
|
|
53
|
+
123
|
|
54
|
+
>>> TypeConverter.convert_value("true", bool, "active")
|
|
55
|
+
True
|
|
56
|
+
>>> TypeConverter.convert_value("invalid", int, "limit")
|
|
57
|
+
JSONResponse({"success": False, "error": "Invalid value for integer conversion", ...})
|
|
58
|
+
"""
|
|
59
|
+
# Unwrap Optional/Union[T, None]
|
|
60
|
+
target_type, _ = TypeUtils.unwrap_optional(target_type)
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
if target_type is bool:
|
|
64
|
+
return value_str.lower() in ("true", "1", "t", "yes")
|
|
65
|
+
elif target_type is not str:
|
|
66
|
+
return target_type(value_str)
|
|
67
|
+
else:
|
|
68
|
+
return value_str
|
|
69
|
+
except (ValueError, TypeError):
|
|
70
|
+
if is_path_param:
|
|
71
|
+
return JSONResponse({"detail": "Not Found"}, status_code=404)
|
|
72
|
+
else:
|
|
73
|
+
type_name = TypeUtils.get_type_name(target_type)
|
|
74
|
+
return validation_error_response(
|
|
75
|
+
f"Invalid value for {type_name} conversion"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def convert_list_values(
|
|
80
|
+
values: list[str], item_type: Type, param_name: str, is_path_param: bool = False
|
|
81
|
+
) -> Union[list[Any], JSONResponse]:
|
|
82
|
+
"""
|
|
83
|
+
Convert a list of string values to the target item type.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
values: List of string values to convert
|
|
87
|
+
item_type: Target type for each item
|
|
88
|
+
param_name: Parameter name for error messages
|
|
89
|
+
is_path_param: Whether this is a path parameter
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
List of converted values or error response
|
|
93
|
+
"""
|
|
94
|
+
base_item_type, item_is_optional = TypeUtils.unwrap_optional(item_type)
|
|
95
|
+
converted_list = []
|
|
96
|
+
|
|
97
|
+
for value_str in values:
|
|
98
|
+
# Handle null/empty values for optional items
|
|
99
|
+
if item_is_optional and (value_str == "" or value_str.lower() == "null"):
|
|
100
|
+
converted_list.append(None)
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
converted_value = TypeConverter.convert_value(
|
|
104
|
+
value_str, base_item_type, param_name, is_path_param
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# If conversion failed, return the error response
|
|
108
|
+
if isinstance(converted_value, JSONResponse):
|
|
109
|
+
return converted_value
|
|
110
|
+
|
|
111
|
+
converted_list.append(converted_value)
|
|
112
|
+
|
|
113
|
+
return converted_list
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tachyon API - Type Utilities
|
|
3
|
+
|
|
4
|
+
This module provides utility functions for working with Python types,
|
|
5
|
+
particularly for handling Optional types, Union types, and generic types
|
|
6
|
+
used throughout the Tachyon framework.
|
|
7
|
+
|
|
8
|
+
This is the centralized module for all type-related utilities to avoid
|
|
9
|
+
code duplication across the framework.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import typing
|
|
13
|
+
from typing import Type, Tuple, Union, Dict, Any
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Centralized type mapping from Python types to OpenAPI schema types
|
|
17
|
+
# Used by openapi.py and app.py for schema generation
|
|
18
|
+
OPENAPI_TYPE_MAP: Dict[Type, str] = {
|
|
19
|
+
int: "integer",
|
|
20
|
+
str: "string",
|
|
21
|
+
bool: "boolean",
|
|
22
|
+
float: "number",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TypeUtils:
|
|
27
|
+
"""
|
|
28
|
+
Utility class for type inspection and manipulation.
|
|
29
|
+
|
|
30
|
+
Provides static methods to analyze Python type annotations,
|
|
31
|
+
particularly useful for handling Optional[T], Union types,
|
|
32
|
+
and generic types in parameter processing.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def unwrap_optional(python_type: Type) -> Tuple[Type, bool]:
|
|
37
|
+
"""
|
|
38
|
+
Unwrap Optional[T] types to get the inner type and optionality flag.
|
|
39
|
+
|
|
40
|
+
This method analyzes a type annotation and determines if it represents
|
|
41
|
+
an Optional type (Union[T, None]). It returns the inner type and a
|
|
42
|
+
boolean indicating whether the type is optional.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
python_type: The type annotation to analyze
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Tuple containing:
|
|
49
|
+
- inner_type: The unwrapped type (T from Optional[T])
|
|
50
|
+
- is_optional: Boolean indicating if the type was Optional
|
|
51
|
+
|
|
52
|
+
Examples:
|
|
53
|
+
>>> TypeUtils.unwrap_optional(Optional[str])
|
|
54
|
+
(str, True)
|
|
55
|
+
>>> TypeUtils.unwrap_optional(str)
|
|
56
|
+
(str, False)
|
|
57
|
+
>>> TypeUtils.unwrap_optional(Union[int, None])
|
|
58
|
+
(int, True)
|
|
59
|
+
"""
|
|
60
|
+
origin = typing.get_origin(python_type)
|
|
61
|
+
args = typing.get_args(python_type)
|
|
62
|
+
|
|
63
|
+
if origin is Union and args:
|
|
64
|
+
non_none = [a for a in args if a is not type(None)] # noqa: E721
|
|
65
|
+
if len(non_none) == 1:
|
|
66
|
+
return non_none[0], True
|
|
67
|
+
|
|
68
|
+
return python_type, False
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def is_list_type(python_type: Type) -> Tuple[bool, Type]:
|
|
72
|
+
"""
|
|
73
|
+
Check if a type is a List type and extract the item type.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
python_type: The type annotation to check
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Tuple containing:
|
|
80
|
+
- is_list: Boolean indicating if the type is a List
|
|
81
|
+
- item_type: The type of list items (str if not a list or no args)
|
|
82
|
+
|
|
83
|
+
Examples:
|
|
84
|
+
>>> TypeUtils.is_list_type(List[str])
|
|
85
|
+
(True, str)
|
|
86
|
+
>>> TypeUtils.is_list_type(str)
|
|
87
|
+
(False, str)
|
|
88
|
+
"""
|
|
89
|
+
origin = typing.get_origin(python_type)
|
|
90
|
+
args = typing.get_args(python_type)
|
|
91
|
+
|
|
92
|
+
if origin in (list, typing.List):
|
|
93
|
+
item_type = args[0] if args else str
|
|
94
|
+
return True, item_type
|
|
95
|
+
|
|
96
|
+
return False, str
|
|
97
|
+
|
|
98
|
+
@staticmethod
|
|
99
|
+
def get_type_name(python_type: Type) -> str:
|
|
100
|
+
"""
|
|
101
|
+
Get a human-readable name for a type.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
python_type: The type to get the name for
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Human-readable type name
|
|
108
|
+
|
|
109
|
+
Examples:
|
|
110
|
+
>>> TypeUtils.get_type_name(int)
|
|
111
|
+
'integer'
|
|
112
|
+
>>> TypeUtils.get_type_name(str)
|
|
113
|
+
'string'
|
|
114
|
+
"""
|
|
115
|
+
return OPENAPI_TYPE_MAP.get(
|
|
116
|
+
python_type, getattr(python_type, "__name__", str(python_type))
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def get_openapi_type(python_type: Type) -> str:
|
|
121
|
+
"""
|
|
122
|
+
Convert Python type to OpenAPI schema type string.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
python_type: The Python type to convert
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
OpenAPI type string ('integer', 'string', 'boolean', 'number')
|
|
129
|
+
|
|
130
|
+
Examples:
|
|
131
|
+
>>> TypeUtils.get_openapi_type(int)
|
|
132
|
+
'integer'
|
|
133
|
+
>>> TypeUtils.get_openapi_type(str)
|
|
134
|
+
'string'
|
|
135
|
+
"""
|
|
136
|
+
return OPENAPI_TYPE_MAP.get(python_type, "string")
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
def get_origin(python_type: Type) -> Any:
|
|
140
|
+
"""
|
|
141
|
+
Get the origin of a generic type (wrapper for typing.get_origin).
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
python_type: The type to get the origin for
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
The origin type or None
|
|
148
|
+
"""
|
|
149
|
+
return typing.get_origin(python_type)
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def get_args(python_type: Type) -> Tuple:
|
|
153
|
+
"""
|
|
154
|
+
Get the arguments of a generic type (wrapper for typing.get_args).
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
python_type: The type to get the arguments for
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
Tuple of type arguments
|
|
161
|
+
"""
|
|
162
|
+
return typing.get_args(python_type)
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tachyon-api
|
|
3
|
+
Version: 0.9.0
|
|
4
|
+
Summary: A lightweight, FastAPI-inspired web framework
|
|
5
|
+
License: GPL-3.0-or-later
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: Juan Manuel Panozzo Zรฉnere
|
|
8
|
+
Author-email: jm.panozzozenere@gmail.com
|
|
9
|
+
Requires-Python: >=3.9,<3.14
|
|
10
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Requires-Dist: msgspec (>=0.19.0,<0.20.0)
|
|
18
|
+
Requires-Dist: orjson (>=3.11.1,<4.0.0)
|
|
19
|
+
Requires-Dist: python-multipart (>=0.0.20,<0.0.21)
|
|
20
|
+
Requires-Dist: ruff (==0.14.9)
|
|
21
|
+
Requires-Dist: starlette (>=0.47.2,<0.48.0)
|
|
22
|
+
Requires-Dist: typer (>=0.16.0,<0.17.0)
|
|
23
|
+
Requires-Dist: uvicorn (>=0.35.0,<0.36.0)
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# ๐ Tachyon API
|
|
27
|
+
|
|
28
|
+

|
|
29
|
+

|
|
30
|
+

|
|
31
|
+

|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
**A lightweight, high-performance API framework for Python with the elegance of FastAPI and the speed of light.**
|
|
35
|
+
|
|
36
|
+
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.
|
|
37
|
+
|
|
38
|
+
## ๐ Quick Start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from tachyon_api import Tachyon, Struct, Body, Query
|
|
42
|
+
|
|
43
|
+
app = Tachyon()
|
|
44
|
+
|
|
45
|
+
class User(Struct):
|
|
46
|
+
name: str
|
|
47
|
+
email: str
|
|
48
|
+
|
|
49
|
+
@app.get("/")
|
|
50
|
+
def hello():
|
|
51
|
+
return {"message": "Tachyon is running at lightspeed!"}
|
|
52
|
+
|
|
53
|
+
@app.post("/users")
|
|
54
|
+
def create_user(user: User = Body(...)):
|
|
55
|
+
return {"created": user.name}
|
|
56
|
+
|
|
57
|
+
@app.get("/search")
|
|
58
|
+
def search(q: str = Query(...), limit: int = Query(10)):
|
|
59
|
+
return {"query": q, "limit": limit}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install tachyon-api
|
|
64
|
+
uvicorn app:app --reload
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
๐ **Docs:** http://localhost:8000/docs
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## โจ Features
|
|
72
|
+
|
|
73
|
+
| Category | Features |
|
|
74
|
+
|----------|----------|
|
|
75
|
+
| **Core** | Decorators API, Routers, Middlewares, ASGI compatible |
|
|
76
|
+
| **Parameters** | Path, Query, Body, Header, Cookie, Form, File |
|
|
77
|
+
| **Validation** | msgspec Struct (ultra-fast), automatic 422 errors |
|
|
78
|
+
| **DI** | `@injectable` (implicit), `Depends()` (explicit) |
|
|
79
|
+
| **Security** | HTTPBearer, HTTPBasic, OAuth2, API Keys |
|
|
80
|
+
| **Async** | Background Tasks, WebSockets |
|
|
81
|
+
| **Performance** | orjson serialization, @cache decorator |
|
|
82
|
+
| **Docs** | OpenAPI 3.0, Scalar UI, Swagger, ReDoc |
|
|
83
|
+
| **CLI** | Project scaffolding, code generation, linting |
|
|
84
|
+
| **Testing** | TachyonTestClient, dependency_overrides |
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## ๐ Documentation
|
|
89
|
+
|
|
90
|
+
Complete documentation is available in the [`docs/`](./docs/) folder:
|
|
91
|
+
|
|
92
|
+
| Guide | Description |
|
|
93
|
+
|-------|-------------|
|
|
94
|
+
| [Getting Started](./docs/01-getting-started.md) | Installation and first project |
|
|
95
|
+
| [Architecture](./docs/02-architecture.md) | Clean architecture patterns |
|
|
96
|
+
| [Dependency Injection](./docs/03-dependency-injection.md) | `@injectable` and `Depends()` |
|
|
97
|
+
| [Parameters](./docs/04-parameters.md) | Path, Query, Body, Header, Cookie, Form, File |
|
|
98
|
+
| [Validation](./docs/05-validation.md) | msgspec Struct validation |
|
|
99
|
+
| [Security](./docs/06-security.md) | JWT, Basic, OAuth2, API Keys |
|
|
100
|
+
| [Caching](./docs/07-caching.md) | `@cache` decorator |
|
|
101
|
+
| [Lifecycle Events](./docs/08-lifecycle.md) | Startup/Shutdown |
|
|
102
|
+
| [Background Tasks](./docs/09-background-tasks.md) | Async task processing |
|
|
103
|
+
| [WebSockets](./docs/10-websockets.md) | Real-time communication |
|
|
104
|
+
| [Testing](./docs/11-testing.md) | TachyonTestClient |
|
|
105
|
+
| [CLI Tools](./docs/12-cli.md) | Scaffolding and generation |
|
|
106
|
+
| [Request Lifecycle](./docs/13-request-lifecycle.md) | How requests are processed |
|
|
107
|
+
| [Migration from FastAPI](./docs/14-migration-fastapi.md) | Migration guide |
|
|
108
|
+
| [Best Practices](./docs/15-best-practices.md) | Recommended patterns |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## ๐ฆ Example: KYC Demo API
|
|
113
|
+
|
|
114
|
+
A complete example demonstrating all Tachyon features is available in [`example/`](./example/):
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
cd example
|
|
118
|
+
pip install -r requirements.txt
|
|
119
|
+
uvicorn example.app:app --reload
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The KYC Demo implements:
|
|
123
|
+
- ๐ JWT Authentication
|
|
124
|
+
- ๐ค Customer CRUD
|
|
125
|
+
- ๐ KYC Verification with Background Tasks
|
|
126
|
+
- ๐ Document Uploads
|
|
127
|
+
- ๐ WebSocket Notifications
|
|
128
|
+
- ๐งช 12 Tests with Mocks
|
|
129
|
+
|
|
130
|
+
**Demo credentials:** `demo@example.com` / `demo123`
|
|
131
|
+
|
|
132
|
+
๐ See [example/README.md](./example/README.md) for full details.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## ๐ Core Dependencies
|
|
137
|
+
|
|
138
|
+
| Package | Purpose |
|
|
139
|
+
|---------|---------|
|
|
140
|
+
| `starlette` | ASGI framework |
|
|
141
|
+
| `msgspec` | Ultra-fast validation/serialization |
|
|
142
|
+
| `orjson` | High-performance JSON |
|
|
143
|
+
| `uvicorn` | ASGI server |
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## ๐ Dependency Injection
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from tachyon_api import injectable, Depends
|
|
151
|
+
|
|
152
|
+
@injectable
|
|
153
|
+
class UserService:
|
|
154
|
+
def get_user(self, id: str):
|
|
155
|
+
return {"id": id}
|
|
156
|
+
|
|
157
|
+
@app.get("/users/{id}")
|
|
158
|
+
def get_user(id: str, service: UserService = Depends()):
|
|
159
|
+
return service.get_user(id)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
๐ [Full DI documentation](./docs/03-dependency-injection.md)
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## ๐ Security
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from tachyon_api.security import HTTPBearer, OAuth2PasswordBearer
|
|
170
|
+
|
|
171
|
+
bearer = HTTPBearer()
|
|
172
|
+
|
|
173
|
+
@app.get("/protected")
|
|
174
|
+
async def protected(credentials = Depends(bearer)):
|
|
175
|
+
return {"token": credentials.credentials}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
๐ [Full Security documentation](./docs/06-security.md)
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## โก Background Tasks
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from tachyon_api.background import BackgroundTasks
|
|
186
|
+
|
|
187
|
+
@app.post("/notify")
|
|
188
|
+
def notify(background_tasks: BackgroundTasks):
|
|
189
|
+
background_tasks.add_task(send_email, "user@example.com")
|
|
190
|
+
return {"status": "queued"}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
๐ [Full Background Tasks documentation](./docs/09-background-tasks.md)
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## ๐ WebSockets
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
@app.websocket("/ws")
|
|
201
|
+
async def websocket(ws):
|
|
202
|
+
await ws.accept()
|
|
203
|
+
data = await ws.receive_text()
|
|
204
|
+
await ws.send_text(f"Echo: {data}")
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
๐ [Full WebSockets documentation](./docs/10-websockets.md)
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## ๐ง CLI Tools
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Create new project
|
|
215
|
+
tachyon new my-api
|
|
216
|
+
|
|
217
|
+
# Generate module
|
|
218
|
+
tachyon generate service users --crud
|
|
219
|
+
|
|
220
|
+
# Code quality
|
|
221
|
+
tachyon lint all
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
๐ [Full CLI documentation](./docs/12-cli.md)
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## ๐งช Testing
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
from tachyon_api.testing import TachyonTestClient
|
|
232
|
+
|
|
233
|
+
def test_hello():
|
|
234
|
+
client = TachyonTestClient(app)
|
|
235
|
+
response = client.get("/")
|
|
236
|
+
assert response.status_code == 200
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
pytest tests/ -v
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
๐ [Full Testing documentation](./docs/11-testing.md)
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## ๐ Why Tachyon?
|
|
248
|
+
|
|
249
|
+
| Feature | Tachyon | FastAPI |
|
|
250
|
+
|---------|---------|---------|
|
|
251
|
+
| **Serialization** | msgspec + orjson | pydantic |
|
|
252
|
+
| **Performance** | โกโกโก Ultra-fast | โก Fast |
|
|
253
|
+
| **Bundle Size** | Minimal | Larger |
|
|
254
|
+
| **Learning Curve** | Easy (FastAPI-like) | Easy |
|
|
255
|
+
| **Type Safety** | Full | Full |
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## ๐ Contributing
|
|
260
|
+
|
|
261
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
262
|
+
|
|
263
|
+
1. Fork the repository
|
|
264
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
265
|
+
3. Run tests (`pytest tests/ -v`)
|
|
266
|
+
4. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
267
|
+
5. Push to the branch (`git push origin feature/amazing-feature`)
|
|
268
|
+
6. Open a Pull Request
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## ๐ License
|
|
273
|
+
|
|
274
|
+
This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](LICENSE) file for details.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## ๐ฎ What's Next
|
|
279
|
+
|
|
280
|
+
See [CHANGELOG.md](./CHANGELOG.md) for version history.
|
|
281
|
+
|
|
282
|
+
Upcoming features:
|
|
283
|
+
- Response streaming
|
|
284
|
+
- GraphQL support
|
|
285
|
+
- More deployment guides
|
|
286
|
+
- Performance benchmarks
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
*Built with ๐ by developers, for developers*
|
|
291
|
+
|