turboapi 0.4.16__cp313-cp313-macosx_10_12_x86_64.whl → 0.5.21__cp313-cp313-macosx_10_12_x86_64.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.
- turboapi/request_handler.py +23 -6
- turboapi/rust_integration.py +37 -11
- turboapi/turbonet.cpython-313-darwin.so +0 -0
- {turboapi-0.4.16.dist-info → turboapi-0.5.21.dist-info}/METADATA +176 -65
- {turboapi-0.4.16.dist-info → turboapi-0.5.21.dist-info}/RECORD +7 -7
- {turboapi-0.4.16.dist-info → turboapi-0.5.21.dist-info}/WHEEL +0 -0
- {turboapi-0.4.16.dist-info → turboapi-0.5.21.dist-info}/licenses/LICENSE +0 -0
turboapi/request_handler.py
CHANGED
|
@@ -229,9 +229,12 @@ class RequestBodyParser:
|
|
|
229
229
|
"""
|
|
230
230
|
if not body:
|
|
231
231
|
return {}
|
|
232
|
-
|
|
232
|
+
|
|
233
233
|
try:
|
|
234
|
-
|
|
234
|
+
# CRITICAL: Make a defensive copy immediately using bytearray to force real copy
|
|
235
|
+
# Free-threaded Python with Metal/MLX can have concurrent memory access issues
|
|
236
|
+
body_copy = bytes(bytearray(body))
|
|
237
|
+
json_data = json.loads(body_copy.decode('utf-8'))
|
|
235
238
|
except (json.JSONDecodeError, UnicodeDecodeError) as e:
|
|
236
239
|
raise ValueError(f"Invalid JSON body: {e}")
|
|
237
240
|
|
|
@@ -348,9 +351,16 @@ class ResponseHandler:
|
|
|
348
351
|
try:
|
|
349
352
|
import json
|
|
350
353
|
body = json.loads(body.decode('utf-8'))
|
|
351
|
-
except
|
|
352
|
-
#
|
|
353
|
-
|
|
354
|
+
except json.JSONDecodeError:
|
|
355
|
+
# Not JSON, try as plain text
|
|
356
|
+
try:
|
|
357
|
+
body = body.decode('utf-8')
|
|
358
|
+
except UnicodeDecodeError:
|
|
359
|
+
# Binary data (audio, image, etc.) - keep as bytes
|
|
360
|
+
pass
|
|
361
|
+
except UnicodeDecodeError:
|
|
362
|
+
# Binary data (audio, image, etc.) - keep as bytes
|
|
363
|
+
pass
|
|
354
364
|
return body, result.status_code
|
|
355
365
|
|
|
356
366
|
# Handle tuple returns: (content, status_code)
|
|
@@ -394,6 +404,13 @@ class ResponseHandler:
|
|
|
394
404
|
def make_serializable(obj):
|
|
395
405
|
if isinstance(obj, Model):
|
|
396
406
|
return obj.model_dump()
|
|
407
|
+
elif isinstance(obj, bytes):
|
|
408
|
+
# Binary data - try to decode as UTF-8, otherwise base64 encode
|
|
409
|
+
try:
|
|
410
|
+
return obj.decode('utf-8')
|
|
411
|
+
except UnicodeDecodeError:
|
|
412
|
+
import base64
|
|
413
|
+
return base64.b64encode(obj).decode('ascii')
|
|
397
414
|
elif isinstance(obj, dict):
|
|
398
415
|
return {k: make_serializable(v) for k, v in obj.items()}
|
|
399
416
|
elif isinstance(obj, (list, tuple)):
|
|
@@ -491,7 +508,7 @@ def create_enhanced_handler(original_handler, route_definition):
|
|
|
491
508
|
|
|
492
509
|
# Call original async handler and await it
|
|
493
510
|
result = await original_handler(**filtered_kwargs)
|
|
494
|
-
|
|
511
|
+
|
|
495
512
|
# Normalize response
|
|
496
513
|
content, status_code = ResponseHandler.normalize_response(result)
|
|
497
514
|
|
turboapi/rust_integration.py
CHANGED
|
@@ -20,16 +20,15 @@ from .version_check import CHECK_MARK, CROSS_MARK, ROCKET
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def classify_handler(handler, route) -> tuple[str, dict[str, str], dict]:
|
|
23
|
-
"""Classify a handler for fast dispatch (Phase 3).
|
|
23
|
+
"""Classify a handler for fast dispatch (Phase 3 + Phase 4 async).
|
|
24
24
|
|
|
25
25
|
Returns:
|
|
26
26
|
(handler_type, param_types, model_info) where:
|
|
27
|
-
- handler_type: "simple_sync" | "body_sync" | "model_sync" | "enhanced"
|
|
27
|
+
- handler_type: "simple_sync" | "body_sync" | "model_sync" | "simple_async" | "body_async" | "enhanced"
|
|
28
28
|
- param_types: dict mapping param_name -> type hint string
|
|
29
29
|
- model_info: dict with "param_name" and "model_class" for model handlers
|
|
30
30
|
"""
|
|
31
|
-
|
|
32
|
-
return "enhanced", {}, {}
|
|
31
|
+
is_async = inspect.iscoroutinefunction(handler)
|
|
33
32
|
|
|
34
33
|
sig = inspect.signature(handler)
|
|
35
34
|
param_types = {}
|
|
@@ -42,8 +41,12 @@ def classify_handler(handler, route) -> tuple[str, dict[str, str], dict]:
|
|
|
42
41
|
# Check for dhi/Pydantic BaseModel
|
|
43
42
|
try:
|
|
44
43
|
if BaseModel is not None and inspect.isclass(annotation) and issubclass(annotation, BaseModel):
|
|
45
|
-
# Found a model parameter - use fast model path
|
|
44
|
+
# Found a model parameter - use fast model path (sync only for now)
|
|
46
45
|
model_info = {"param_name": param_name, "model_class": annotation}
|
|
46
|
+
# For async handlers, model parsing needs the enhanced path
|
|
47
|
+
# since Rust-side model parsing only supports sync handlers
|
|
48
|
+
if is_async:
|
|
49
|
+
needs_body = True
|
|
47
50
|
continue # Don't add to param_types
|
|
48
51
|
except TypeError:
|
|
49
52
|
pass
|
|
@@ -64,13 +67,23 @@ def classify_handler(handler, route) -> tuple[str, dict[str, str], dict]:
|
|
|
64
67
|
elif annotation is str or annotation is inspect.Parameter.empty:
|
|
65
68
|
param_types[param_name] = "str"
|
|
66
69
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
method = route.method.value.upper() if hasattr(route, "method") else "GET"
|
|
71
|
+
|
|
72
|
+
# Model handlers use fast model path (simd-json + model_validate) - sync only
|
|
73
|
+
if model_info and not is_async:
|
|
70
74
|
if method in ("POST", "PUT", "PATCH", "DELETE"):
|
|
71
75
|
return "model_sync", param_types, model_info
|
|
72
76
|
|
|
73
|
-
|
|
77
|
+
# Async handlers - Phase 4 async fast paths via Tokio
|
|
78
|
+
if is_async:
|
|
79
|
+
if method in ("POST", "PUT", "PATCH", "DELETE"):
|
|
80
|
+
if needs_body:
|
|
81
|
+
# Complex body types still need enhanced path
|
|
82
|
+
return "enhanced", param_types, {}
|
|
83
|
+
return "body_async", param_types, {}
|
|
84
|
+
return "simple_async", param_types, {}
|
|
85
|
+
|
|
86
|
+
# Sync handlers - Phase 3 sync fast paths
|
|
74
87
|
if method in ("POST", "PUT", "PATCH", "DELETE"):
|
|
75
88
|
if needs_body:
|
|
76
89
|
return "enhanced", param_types, {}
|
|
@@ -207,8 +220,7 @@ class RustIntegratedTurboAPI(TurboAPI):
|
|
|
207
220
|
)
|
|
208
221
|
print(f"{CHECK_MARK} [model_sync] {route.method.value} {route.path}")
|
|
209
222
|
elif handler_type in ("simple_sync", "body_sync"):
|
|
210
|
-
# FAST PATH: Register with metadata for Rust-side parsing
|
|
211
|
-
# Enhanced handler is fallback, original handler is for direct call
|
|
223
|
+
# SYNC FAST PATH: Register with metadata for Rust-side parsing
|
|
212
224
|
enhanced_handler = create_enhanced_handler(route.handler, route)
|
|
213
225
|
param_types_json = json.dumps(param_types)
|
|
214
226
|
|
|
@@ -221,6 +233,20 @@ class RustIntegratedTurboAPI(TurboAPI):
|
|
|
221
233
|
route.handler, # Original unwrapped handler
|
|
222
234
|
)
|
|
223
235
|
print(f"{CHECK_MARK} [{handler_type}] {route.method.value} {route.path}")
|
|
236
|
+
elif handler_type in ("simple_async", "body_async"):
|
|
237
|
+
# ASYNC FAST PATH: Register with Tokio async runtime
|
|
238
|
+
enhanced_handler = create_enhanced_handler(route.handler, route)
|
|
239
|
+
param_types_json = json.dumps(param_types)
|
|
240
|
+
|
|
241
|
+
self.rust_server.add_route_async_fast(
|
|
242
|
+
route.method.value,
|
|
243
|
+
route.path,
|
|
244
|
+
enhanced_handler, # Fallback wrapper
|
|
245
|
+
handler_type,
|
|
246
|
+
param_types_json,
|
|
247
|
+
route.handler, # Original async handler
|
|
248
|
+
)
|
|
249
|
+
print(f"{CHECK_MARK} [{handler_type}] {route.method.value} {route.path}")
|
|
224
250
|
else:
|
|
225
251
|
# ENHANCED PATH: Full Python wrapper needed
|
|
226
252
|
enhanced_handler = create_enhanced_handler(route.handler, route)
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: turboapi
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.21
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Rust
|
|
|
11
11
|
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
12
12
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
13
13
|
Classifier: Framework :: FastAPI
|
|
14
|
-
Requires-Dist: dhi>=1.1.
|
|
14
|
+
Requires-Dist: dhi>=1.1.15
|
|
15
15
|
Requires-Dist: matplotlib>=3.5.0 ; extra == 'benchmark'
|
|
16
16
|
Requires-Dist: requests>=2.25.0 ; extra == 'benchmark'
|
|
17
17
|
Requires-Dist: pytest>=7.0.0 ; extra == 'dev'
|
|
@@ -42,11 +42,19 @@ Project-URL: Repository, https://github.com/justrach/turboAPI
|
|
|
42
42
|
<strong>The FastAPI you know. The speed you deserve.</strong>
|
|
43
43
|
</p>
|
|
44
44
|
|
|
45
|
+
<p align="center">
|
|
46
|
+
<a href="https://pypi.org/project/turboapi/"><img src="https://img.shields.io/pypi/v/turboapi.svg" alt="PyPI version"></a>
|
|
47
|
+
<a href="https://github.com/justrach/turboAPI/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
|
|
48
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.13+-blue.svg" alt="Python 3.13+"></a>
|
|
49
|
+
<a href="https://deepwiki.com/justrach/turboAPI"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
|
|
50
|
+
</p>
|
|
51
|
+
|
|
45
52
|
<p align="center">
|
|
46
53
|
<a href="#the-problem">The Problem</a> •
|
|
47
54
|
<a href="#the-solution">The Solution</a> •
|
|
48
55
|
<a href="#quick-start">Quick Start</a> •
|
|
49
56
|
<a href="#benchmarks">Benchmarks</a> •
|
|
57
|
+
<a href="#async-support">Async Support</a> •
|
|
50
58
|
<a href="#migration-guide">Migration Guide</a>
|
|
51
59
|
</p>
|
|
52
60
|
|
|
@@ -64,7 +72,7 @@ You've optimized your database queries. Added caching. Switched to async. Still
|
|
|
64
72
|
|
|
65
73
|
## The Solution
|
|
66
74
|
|
|
67
|
-
**TurboAPI** is FastAPI with a Rust-powered engine. Same API. Same syntax.
|
|
75
|
+
**TurboAPI** is FastAPI with a Rust-powered engine. Same API. Same syntax. **1.3-1.8x faster**.
|
|
68
76
|
|
|
69
77
|
```python
|
|
70
78
|
# This is all you change
|
|
@@ -82,9 +90,10 @@ Everything else stays exactly the same.
|
|
|
82
90
|
| What FastAPI Does | What TurboAPI Does | Speedup |
|
|
83
91
|
|-------------------|-------------------|---------|
|
|
84
92
|
| HTTP parsing in Python | HTTP parsing in Rust (Hyper/Tokio) | 3x |
|
|
85
|
-
| JSON with `json.dumps()` |
|
|
93
|
+
| JSON with `json.dumps()` | SIMD-accelerated JSON (simd-json) | 2x |
|
|
86
94
|
| GIL-bound threading | Python 3.13 free-threading | 2x |
|
|
87
95
|
| dict-based routing | Radix tree with O(log n) lookup | 1.5x |
|
|
96
|
+
| Async via asyncio | Async via Tokio work-stealing | 1.2x |
|
|
88
97
|
|
|
89
98
|
The result? Your existing FastAPI code runs faster without changing a single line of business logic.
|
|
90
99
|
|
|
@@ -116,6 +125,30 @@ app.run()
|
|
|
116
125
|
|
|
117
126
|
That's it. Your first TurboAPI server is running at `http://localhost:8000`.
|
|
118
127
|
|
|
128
|
+
### Async Handlers (New!)
|
|
129
|
+
|
|
130
|
+
TurboAPI now supports **true async** with Tokio-powered execution:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from turboapi import TurboAPI
|
|
134
|
+
import asyncio
|
|
135
|
+
|
|
136
|
+
app = TurboAPI()
|
|
137
|
+
|
|
138
|
+
@app.get("/sync")
|
|
139
|
+
def sync_handler():
|
|
140
|
+
return {"type": "sync", "message": "Fast!"}
|
|
141
|
+
|
|
142
|
+
@app.get("/async")
|
|
143
|
+
async def async_handler():
|
|
144
|
+
await asyncio.sleep(0.001) # Simulated I/O
|
|
145
|
+
return {"type": "async", "message": "Even faster under load!"}
|
|
146
|
+
|
|
147
|
+
app.run()
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Async handlers are automatically detected and routed through Tokio's work-stealing scheduler for optimal concurrency.
|
|
151
|
+
|
|
119
152
|
### For Maximum Performance
|
|
120
153
|
|
|
121
154
|
Run with Python's free-threading mode:
|
|
@@ -132,9 +165,18 @@ This unlocks the full power of TurboAPI's Rust core by removing the GIL bottlene
|
|
|
132
165
|
|
|
133
166
|
Real numbers matter. Here's TurboAPI vs FastAPI on identical hardware:
|
|
134
167
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
168
|
+
### Latest Benchmark Results
|
|
169
|
+
|
|
170
|
+
| Endpoint | TurboAPI | FastAPI | Improvement |
|
|
171
|
+
|----------|----------|---------|-------------|
|
|
172
|
+
| **Sequential Latency** |
|
|
173
|
+
| GET / | 0.76ms | 1.05ms | **1.4x faster** |
|
|
174
|
+
| GET /benchmark/simple | 0.61ms | 0.81ms | **1.3x faster** |
|
|
175
|
+
| GET /benchmark/medium | 0.61ms | 0.77ms | **1.3x faster** |
|
|
176
|
+
| GET /benchmark/json | 0.72ms | 1.04ms | **1.4x faster** |
|
|
177
|
+
| **Concurrent Latency** |
|
|
178
|
+
| GET / | 2.05ms | 2.53ms | **1.2x faster** |
|
|
179
|
+
| GET /benchmark/json | 2.17ms | 3.90ms | **1.8x faster** |
|
|
138
180
|
|
|
139
181
|
### Throughput (requests/second)
|
|
140
182
|
|
|
@@ -144,34 +186,89 @@ Real numbers matter. Here's TurboAPI vs FastAPI on identical hardware:
|
|
|
144
186
|
| GET /json (object) | **20,592** | 7,882 | 2.6x |
|
|
145
187
|
| GET /users/{id} (path params) | **18,428** | 7,344 | 2.5x |
|
|
146
188
|
| POST /items (model validation) | **19,255** | 6,312 | **3.1x** |
|
|
147
|
-
| GET /status201 (custom status) | **15,698** | 8,608 | 1.8x |
|
|
148
|
-
|
|
149
|
-
### Latency (lower is better)
|
|
150
189
|
|
|
151
|
-
|
|
152
|
-
<img src="assets/benchmark_latency.png" alt="Latency Comparison" width="800"/>
|
|
153
|
-
</p>
|
|
190
|
+
### Async Handler Performance
|
|
154
191
|
|
|
155
|
-
|
|
|
156
|
-
|
|
157
|
-
|
|
|
158
|
-
|
|
|
159
|
-
|
|
|
192
|
+
| Metric | Sync Handler | Async Handler | Notes |
|
|
193
|
+
|--------|--------------|---------------|-------|
|
|
194
|
+
| Sequential (100 req) | 0.66ms | 0.76ms | Similar performance |
|
|
195
|
+
| Concurrent (200 req) | 108ms batch | 139ms batch | Sync faster for CPU-bound |
|
|
196
|
+
| I/O Wait (1ms sleep) | 2.22ms | 2.06ms | **Async wins for I/O** |
|
|
160
197
|
|
|
161
|
-
|
|
198
|
+
**Key Insight:** Use async handlers when you have actual I/O operations (database, network). For pure CPU work, sync handlers are slightly faster.
|
|
162
199
|
|
|
163
200
|
### Run Your Own Benchmarks
|
|
164
201
|
|
|
165
202
|
```bash
|
|
166
|
-
#
|
|
167
|
-
|
|
203
|
+
# Quick benchmark
|
|
204
|
+
python benches/python_benchmark.py
|
|
168
205
|
|
|
169
|
-
#
|
|
170
|
-
|
|
171
|
-
PYTHON_GIL=0 python benchmarks/run_benchmarks.py
|
|
206
|
+
# Full comparison with FastAPI
|
|
207
|
+
python tests/benchmark_comparison.py
|
|
172
208
|
|
|
173
|
-
#
|
|
174
|
-
python
|
|
209
|
+
# Async vs Sync comparison
|
|
210
|
+
python benches/async_comparison_bench.py
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Async Support
|
|
216
|
+
|
|
217
|
+
### How Async Works in TurboAPI
|
|
218
|
+
|
|
219
|
+
TurboAPI uses a **hybrid async architecture**:
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
┌─────────────────────────────────────────────────────────┐
|
|
223
|
+
│ Your Python Handlers │
|
|
224
|
+
│ @app.get("/sync") @app.get("/async") │
|
|
225
|
+
│ def handler(): async def handler(): │
|
|
226
|
+
├─────────────────────────────────────────────────────────┤
|
|
227
|
+
│ Handler Classification │
|
|
228
|
+
│ simple_sync │ body_sync │ simple_async │ body_async │
|
|
229
|
+
├─────────────────────────────────────────────────────────┤
|
|
230
|
+
│ Tokio Runtime (Rust) │
|
|
231
|
+
│ Work-stealing scheduler • 14 workers │
|
|
232
|
+
├─────────────────────────────────────────────────────────┤
|
|
233
|
+
│ pyo3-async-runtimes │
|
|
234
|
+
│ Python coroutines ↔ Rust futures conversion │
|
|
235
|
+
└─────────────────────────────────────────────────────────┘
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Handler Types
|
|
239
|
+
|
|
240
|
+
TurboAPI automatically classifies handlers for optimal dispatch:
|
|
241
|
+
|
|
242
|
+
| Handler Type | Description | Use Case |
|
|
243
|
+
|--------------|-------------|----------|
|
|
244
|
+
| `simple_sync` | Sync, no body | GET endpoints |
|
|
245
|
+
| `body_sync` | Sync, with body | POST/PUT without complex types |
|
|
246
|
+
| `model_sync` | Sync, with model validation | POST with dhi models |
|
|
247
|
+
| `simple_async` | Async, no body | GET with I/O operations |
|
|
248
|
+
| `body_async` | Async, with body | POST/PUT with I/O operations |
|
|
249
|
+
| `enhanced` | Full Python wrapper | Complex dependencies |
|
|
250
|
+
|
|
251
|
+
### When to Use Async
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
# Use sync for pure computation
|
|
255
|
+
@app.get("/compute")
|
|
256
|
+
def compute():
|
|
257
|
+
result = sum(i * i for i in range(1000))
|
|
258
|
+
return {"result": result}
|
|
259
|
+
|
|
260
|
+
# Use async for I/O operations
|
|
261
|
+
@app.get("/fetch-data")
|
|
262
|
+
async def fetch_data():
|
|
263
|
+
async with aiohttp.ClientSession() as session:
|
|
264
|
+
async with session.get("https://api.example.com/data") as resp:
|
|
265
|
+
return await resp.json()
|
|
266
|
+
|
|
267
|
+
# Use async for database operations
|
|
268
|
+
@app.get("/users/{user_id}")
|
|
269
|
+
async def get_user(user_id: int):
|
|
270
|
+
user = await database.fetch_one(query, values={"id": user_id})
|
|
271
|
+
return {"user": user}
|
|
175
272
|
```
|
|
176
273
|
|
|
177
274
|
---
|
|
@@ -234,6 +331,7 @@ Everything you use in FastAPI works in TurboAPI:
|
|
|
234
331
|
| Query parameters | ✅ | With validation |
|
|
235
332
|
| Request body (JSON) | ✅ | SIMD-accelerated |
|
|
236
333
|
| Response models | ✅ | Full support |
|
|
334
|
+
| **Async handlers** | ✅ | **Tokio-powered** |
|
|
237
335
|
| Dependency injection | ✅ | `Depends()` with caching |
|
|
238
336
|
| OAuth2 authentication | ✅ | Password & AuthCode flows |
|
|
239
337
|
| HTTP Basic/Bearer auth | ✅ | Full implementation |
|
|
@@ -241,7 +339,7 @@ Everything you use in FastAPI works in TurboAPI:
|
|
|
241
339
|
| CORS middleware | ✅ | Rust-accelerated |
|
|
242
340
|
| GZip middleware | ✅ | Configurable |
|
|
243
341
|
| Background tasks | ✅ | Async-compatible |
|
|
244
|
-
| WebSocket | ✅ |
|
|
342
|
+
| WebSocket | ✅ | HTTP upgrade support |
|
|
245
343
|
| APIRouter | ✅ | Prefixes and tags |
|
|
246
344
|
| HTTPException | ✅ | With custom headers |
|
|
247
345
|
| Custom responses | ✅ | JSON, HTML, Redirect, etc. |
|
|
@@ -266,6 +364,27 @@ def get_current_user(token: str = Depends(oauth2_scheme)):
|
|
|
266
364
|
return {"user": "authenticated", "token": token}
|
|
267
365
|
```
|
|
268
366
|
|
|
367
|
+
### Async Database Access
|
|
368
|
+
|
|
369
|
+
```python
|
|
370
|
+
from turboapi import TurboAPI
|
|
371
|
+
import asyncpg
|
|
372
|
+
|
|
373
|
+
app = TurboAPI()
|
|
374
|
+
pool = None
|
|
375
|
+
|
|
376
|
+
@app.on_event("startup")
|
|
377
|
+
async def startup():
|
|
378
|
+
global pool
|
|
379
|
+
pool = await asyncpg.create_pool("postgresql://localhost/mydb")
|
|
380
|
+
|
|
381
|
+
@app.get("/users/{user_id}")
|
|
382
|
+
async def get_user(user_id: int):
|
|
383
|
+
async with pool.acquire() as conn:
|
|
384
|
+
row = await conn.fetchrow("SELECT * FROM users WHERE id = $1", user_id)
|
|
385
|
+
return dict(row) if row else {"error": "Not found"}
|
|
386
|
+
```
|
|
387
|
+
|
|
269
388
|
### Request Validation
|
|
270
389
|
|
|
271
390
|
```python
|
|
@@ -297,47 +416,33 @@ app.add_middleware(
|
|
|
297
416
|
app.add_middleware(GZipMiddleware, minimum_size=1000)
|
|
298
417
|
```
|
|
299
418
|
|
|
300
|
-
### API Router
|
|
301
|
-
|
|
302
|
-
```python
|
|
303
|
-
from turboapi import APIRouter
|
|
304
|
-
|
|
305
|
-
router = APIRouter(prefix="/api/v1", tags=["users"])
|
|
306
|
-
|
|
307
|
-
@router.get("/users")
|
|
308
|
-
def list_users():
|
|
309
|
-
return {"users": []}
|
|
310
|
-
|
|
311
|
-
@router.get("/users/{user_id}")
|
|
312
|
-
def get_user(user_id: int):
|
|
313
|
-
return {"user_id": user_id}
|
|
314
|
-
|
|
315
|
-
app.include_router(router)
|
|
316
|
-
```
|
|
317
|
-
|
|
318
419
|
---
|
|
319
420
|
|
|
320
|
-
##
|
|
421
|
+
## Architecture
|
|
321
422
|
|
|
322
423
|
TurboAPI's secret is a hybrid architecture:
|
|
323
424
|
|
|
324
425
|
```
|
|
325
|
-
|
|
326
|
-
│ Your Python Application
|
|
327
|
-
│
|
|
328
|
-
|
|
329
|
-
│
|
|
330
|
-
│
|
|
331
|
-
|
|
332
|
-
│
|
|
333
|
-
│
|
|
334
|
-
|
|
335
|
-
│
|
|
336
|
-
│
|
|
337
|
-
|
|
338
|
-
│
|
|
339
|
-
│
|
|
340
|
-
|
|
426
|
+
┌──────────────────────────────────────────────────────────┐
|
|
427
|
+
│ Your Python Application │
|
|
428
|
+
│ (exactly like FastAPI code) │
|
|
429
|
+
├──────────────────────────────────────────────────────────┤
|
|
430
|
+
│ TurboAPI (FastAPI-compatible layer) │
|
|
431
|
+
│ Routing • Validation • Dependency Injection │
|
|
432
|
+
├──────────────────────────────────────────────────────────┤
|
|
433
|
+
│ Handler Classification (Phase 3+4) │
|
|
434
|
+
│ simple_sync │ body_sync │ simple_async │ body_async │
|
|
435
|
+
├──────────────────────────────────────────────────────────┤
|
|
436
|
+
│ PyO3 Bridge (zero-copy) │
|
|
437
|
+
│ Rust ↔ Python with minimal overhead │
|
|
438
|
+
├──────────────────────────────────────────────────────────┤
|
|
439
|
+
│ TurboNet (Rust HTTP Core) │
|
|
440
|
+
│ • Hyper + Tokio async runtime (14 worker threads) │
|
|
441
|
+
│ • SIMD-accelerated JSON (simd-json) │
|
|
442
|
+
│ • Radix tree routing │
|
|
443
|
+
│ • Zero-copy response buffers │
|
|
444
|
+
│ • pyo3-async-runtimes for async handler support │
|
|
445
|
+
└──────────────────────────────────────────────────────────┘
|
|
341
446
|
```
|
|
342
447
|
|
|
343
448
|
**Python handles the logic you care about.** Routes, validation rules, business logic—all in Python.
|
|
@@ -369,6 +474,10 @@ pip install -e ./python
|
|
|
369
474
|
|
|
370
475
|
# Run tests
|
|
371
476
|
PYTHON_GIL=0 python -m pytest tests/ -v
|
|
477
|
+
|
|
478
|
+
# Run benchmarks
|
|
479
|
+
python benches/python_benchmark.py
|
|
480
|
+
python tests/benchmark_comparison.py
|
|
372
481
|
```
|
|
373
482
|
|
|
374
483
|
---
|
|
@@ -383,20 +492,21 @@ PYTHON_GIL=0 python -m pytest tests/ -v
|
|
|
383
492
|
- [x] FastAPI feature parity (OAuth2, Depends, Middleware)
|
|
384
493
|
- [x] Radix tree routing with path parameters
|
|
385
494
|
- [x] Handler classification for optimized fast paths
|
|
495
|
+
- [x] **Async handler optimization (Tokio + pyo3-async-runtimes)**
|
|
496
|
+
- [x] **WebSocket HTTP upgrade support**
|
|
386
497
|
|
|
387
498
|
### In Progress 🚧
|
|
388
499
|
|
|
389
|
-
- [ ] Async handler optimization (pure Tokio)
|
|
390
|
-
- [ ] WebSocket performance improvements
|
|
391
500
|
- [ ] HTTP/2 with server push
|
|
501
|
+
- [ ] OpenAPI/Swagger auto-generation
|
|
392
502
|
|
|
393
503
|
### Planned 📋
|
|
394
504
|
|
|
395
|
-
- [ ] OpenAPI/Swagger auto-generation
|
|
396
505
|
- [ ] GraphQL support
|
|
397
506
|
- [ ] Database connection pooling
|
|
398
507
|
- [ ] Prometheus metrics
|
|
399
508
|
- [ ] Distributed tracing
|
|
509
|
+
- [ ] gRPC support
|
|
400
510
|
|
|
401
511
|
---
|
|
402
512
|
|
|
@@ -404,6 +514,7 @@ PYTHON_GIL=0 python -m pytest tests/ -v
|
|
|
404
514
|
|
|
405
515
|
- **Issues & Features**: [GitHub Issues](https://github.com/justrach/turboAPI/issues)
|
|
406
516
|
- **Discussions**: [GitHub Discussions](https://github.com/justrach/turboAPI/discussions)
|
|
517
|
+
- **Documentation**: [](https://deepwiki.com/justrach/turboAPI)
|
|
407
518
|
|
|
408
519
|
---
|
|
409
520
|
|
|
@@ -10,20 +10,20 @@ turboapi/main_app.py,sha256=5w6x1DX_XChPCcZf24b4CC8qe24XFyGY3BRuR71qkCM,13411
|
|
|
10
10
|
turboapi/middleware.py,sha256=3G5zhPKiHm07b_xv_dOsO88W1PfO78SGMySJe4d9nb4,10737
|
|
11
11
|
turboapi/models.py,sha256=OzyDTEl97zcXa6DTrsR_hZZjj8Lr9QwsD0BrC74BE5o,4476
|
|
12
12
|
turboapi/openapi.py,sha256=wwS4akekFns-OYQUh7bjKBzRqdg9Z7z5MFo27LlFkEY,7281
|
|
13
|
-
turboapi/request_handler.py,sha256=
|
|
13
|
+
turboapi/request_handler.py,sha256=YHfJFqQJI_mZdczpARprOkpsNnCIojTrlW_vLgTdPQw,24593
|
|
14
14
|
turboapi/responses.py,sha256=wiYkG53NNwXu1RDyRlZFF-8jxx51xL6LOTUj5TG__2c,6052
|
|
15
15
|
turboapi/routing.py,sha256=-39IUSfsMswCLR-zJ6c4Ri0tYd7_UjYnQyioT4a1W_Q,7601
|
|
16
|
-
turboapi/rust_integration.py,sha256=
|
|
16
|
+
turboapi/rust_integration.py,sha256=StR_4PHIzz1Qk9T-MG5veu3qerwKohoXFeOagk6Be6E,15543
|
|
17
17
|
turboapi/security.py,sha256=U2ZQSMWK9a6YzSk5F39TgeZriJ7JtDNZo3RldVPphKI,17493
|
|
18
18
|
turboapi/server_integration.py,sha256=cnyT4Eqrw3lkZjdVkDvN2qMH5yXh68LJhC2w9ccH9Qc,17904
|
|
19
19
|
turboapi/staticfiles.py,sha256=MLSvPeYUQGPEL4_mVQ2z3ItREZxUTEN-awQrNxPAgsM,2861
|
|
20
20
|
turboapi/status.py,sha256=7qVEXVQ3AVFcgEKbmdq6dTypCP275qIbk51hMUQztXA,3049
|
|
21
21
|
turboapi/templating.py,sha256=YlZeesNAuAcqkdUhkE1xGzTY0w_-nhbG5bJWntYe2AM,2087
|
|
22
22
|
turboapi/testclient.py,sha256=TBuRPms8G9MncARfwtvCKvqrtfthraTcy0TkPmweMao,10669
|
|
23
|
-
turboapi/turbonet.cpython-313-darwin.so,sha256=
|
|
23
|
+
turboapi/turbonet.cpython-313-darwin.so,sha256=qaOvLmIwboqv7_subdCl-6q5I38tMZ6VLxRSLo_Ba5Q,5669784
|
|
24
24
|
turboapi/version_check.py,sha256=NmKEt9Qloi5NrGYvVusjFTDhMhZzZfazuXyjcOPO2vc,9357
|
|
25
25
|
turboapi/websockets.py,sha256=7Ao_dKfGWHu2nMU5pm9X20E9P0qnh4lJOykHFmzc5tc,4226
|
|
26
|
-
turboapi-0.
|
|
27
|
-
turboapi-0.
|
|
28
|
-
turboapi-0.
|
|
29
|
-
turboapi-0.
|
|
26
|
+
turboapi-0.5.21.dist-info/METADATA,sha256=8q-P0UJmWeRKs9E3YJpaOLNJeZc9OK_elGvCvNjcunM,17556
|
|
27
|
+
turboapi-0.5.21.dist-info/WHEEL,sha256=y-bBezkr9XqWB_KK0tUa5PfJn4Rf73obH4dbo4t0ZEc,107
|
|
28
|
+
turboapi-0.5.21.dist-info/licenses/LICENSE,sha256=09pmQi_FlhCpUCJ8bMQQoax_oOooJS3FKezznBBU8OQ,1068
|
|
29
|
+
turboapi-0.5.21.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|