tachyon-api 1.2.0__tar.gz → 1.3.1__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.
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/PKG-INFO +163 -58
- tachyon_api-1.3.1/README.md +436 -0
- tachyon_api-1.3.1/pyproject.toml +78 -0
- tachyon_api-1.3.1/setup.py +59 -0
- tachyon_api-1.3.1/tachyon_api/app/__init__.py +234 -0
- tachyon_api-1.3.1/tachyon_api/app/_asgi_entry.py +33 -0
- tachyon_api-1.3.1/tachyon_api/app/_asgi_handler.py +14 -0
- tachyon_api-1.3.1/tachyon_api/app/_docs_routes.py +43 -0
- tachyon_api-1.3.1/tachyon_api/app/_docs_schemas.py +43 -0
- tachyon_api-1.3.1/tachyon_api/app/_error_static.py +24 -0
- tachyon_api-1.3.1/tachyon_api/app/_exception_table.py +66 -0
- tachyon_api-1.3.1/tachyon_api/app/_exception_table.pyx +74 -0
- tachyon_api-1.3.1/tachyon_api/app/_fast_asgi_factory.py +61 -0
- tachyon_api-1.3.1/tachyon_api/app/_handler_factory.py +69 -0
- tachyon_api-1.3.1/tachyon_api/app/_http_dispatch.py +20 -0
- tachyon_api-1.3.1/tachyon_api/app/_mw_stack.py +27 -0
- tachyon_api-1.3.1/tachyon_api/app/_registry.py +20 -0
- tachyon_api-1.3.1/tachyon_api/app/_route_installer.py +41 -0
- tachyon_api-1.3.1/tachyon_api/cli/commands/__init__.py +1 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/commands/run.py +0 -2
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/commands/skill.py +3 -3
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/templates/ai_skill.py +3 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/templates/project.py +1 -1
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/core/websocket.py +3 -1
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/models.py +9 -7
- tachyon_api-1.3.1/tachyon_api/openapi/__init__.py +32 -0
- tachyon_api-1.3.1/tachyon_api/openapi/_config.py +158 -0
- tachyon_api-1.3.1/tachyon_api/openapi/_generator.py +58 -0
- tachyon_api-1.3.1/tachyon_api/openapi/_html_renderers.py +129 -0
- tachyon_api-1.3.1/tachyon_api/openapi/_param_schemas.py +48 -0
- tachyon_api-1.3.1/tachyon_api/openapi/_route_builder.py +174 -0
- tachyon_api-1.3.1/tachyon_api/openapi/_struct_schemas.py +81 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/__init__.py +25 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/_missing.py +16 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/_missing.pyx +19 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/body.pxd +15 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/body.py +61 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/body.pyx +60 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/body_limit.pxd +10 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/body_limit.py +36 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/body_limit.pyx +38 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/cookie.pxd +6 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/cookie.py +18 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/cookie.pyx +14 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/file.pxd +6 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/file.py +26 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/file.pyx +23 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/form.pxd +6 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/form.py +17 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/form.pyx +14 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/header.pxd +12 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/header.py +18 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/header.pyx +18 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/path.pxd +6 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/path.py +40 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/path.pyx +91 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/query.pxd +6 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/query.py +26 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/query.pyx +80 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/query_list.pxd +6 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/query_list.py +53 -0
- tachyon_api-1.3.1/tachyon_api/processing/_extractors/query_list.pyx +54 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/processing/compiler.py +18 -19
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/processing/compiler.pyx +26 -18
- tachyon_api-1.3.1/tachyon_api/processing/dependencies/__init__.py +5 -0
- tachyon_api-1.3.1/tachyon_api/processing/dependencies/_callable_factory.py +55 -0
- tachyon_api-1.3.1/tachyon_api/processing/dependencies/_circular_detector.py +23 -0
- tachyon_api-1.3.1/tachyon_api/processing/dependencies/_circular_detector.pyx +25 -0
- tachyon_api-1.3.1/tachyon_api/processing/dependencies/_class_factory.py +32 -0
- tachyon_api-1.3.1/tachyon_api/processing/dependencies/_class_factory.pyx +35 -0
- tachyon_api-1.3.1/tachyon_api/processing/dependencies/_override_lookup.py +30 -0
- tachyon_api-1.3.1/tachyon_api/processing/dependencies/_override_lookup.pyx +31 -0
- tachyon_api-1.3.1/tachyon_api/processing/dependencies/_resolver.py +97 -0
- tachyon_api-1.3.1/tachyon_api/processing/dependencies/_resolver.pyx +85 -0
- tachyon_api-1.3.1/tachyon_api/processing/dependencies/_scope_cache.py +45 -0
- tachyon_api-1.3.1/tachyon_api/processing/dependencies/_scope_cache.pyx +36 -0
- tachyon_api-1.3.1/tachyon_api/processing/dependencies/_sig_cache.py +20 -0
- tachyon_api-1.3.1/tachyon_api/processing/parameters.py +145 -0
- tachyon_api-1.3.1/tachyon_api/processing/parameters.pyx +186 -0
- tachyon_api-1.3.1/tachyon_api/responses/__init__.py +44 -0
- tachyon_api-1.3.1/tachyon_api/responses/_bytes_response.py +32 -0
- tachyon_api-1.3.1/tachyon_api/responses/_bytes_response.pyx +41 -0
- tachyon_api-1.3.1/tachyon_api/responses/_caches.py +29 -0
- tachyon_api-1.3.1/tachyon_api/responses/_constants.py +9 -0
- tachyon_api-1.3.1/tachyon_api/responses/_error.py +25 -0
- tachyon_api-1.3.1/tachyon_api/responses/_internal_error.py +51 -0
- tachyon_api-1.3.1/tachyon_api/responses/_internal_error.pyx +53 -0
- tachyon_api-1.3.1/tachyon_api/responses/_json_response.py +41 -0
- tachyon_api-1.3.1/tachyon_api/responses/_json_response.pyx +52 -0
- tachyon_api-1.3.1/tachyon_api/responses/_success.py +10 -0
- tachyon_api-1.3.1/tachyon_api/responses/_validation.py +33 -0
- tachyon_api-1.3.1/tachyon_api/responses/_wire.py +26 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/routing/trie.py +1 -1
- tachyon_api-1.3.1/tachyon_api/security/__init__.py +18 -0
- tachyon_api-1.3.1/tachyon_api/security/_api_keys.py +63 -0
- tachyon_api-1.3.1/tachyon_api/security/_bearer_parser.py +16 -0
- tachyon_api-1.3.1/tachyon_api/security/_bearer_parser.pyx +29 -0
- tachyon_api-1.3.1/tachyon_api/security/_credentials.py +21 -0
- tachyon_api-1.3.1/tachyon_api/security/_http_basic.py +50 -0
- tachyon_api-1.3.1/tachyon_api/security/_http_bearer.py +29 -0
- tachyon_api-1.3.1/tachyon_api/security/_oauth2_bearer.py +32 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/server.py +0 -1
- tachyon_api-1.2.0/README.md +0 -337
- tachyon_api-1.2.0/pyproject.toml +0 -46
- tachyon_api-1.2.0/setup.py +0 -78
- tachyon_api-1.2.0/tachyon_api/_server_fast.c +0 -9232
- tachyon_api-1.2.0/tachyon_api/app.py +0 -477
- tachyon_api-1.2.0/tachyon_api/cli/commands/__init__.py +0 -5
- tachyon_api-1.2.0/tachyon_api/openapi.py +0 -500
- tachyon_api-1.2.0/tachyon_api/processing/dependencies.py +0 -132
- tachyon_api-1.2.0/tachyon_api/processing/parameters.py +0 -266
- tachyon_api-1.2.0/tachyon_api/processing/parameters.pyx +0 -351
- tachyon_api-1.2.0/tachyon_api/responses.py +0 -209
- tachyon_api-1.2.0/tachyon_api/security.py +0 -169
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/LICENSE +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/MANIFEST.in +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/__init__.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/_server_fast.pyx +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/background.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cache.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/__init__.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/__main__.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/commands/generate.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/commands/lint.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/commands/new.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/commands/openapi.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/commands/routes.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/main.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/templates/__init__.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/templates/service.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/cli/utils.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/core/__init__.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/core/lifecycle.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/di.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/exceptions.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/files.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/middlewares/__init__.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/middlewares/core.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/middlewares/cors.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/middlewares/logger.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/middlewares/security_headers.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/params.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/processing/__init__.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/processing/dispatch.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/processing/dispatch.pyx +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/processing/response_processor.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/processing/response_processor.pyx +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/processing/scope.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/processing/scope.pyx +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/router.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/routing/__init__.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/routing/trie.pyx +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/testing.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/utils/__init__.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/utils/type_converter.py +0 -0
- {tachyon_api-1.2.0 → tachyon_api-1.3.1}/tachyon_api/utils/type_utils.py +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: tachyon-api
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.1
|
|
4
4
|
Summary: A lightweight, FastAPI-inspired web framework
|
|
5
5
|
License: GPL-3.0-or-later
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Author: Juan Manuel Panozzo Zénere
|
|
7
8
|
Author-email: jm.panozzozenere@gmail.com
|
|
8
9
|
Requires-Python: >=3.10,<4.0
|
|
@@ -12,21 +13,26 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
12
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Provides-Extra: benchmark
|
|
15
18
|
Provides-Extra: fast
|
|
19
|
+
Requires-Dist: fastapi (>=0.136.1,<0.137.0) ; extra == "benchmark"
|
|
20
|
+
Requires-Dist: httptools (>=0.6.0)
|
|
16
21
|
Requires-Dist: msgspec (>=0.19.0,<0.20.0)
|
|
17
22
|
Requires-Dist: orjson (>=3.11.1,<4.0.0)
|
|
18
|
-
Requires-Dist:
|
|
19
|
-
Requires-Dist:
|
|
23
|
+
Requires-Dist: pydantic (>=2.13.4,<3.0.0) ; extra == "benchmark"
|
|
24
|
+
Requires-Dist: python-multipart (>=0.0.32,<0.0.33)
|
|
25
|
+
Requires-Dist: starlette (>=1.3.1,<2.0.0)
|
|
20
26
|
Requires-Dist: typer (>=0.16.0,<0.17.0)
|
|
21
27
|
Requires-Dist: uvicorn (>=0.35.0,<0.36.0)
|
|
22
28
|
Description-Content-Type: text/markdown
|
|
23
29
|
|
|
24
30
|
# 🚀 Tachyon API
|
|
25
31
|
|
|
26
|
-

|
|
27
33
|

|
|
28
34
|

|
|
29
|
-

|
|
30
36
|

|
|
31
37
|
|
|
32
38
|
**A lightweight, high-performance API framework for Python with the elegance of FastAPI and the speed of light.**
|
|
@@ -59,7 +65,7 @@ def search(q: str = Query(...), limit: int = Query(10)):
|
|
|
59
65
|
|
|
60
66
|
```bash
|
|
61
67
|
pip install tachyon-api
|
|
62
|
-
|
|
68
|
+
tachyon run # uvloop + httptools + reload, served at :8000
|
|
63
69
|
```
|
|
64
70
|
|
|
65
71
|
📖 **Docs:** http://localhost:8000/docs
|
|
@@ -68,37 +74,55 @@ uvicorn app:app --reload
|
|
|
68
74
|
|
|
69
75
|
## ⚡ Performance
|
|
70
76
|
|
|
71
|
-
Benchmarked against **FastAPI 0.136.1 (Pydantic v2)** · 1 worker · 100 concurrent connections · uvloop + httptools · Cython extensions
|
|
77
|
+
Benchmarked against **FastAPI 0.136.1 (Pydantic v2)** · 1 worker · 100 concurrent connections · uvloop + httptools · precompiled Cython extensions (shipped by default — see below)
|
|
72
78
|
|
|
73
79
|
| Scenario | FastAPI | Tachyon | Speedup |
|
|
74
80
|
|---|---:|---:|---:|
|
|
75
|
-
| Hello World | 10,
|
|
76
|
-
| Path + query params | 7,
|
|
77
|
-
| Body validation (Struct) | 8,
|
|
78
|
-
| Nested body (complex Struct) | 8,
|
|
79
|
-
| Response model serialization | 6,
|
|
80
|
-
| Header param + auth | 8,
|
|
81
|
-
| Dependency injection | 6,
|
|
82
|
-
| Multiple query params | 6,
|
|
83
|
-
| **Total throughput** | **
|
|
81
|
+
| Hello World | 10,314 req/s | **49,755 req/s** | **4.82x** |
|
|
82
|
+
| Path + query params | 7,166 req/s | **37,598 req/s** | **5.25x** |
|
|
83
|
+
| Body validation (Struct) | 8,371 req/s | **40,916 req/s** | **4.89x** |
|
|
84
|
+
| Nested body (complex Struct) | 8,027 req/s | **39,994 req/s** | **4.98x** |
|
|
85
|
+
| Response model serialization | 6,343 req/s | **47,561 req/s** | **7.50x** |
|
|
86
|
+
| Header param + auth | 8,701 req/s | **45,415 req/s** | **5.22x** |
|
|
87
|
+
| Dependency injection | 6,449 req/s | **45,610 req/s** | **7.07x** |
|
|
88
|
+
| Multiple query params | 6,264 req/s | **34,111 req/s** | **5.45x** |
|
|
89
|
+
| **Total throughput** | **61,635 req/s** | **340,960 req/s** | **5.53x** |
|
|
84
90
|
|
|
85
91
|
**Latency:** ~2.3ms (Tachyon) vs ~13ms (FastAPI) on average.
|
|
86
92
|
|
|
87
93
|
> Benchmark code in [`benchmark/`](./benchmark/). Run with `bash benchmark/run_benchmark.sh`.
|
|
88
94
|
|
|
89
|
-
###
|
|
95
|
+
### Precompiled wheels — zero setup
|
|
90
96
|
|
|
91
|
-
|
|
97
|
+
`pip install tachyon-api` ships **prebuilt wheels with all 27 Cython extensions already compiled** for:
|
|
92
98
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
| Platform | Architectures | CPython |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| Linux | x86_64, aarch64 | 3.10 · 3.11 · 3.12 · 3.13 |
|
|
102
|
+
| macOS | arm64 (Apple Silicon) | 3.10 · 3.11 · 3.12 · 3.13 |
|
|
103
|
+
| Windows | x86_64 | 3.10 · 3.11 · 3.12 · 3.13 |
|
|
104
|
+
|
|
105
|
+
No manual build step. No Cython required. The numbers above are what you get out of the box.
|
|
106
|
+
|
|
107
|
+
> **Not on the list?** (macOS Intel, Alpine, etc.) `pip` falls back to the sdist and compiles from `.pyx` source — requires a C compiler and `pip install tachyon-api[fast]` (which pulls in Cython). If compilation fails, the framework still works: runtime falls back to the pure-Python siblings of every `.pyx` module automatically.
|
|
97
108
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
109
|
+
### Compiled vs pure-Python delta
|
|
110
|
+
|
|
111
|
+
Same code, same workload — the only difference is whether the 27 Cython `.so` extensions are loaded:
|
|
112
|
+
|
|
113
|
+
| Scenario | Compiled | Pure-Python | Δ |
|
|
114
|
+
|---|---:|---:|---:|
|
|
115
|
+
| Hello World | 49,755 req/s | 47,899 req/s | +3.9% |
|
|
116
|
+
| Path + query params | 37,598 req/s | 31,901 req/s | **+17.9%** |
|
|
117
|
+
| Body — simple Struct | 40,916 req/s | 35,623 req/s | **+14.9%** |
|
|
118
|
+
| Body — nested Struct | 39,994 req/s | 35,204 req/s | **+13.6%** |
|
|
119
|
+
| Response model | 47,561 req/s | 40,604 req/s | **+17.1%** |
|
|
120
|
+
| Header param + auth | 45,415 req/s | 39,380 req/s | **+15.3%** |
|
|
121
|
+
| **Dependency injection** | 45,610 req/s | 38,552 req/s | **+18.3%** |
|
|
122
|
+
| Multiple query params | 34,111 req/s | 29,803 req/s | **+14.5%** |
|
|
123
|
+
| **TOTAL** | **340,960 req/s** | **298,966 req/s** | **+14.0%** |
|
|
124
|
+
|
|
125
|
+
The biggest wins concentrate on real framework work — DI resolution (+18.3%), path/query parsing (+17.9%), response model (+17.1%), and validation (+13–15%). Hello-world barely moves (+3.9%): the framework is already a tiny slice of that request.
|
|
102
126
|
|
|
103
127
|
### Why is Tachyon faster?
|
|
104
128
|
|
|
@@ -118,15 +142,16 @@ python setup.py build_ext --inplace # compile extensions (required step)
|
|
|
118
142
|
| Category | Features |
|
|
119
143
|
|----------|----------|
|
|
120
144
|
| **Core** | Decorators API, Routers, Middlewares, ASGI compatible |
|
|
121
|
-
| **Parameters** | Path, Query, Body, Header, Cookie, Form, File (all with `alias=`) |
|
|
122
|
-
| **Validation** | msgspec Struct (ultra-fast), automatic 422 errors, body size limit |
|
|
123
|
-
| **DI** | `@injectable` (
|
|
124
|
-
| **Security** | HTTPBearer, HTTPBasic, OAuth2, API Keys |
|
|
125
|
-
| **Async** | Background Tasks, WebSockets |
|
|
126
|
-
| **Performance** | orjson serialization, `@cache` decorator, endpoint pre-compilation |
|
|
127
|
-
| **Docs** | OpenAPI 3.0, Scalar UI, Swagger, ReDoc (XSS-safe HTML generation) |
|
|
128
|
-
| **CLI** | Project scaffolding, code generation, linting |
|
|
129
|
-
| **Testing** | `TachyonTestClient
|
|
145
|
+
| **Parameters** | Path, Query, Body (incl. `Body(List[Struct])`), Header, Cookie, Form, File (all with `alias=`) |
|
|
146
|
+
| **Validation** | msgspec Struct (ultra-fast), automatic 422 errors, configurable body size limit (default 2 MB) |
|
|
147
|
+
| **DI** | `@injectable` (3 scopes: singleton / **request** / **transient**), `Depends()` (sync + async), circular dep detection |
|
|
148
|
+
| **Security** | HTTPBearer, HTTPBasic, OAuth2, API Keys (Header / Query / Cookie), `SecurityHeadersMiddleware` (X-Frame-Options, CSP, HSTS, …) |
|
|
149
|
+
| **Async** | Background Tasks (failures logged, not silenced), WebSockets with **typed path params + DI** |
|
|
150
|
+
| **Performance** | orjson serialization, `@cache` decorator, endpoint pre-compilation, 27 precompiled Cython extensions (shipped by default) |
|
|
151
|
+
| **Docs** | OpenAPI 3.0 (incl. `List[Struct]` arrays + `multipart/form-data`), Scalar UI, Swagger, ReDoc (XSS-safe HTML generation) |
|
|
152
|
+
| **CLI** | Project scaffolding, code generation, linting, AI-agent skill installer |
|
|
153
|
+
| **Testing** | `TachyonTestClient` (sync), `create_client()` (async, full httpx kwargs), `dependency_overrides` |
|
|
154
|
+
| **Architecture** | Atomic SRP modules across `app/`, `processing/`, `responses/`, `openapi/`, `security/` — 27 compiled to `.so` for the hot path (v1.2.x refactor + v1.2.9 Cython sprint) |
|
|
130
155
|
|
|
131
156
|
---
|
|
132
157
|
|
|
@@ -149,6 +174,7 @@ python setup.py build_ext --inplace # compile extensions (required step)
|
|
|
149
174
|
| [Request Lifecycle](./docs/13-request-lifecycle.md) | How requests are processed |
|
|
150
175
|
| [Migration from FastAPI](./docs/14-migration-fastapi.md) | Migration guide |
|
|
151
176
|
| [Best Practices](./docs/15-best-practices.md) | Recommended patterns |
|
|
177
|
+
| [Cython Build](./docs/16-cython-build.md) | Precompiled wheels, source builds, and the `.py`/`.pyx` fallback model |
|
|
152
178
|
|
|
153
179
|
---
|
|
154
180
|
|
|
@@ -159,16 +185,19 @@ A complete example demonstrating all Tachyon features is available in [`example/
|
|
|
159
185
|
```bash
|
|
160
186
|
cd example
|
|
161
187
|
pip install -r requirements.txt
|
|
162
|
-
|
|
188
|
+
tachyon run example.app:app
|
|
163
189
|
```
|
|
164
190
|
|
|
165
|
-
The KYC Demo
|
|
166
|
-
- 🔐 JWT Authentication
|
|
167
|
-
- 👤 Customer CRUD
|
|
191
|
+
The KYC Demo exercises every v1.2.x feature:
|
|
192
|
+
- 🔐 JWT Authentication + API Keys
|
|
193
|
+
- 👤 Customer CRUD + bulk endpoint (`Body(List[Struct])`)
|
|
168
194
|
- 📋 KYC Verification with Background Tasks
|
|
169
|
-
- 📁 Document Uploads
|
|
170
|
-
- 🌐 WebSocket
|
|
171
|
-
-
|
|
195
|
+
- 📁 Document Uploads (`multipart/form-data`)
|
|
196
|
+
- 🌐 WebSocket — legacy plain-string + modern DI-injected with `room_id: uuid.UUID`
|
|
197
|
+
- 💉 DI scopes — `singleton` (services), `request` (correlation context), `transient` (ID generator)
|
|
198
|
+
- 🛡️ Security headers + opt-in CORS allow-list
|
|
199
|
+
- 🚨 Custom exception handler for the `KYCException` hierarchy
|
|
200
|
+
- 🧪 17 tests (`pytest example/tests/`), including async tests via `create_client`
|
|
172
201
|
|
|
173
202
|
**Demo credentials:** `demo@example.com` / `demo123`
|
|
174
203
|
|
|
@@ -187,19 +216,71 @@ The KYC Demo implements:
|
|
|
187
216
|
|
|
188
217
|
---
|
|
189
218
|
|
|
219
|
+
## 🏛️ Architecture
|
|
220
|
+
|
|
221
|
+
Tachyon's request hot path is a thin chain of composed collaborators — every
|
|
222
|
+
piece is single-responsibility and ready for Cython compilation:
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
client
|
|
226
|
+
│
|
|
227
|
+
├─→ Tachyon.__call__ (ASGI entry — sets scope["app"])
|
|
228
|
+
│ │
|
|
229
|
+
│ ├─→ ASGIEntry lazy build of HTTP app
|
|
230
|
+
│ │
|
|
231
|
+
│ ├─→ HTTPDispatcher HTTP → trie · WS/lifespan → Starlette
|
|
232
|
+
│ │
|
|
233
|
+
│ ├─→ MiddlewareStack user-registered middlewares (CORS, Security…)
|
|
234
|
+
│ │
|
|
235
|
+
│ └─→ TachyonDispatcher (Cython cdef) ← radix trie match O(k)
|
|
236
|
+
│ │
|
|
237
|
+
│ ├─→ _ASGIHandler (no-param fast path, 2 sends only)
|
|
238
|
+
│ │
|
|
239
|
+
│ └─→ handler closure
|
|
240
|
+
│ ├─→ ParameterPipeline → 8 atomic extractors
|
|
241
|
+
│ │ (body / query / query-list / header / cookie / form / file / path)
|
|
242
|
+
│ ├─→ DependencyResolver → OverrideLookup / ScopeCache /
|
|
243
|
+
│ │ ClassFactory / CallableFactory
|
|
244
|
+
│ ├─→ ResponseProcessor (msgspec encode if Struct)
|
|
245
|
+
│ └─→ ExceptionTable (walks subclass handlers)
|
|
246
|
+
│
|
|
247
|
+
└─→ TachyonJSONResponse | TachyonBytesResponse | _InternalErrorResponse
|
|
248
|
+
(pre-built ASGI dicts, zero extra allocations per response)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
The v1.2.x SRP refactor decomposed the monolithic hot path into atomic modules
|
|
252
|
+
with `__slots__` and full type hints — direct `cdef class` candidates. The
|
|
253
|
+
v1.2.9 Cython sprint then compiled 27 of them to `.so`, keeping every `.py`
|
|
254
|
+
sibling as a transparent fallback (Python prefers `.so` automatically).
|
|
255
|
+
|
|
256
|
+
👉 [Full architecture documentation](./docs/02-architecture.md)
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
190
260
|
## 💉 Dependency Injection
|
|
191
261
|
|
|
192
262
|
```python
|
|
193
263
|
from tachyon_api import injectable, Depends
|
|
194
264
|
|
|
195
|
-
@injectable
|
|
196
|
-
class
|
|
197
|
-
def
|
|
198
|
-
|
|
265
|
+
@injectable # singleton (default) — one per app
|
|
266
|
+
class DB:
|
|
267
|
+
def __init__(self):
|
|
268
|
+
self.pool = "..."
|
|
269
|
+
|
|
270
|
+
@injectable(scope="request") # one per HTTP request
|
|
271
|
+
class RequestContext:
|
|
272
|
+
def __init__(self):
|
|
273
|
+
import uuid
|
|
274
|
+
self.correlation_id = str(uuid.uuid4())
|
|
275
|
+
|
|
276
|
+
@injectable(scope="transient") # new instance every time it's injected
|
|
277
|
+
class IdGenerator:
|
|
278
|
+
def __init__(self):
|
|
279
|
+
self._seq = 0
|
|
199
280
|
|
|
200
281
|
@app.get("/users/{id}")
|
|
201
|
-
def get_user(id: str,
|
|
202
|
-
return
|
|
282
|
+
def get_user(id: str, db: DB = Depends(), ctx: RequestContext = Depends()):
|
|
283
|
+
return {"id": id, "trace": ctx.correlation_id}
|
|
203
284
|
```
|
|
204
285
|
|
|
205
286
|
👉 [Full DI documentation](./docs/03-dependency-injection.md)
|
|
@@ -240,11 +321,22 @@ def notify(background_tasks: BackgroundTasks):
|
|
|
240
321
|
## 🌐 WebSockets
|
|
241
322
|
|
|
242
323
|
```python
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
324
|
+
import uuid
|
|
325
|
+
from tachyon_api import injectable, Depends
|
|
326
|
+
|
|
327
|
+
@injectable
|
|
328
|
+
class RoomBroadcaster:
|
|
329
|
+
async def join(self, ws, room_key: str): ...
|
|
330
|
+
|
|
331
|
+
@app.websocket("/ws/rooms/{room_id}") # typed UUID path param
|
|
332
|
+
async def room(
|
|
333
|
+
websocket,
|
|
334
|
+
room_id: uuid.UUID, # auto-converted; 1008 on mismatch
|
|
335
|
+
broadcaster: RoomBroadcaster = Depends(), # @injectable DI in WS
|
|
336
|
+
):
|
|
337
|
+
await broadcaster.join(websocket, str(room_id))
|
|
338
|
+
while True:
|
|
339
|
+
await websocket.send_json({"room": str(room_id)})
|
|
248
340
|
```
|
|
249
341
|
|
|
250
342
|
👉 [Full WebSockets documentation](./docs/10-websockets.md)
|
|
@@ -295,12 +387,23 @@ Installs knowledge about `Body()` requirement, `Struct` vs BaseModel, DI pattern
|
|
|
295
387
|
## 🧪 Testing
|
|
296
388
|
|
|
297
389
|
```python
|
|
390
|
+
# Sync — Starlette TestClient compatible
|
|
298
391
|
from tachyon_api.testing import TachyonTestClient
|
|
299
392
|
|
|
300
393
|
def test_hello():
|
|
301
394
|
client = TachyonTestClient(app)
|
|
302
|
-
|
|
303
|
-
|
|
395
|
+
assert client.get("/").status_code == 200
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
# Async — httpx.AsyncClient over ASGI transport
|
|
399
|
+
import pytest
|
|
400
|
+
from tachyon_api.testing import create_client
|
|
401
|
+
|
|
402
|
+
@pytest.mark.asyncio
|
|
403
|
+
async def test_hello_async():
|
|
404
|
+
async with create_client(app, headers={"X-Trace": "abc"}) as client:
|
|
405
|
+
response = await client.get("/")
|
|
406
|
+
assert response.status_code == 200
|
|
304
407
|
```
|
|
305
408
|
|
|
306
409
|
```bash
|
|
@@ -315,8 +418,8 @@ pytest tests/ -v
|
|
|
315
418
|
|
|
316
419
|
| Feature | Tachyon | FastAPI |
|
|
317
420
|
|---------|---------|---------|
|
|
318
|
-
| **Throughput** | ~
|
|
319
|
-
| **Latency** | ~2.
|
|
421
|
+
| **Throughput** | ~341k req/s total | ~62k req/s total |
|
|
422
|
+
| **Latency** | ~2.3ms avg | ~14ms avg |
|
|
320
423
|
| **Routing** | Radix trie O(k) | Regex scan O(N) |
|
|
321
424
|
| **Serialization** | msgspec + orjson | Pydantic v2 |
|
|
322
425
|
| **Request compilation** | Once at startup | Per request |
|
|
@@ -354,6 +457,8 @@ Upcoming:
|
|
|
354
457
|
- Response streaming
|
|
355
458
|
- GraphQL support
|
|
356
459
|
- Multi-worker benchmarks
|
|
460
|
+
- Benchmark suite vs Litestar / BlackSheep / Robyn
|
|
461
|
+
- SQLAlchemy async integration guide
|
|
357
462
|
|
|
358
463
|
---
|
|
359
464
|
|