turboapi 0.4.12__cp314-cp314t-macosx_11_0_arm64.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/__init__.py +24 -0
- turboapi/async_limiter.py +86 -0
- turboapi/async_pool.py +141 -0
- turboapi/decorators.py +69 -0
- turboapi/main_app.py +314 -0
- turboapi/middleware.py +342 -0
- turboapi/models.py +148 -0
- turboapi/request_handler.py +227 -0
- turboapi/routing.py +219 -0
- turboapi/rust_integration.py +335 -0
- turboapi/security.py +542 -0
- turboapi/server_integration.py +436 -0
- turboapi/turbonet.cpython-314t-darwin.so +0 -0
- turboapi/version_check.py +268 -0
- turboapi-0.4.12.dist-info/METADATA +31 -0
- turboapi-0.4.12.dist-info/RECORD +17 -0
- turboapi-0.4.12.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TurboAPI Direct Rust Integration
|
|
3
|
+
Connects FastAPI-compatible routing directly to Rust HTTP core with zero Python overhead
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import inspect
|
|
7
|
+
import json
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from .main_app import TurboAPI
|
|
11
|
+
from .request_handler import create_enhanced_handler, ResponseHandler
|
|
12
|
+
from .version_check import CHECK_MARK, CROSS_MARK, ROCKET
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
from turboapi import turbonet
|
|
16
|
+
RUST_CORE_AVAILABLE = True
|
|
17
|
+
except ImportError:
|
|
18
|
+
RUST_CORE_AVAILABLE = False
|
|
19
|
+
turbonet = None
|
|
20
|
+
print("[WARN] Rust core not available - running in simulation mode")
|
|
21
|
+
|
|
22
|
+
class RustIntegratedTurboAPI(TurboAPI):
|
|
23
|
+
"""TurboAPI with direct Rust HTTP server integration - zero Python middleware overhead."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, *args, **kwargs):
|
|
26
|
+
super().__init__(*args, **kwargs)
|
|
27
|
+
self.rust_server = None
|
|
28
|
+
self.route_handlers = {} # Store Python handlers by route key
|
|
29
|
+
print(f"{ROCKET} RustIntegratedTurboAPI created - direct Rust integration")
|
|
30
|
+
|
|
31
|
+
# Check environment variable to disable rate limiting for benchmarking
|
|
32
|
+
import os
|
|
33
|
+
if os.getenv("TURBO_DISABLE_RATE_LIMITING") == "1":
|
|
34
|
+
self.configure_rate_limiting(enabled=False)
|
|
35
|
+
print("[CONFIG] Rate limiting disabled via environment variable")
|
|
36
|
+
|
|
37
|
+
# FastAPI-like decorators for better developer experience
|
|
38
|
+
def get(self, path: str, **kwargs):
|
|
39
|
+
"""Decorator for GET routes - FastAPI-like syntax."""
|
|
40
|
+
return super().get(path, **kwargs)
|
|
41
|
+
|
|
42
|
+
def post(self, path: str, **kwargs):
|
|
43
|
+
"""Decorator for POST routes - FastAPI-like syntax."""
|
|
44
|
+
return super().post(path, **kwargs)
|
|
45
|
+
|
|
46
|
+
def put(self, path: str, **kwargs):
|
|
47
|
+
"""Decorator for PUT routes - FastAPI-like syntax."""
|
|
48
|
+
return super().put(path, **kwargs)
|
|
49
|
+
|
|
50
|
+
def delete(self, path: str, **kwargs):
|
|
51
|
+
"""Decorator for DELETE routes - FastAPI-like syntax."""
|
|
52
|
+
return super().delete(path, **kwargs)
|
|
53
|
+
|
|
54
|
+
def patch(self, path: str, **kwargs):
|
|
55
|
+
"""Decorator for PATCH routes - FastAPI-like syntax."""
|
|
56
|
+
return super().patch(path, **kwargs)
|
|
57
|
+
|
|
58
|
+
def configure_rate_limiting(self, enabled: bool = False, requests_per_minute: int = 1000000):
|
|
59
|
+
"""Configure rate limiting for the server.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
enabled: Whether to enable rate limiting (default: False for benchmarking)
|
|
63
|
+
requests_per_minute: Maximum requests per minute per IP (default: 1,000,000)
|
|
64
|
+
"""
|
|
65
|
+
if RUST_CORE_AVAILABLE:
|
|
66
|
+
try:
|
|
67
|
+
turbonet.configure_rate_limiting(enabled, requests_per_minute)
|
|
68
|
+
status = "enabled" if enabled else "disabled"
|
|
69
|
+
print(f"[CONFIG] Rate limiting {status} ({requests_per_minute:,} req/min)")
|
|
70
|
+
except Exception as e:
|
|
71
|
+
print(f"[WARN] Failed to configure rate limiting: {e}")
|
|
72
|
+
else:
|
|
73
|
+
print("[WARN] Rate limiting configuration requires Rust core")
|
|
74
|
+
|
|
75
|
+
def _initialize_rust_server(self, host: str = "127.0.0.1", port: int = 8000):
|
|
76
|
+
"""Initialize the Rust HTTP server with direct integration."""
|
|
77
|
+
if not RUST_CORE_AVAILABLE:
|
|
78
|
+
print("[WARN] Rust core not available - cannot initialize server")
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
# Create Rust server
|
|
83
|
+
self.rust_server = turbonet.TurboServer(host, port)
|
|
84
|
+
|
|
85
|
+
# Add middleware directly to Rust server (zero Python overhead)
|
|
86
|
+
for middleware_class, kwargs in self.middleware_stack:
|
|
87
|
+
middleware_name = middleware_class.__name__
|
|
88
|
+
|
|
89
|
+
if middleware_name == "CorsMiddleware":
|
|
90
|
+
cors_middleware = turbonet.CorsMiddleware(
|
|
91
|
+
kwargs.get("origins", ["*"]),
|
|
92
|
+
kwargs.get("methods", ["GET", "POST", "PUT", "DELETE"]),
|
|
93
|
+
kwargs.get("headers", ["*"]),
|
|
94
|
+
kwargs.get("max_age", 3600)
|
|
95
|
+
)
|
|
96
|
+
self.rust_server.add_middleware(cors_middleware)
|
|
97
|
+
print(f"{CHECK_MARK} Added CORS middleware to Rust server")
|
|
98
|
+
|
|
99
|
+
elif middleware_name == "RateLimitMiddleware":
|
|
100
|
+
rate_limit = turbonet.RateLimitMiddleware(
|
|
101
|
+
kwargs.get("requests_per_minute", 1000)
|
|
102
|
+
)
|
|
103
|
+
self.rust_server.add_middleware(rate_limit)
|
|
104
|
+
print(f"{CHECK_MARK} Added Rate Limiting middleware to Rust server")
|
|
105
|
+
|
|
106
|
+
# Add more middleware types as needed
|
|
107
|
+
|
|
108
|
+
# Register all routes with Rust server
|
|
109
|
+
self._register_routes_with_rust()
|
|
110
|
+
|
|
111
|
+
print(f"{CHECK_MARK} Rust server initialized with {len(self.registry.get_routes())} routes")
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
print(f"{CROSS_MARK} Rust server initialization failed: {e}")
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
def _register_routes_with_rust(self):
|
|
119
|
+
"""Register all Python routes with the Rust HTTP server."""
|
|
120
|
+
for route in self.registry.get_routes():
|
|
121
|
+
try:
|
|
122
|
+
# Create route key
|
|
123
|
+
route_key = f"{route.method.value}:{route.path}"
|
|
124
|
+
|
|
125
|
+
# Store Python handler
|
|
126
|
+
self.route_handlers[route_key] = route.handler
|
|
127
|
+
|
|
128
|
+
# Create enhanced handler with automatic body parsing
|
|
129
|
+
enhanced_handler = create_enhanced_handler(route.handler, route)
|
|
130
|
+
|
|
131
|
+
# Create Rust-compatible handler wrapper
|
|
132
|
+
def create_rust_handler(python_handler, route_def):
|
|
133
|
+
def rust_handler(rust_request):
|
|
134
|
+
"""Rust-callable handler that calls Python function with automatic body parsing."""
|
|
135
|
+
try:
|
|
136
|
+
# Extract request data from Rust
|
|
137
|
+
path = rust_request.path
|
|
138
|
+
query_string = rust_request.query_string
|
|
139
|
+
|
|
140
|
+
# Get headers - try method call first, then attribute
|
|
141
|
+
try:
|
|
142
|
+
headers = rust_request.get_headers() if callable(getattr(rust_request, 'get_headers', None)) else {}
|
|
143
|
+
except:
|
|
144
|
+
headers = getattr(rust_request, 'headers', {})
|
|
145
|
+
|
|
146
|
+
# Get body - Rust sets it as 'body' attribute (bytes)
|
|
147
|
+
body = getattr(rust_request, 'body', b'')
|
|
148
|
+
|
|
149
|
+
# Also try get_body if it's set
|
|
150
|
+
if not body:
|
|
151
|
+
get_body_attr = getattr(rust_request, 'get_body', None)
|
|
152
|
+
if get_body_attr is not None:
|
|
153
|
+
if callable(get_body_attr):
|
|
154
|
+
body = get_body_attr()
|
|
155
|
+
else:
|
|
156
|
+
body = get_body_attr
|
|
157
|
+
|
|
158
|
+
# Parse query parameters
|
|
159
|
+
query_params = {}
|
|
160
|
+
if query_string:
|
|
161
|
+
# Simple query string parsing
|
|
162
|
+
for param in query_string.split('&'):
|
|
163
|
+
if '=' in param:
|
|
164
|
+
key, value = param.split('=', 1)
|
|
165
|
+
query_params[key] = value
|
|
166
|
+
|
|
167
|
+
# Parse path parameters
|
|
168
|
+
path_params = self._extract_path_params(route_def.path, path)
|
|
169
|
+
|
|
170
|
+
# Prepare arguments for enhanced handler
|
|
171
|
+
call_args = {}
|
|
172
|
+
|
|
173
|
+
# Add path parameters
|
|
174
|
+
call_args.update(path_params)
|
|
175
|
+
|
|
176
|
+
# Add query parameters
|
|
177
|
+
call_args.update(query_params)
|
|
178
|
+
|
|
179
|
+
# Always add body and headers for enhanced handler
|
|
180
|
+
call_args['body'] = body if body else b''
|
|
181
|
+
call_args['headers'] = headers
|
|
182
|
+
|
|
183
|
+
# Call enhanced handler (handles parsing, validation, response normalization)
|
|
184
|
+
result = python_handler(**call_args)
|
|
185
|
+
|
|
186
|
+
# Enhanced handler returns normalized format
|
|
187
|
+
# {"content": ..., "status_code": ..., "content_type": ...}
|
|
188
|
+
# But Rust expects a plain dict that it will JSON serialize
|
|
189
|
+
# So just return the content directly
|
|
190
|
+
if isinstance(result, dict) and 'content' in result and 'status_code' in result:
|
|
191
|
+
# Return just the content - Rust will handle status codes later
|
|
192
|
+
# For now, just return the content as a dict
|
|
193
|
+
return result['content']
|
|
194
|
+
|
|
195
|
+
# Fallback for plain dict responses
|
|
196
|
+
return result
|
|
197
|
+
|
|
198
|
+
except Exception as e:
|
|
199
|
+
# Return 500 error as plain dict (Rust will serialize it)
|
|
200
|
+
import traceback
|
|
201
|
+
return {
|
|
202
|
+
"error": "Internal Server Error",
|
|
203
|
+
"detail": str(e),
|
|
204
|
+
"traceback": traceback.format_exc()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return rust_handler # noqa: B023
|
|
208
|
+
|
|
209
|
+
# Register the ORIGINAL handler directly with Rust
|
|
210
|
+
# Rust will call it with call0() (no arguments)
|
|
211
|
+
# The original handler doesn't expect any arguments
|
|
212
|
+
self.rust_server.add_route(
|
|
213
|
+
route.method.value,
|
|
214
|
+
route.path,
|
|
215
|
+
route.handler # Pass original handler, not wrapper!
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
print(f"{CHECK_MARK} Registered {route.method.value} {route.path} with Rust server")
|
|
219
|
+
|
|
220
|
+
except Exception as e:
|
|
221
|
+
print(f"{CROSS_MARK} Failed to register route {route.method.value} {route.path}: {e}")
|
|
222
|
+
|
|
223
|
+
def _extract_path_params(self, route_path: str, actual_path: str) -> dict[str, str]:
|
|
224
|
+
"""Extract path parameters from actual path using route pattern."""
|
|
225
|
+
import re
|
|
226
|
+
|
|
227
|
+
# Convert route path to regex
|
|
228
|
+
pattern = route_path
|
|
229
|
+
param_names = []
|
|
230
|
+
|
|
231
|
+
# Find all path parameters
|
|
232
|
+
param_matches = re.findall(r'\{([^}]+)\}', route_path)
|
|
233
|
+
|
|
234
|
+
for param in param_matches:
|
|
235
|
+
param_names.append(param)
|
|
236
|
+
pattern = pattern.replace(f'{{{param}}}', '([^/]+)')
|
|
237
|
+
|
|
238
|
+
# Match actual path
|
|
239
|
+
match = re.match(f'^{pattern}$', actual_path)
|
|
240
|
+
|
|
241
|
+
if match:
|
|
242
|
+
return dict(zip(param_names, match.groups(), strict=False))
|
|
243
|
+
|
|
244
|
+
return {}
|
|
245
|
+
|
|
246
|
+
def _convert_to_rust_response(self, result) -> Any:
|
|
247
|
+
"""Convert Python result to Rust ResponseView."""
|
|
248
|
+
if not RUST_CORE_AVAILABLE:
|
|
249
|
+
return result
|
|
250
|
+
|
|
251
|
+
if isinstance(result, dict) and "status_code" in result:
|
|
252
|
+
# Handle error responses
|
|
253
|
+
response = turbonet.ResponseView(result["status_code"])
|
|
254
|
+
if "error" in result:
|
|
255
|
+
response.json(json.dumps({
|
|
256
|
+
"error": result["error"],
|
|
257
|
+
"detail": result.get("detail", "")
|
|
258
|
+
}))
|
|
259
|
+
else:
|
|
260
|
+
response.json(json.dumps(result.get("data", result)))
|
|
261
|
+
return response
|
|
262
|
+
elif isinstance(result, dict):
|
|
263
|
+
# JSON response
|
|
264
|
+
response = turbonet.ResponseView(200)
|
|
265
|
+
response.json(json.dumps(result))
|
|
266
|
+
return response
|
|
267
|
+
elif isinstance(result, str):
|
|
268
|
+
# Text response
|
|
269
|
+
response = turbonet.ResponseView(200)
|
|
270
|
+
response.text(result)
|
|
271
|
+
return response
|
|
272
|
+
else:
|
|
273
|
+
# Default JSON response
|
|
274
|
+
response = turbonet.ResponseView(200)
|
|
275
|
+
response.json(json.dumps({"data": result}))
|
|
276
|
+
return response
|
|
277
|
+
|
|
278
|
+
def run(self, host: str = "127.0.0.1", port: int = 8000, **kwargs):
|
|
279
|
+
"""Run with direct Rust server integration."""
|
|
280
|
+
print(f"\n{ROCKET} Starting TurboAPI with Direct Rust Integration...")
|
|
281
|
+
print(f" Host: {host}:{port}")
|
|
282
|
+
print(f" Title: {self.title} v{self.version}")
|
|
283
|
+
|
|
284
|
+
# Initialize Rust server
|
|
285
|
+
if not self._initialize_rust_server(host, port):
|
|
286
|
+
print(f"{CROSS_MARK} Failed to initialize Rust server")
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
# Print integration info
|
|
290
|
+
print("\n[CONFIG] Direct Rust Integration:")
|
|
291
|
+
print(f" Rust HTTP Server: {CHECK_MARK} Active")
|
|
292
|
+
print(f" Middleware Pipeline: {CHECK_MARK} Rust-native (zero Python overhead)")
|
|
293
|
+
print(f" Route Handlers: {CHECK_MARK} {len(self.route_handlers)} Python functions registered")
|
|
294
|
+
print(f" Performance: {CHECK_MARK} 5-10x FastAPI target (no Python middleware overhead)")
|
|
295
|
+
|
|
296
|
+
# Print route information
|
|
297
|
+
self.print_routes()
|
|
298
|
+
|
|
299
|
+
print("\n[PERF] Zero-Overhead Architecture:")
|
|
300
|
+
print(" HTTP Request → Rust Middleware → Python Handler → Rust Response")
|
|
301
|
+
print(" No Python middleware overhead!")
|
|
302
|
+
print(" Direct Rust-to-Python calls only for route handlers")
|
|
303
|
+
|
|
304
|
+
# Run startup handlers
|
|
305
|
+
if self.startup_handlers:
|
|
306
|
+
import asyncio
|
|
307
|
+
asyncio.run(self._run_startup_handlers())
|
|
308
|
+
|
|
309
|
+
print(f"\n{CHECK_MARK} TurboAPI Direct Rust Integration ready!")
|
|
310
|
+
print(f" Visit: http://{host}:{port}")
|
|
311
|
+
|
|
312
|
+
try:
|
|
313
|
+
if RUST_CORE_AVAILABLE:
|
|
314
|
+
# Start the actual Rust server
|
|
315
|
+
print("\n[SERVER] Starting Rust HTTP server with zero Python overhead...")
|
|
316
|
+
self.rust_server.run()
|
|
317
|
+
else:
|
|
318
|
+
print("\n[WARN] Rust core not available - simulation mode")
|
|
319
|
+
print("Press Ctrl+C to stop")
|
|
320
|
+
import time
|
|
321
|
+
while True:
|
|
322
|
+
time.sleep(1)
|
|
323
|
+
|
|
324
|
+
except KeyboardInterrupt:
|
|
325
|
+
print("\n[STOP] Shutting down TurboAPI server...")
|
|
326
|
+
|
|
327
|
+
# Run shutdown handlers
|
|
328
|
+
if self.shutdown_handlers:
|
|
329
|
+
import asyncio
|
|
330
|
+
asyncio.run(self._run_shutdown_handlers())
|
|
331
|
+
|
|
332
|
+
print("[BYE] Server stopped")
|
|
333
|
+
|
|
334
|
+
# Export the correct integration class
|
|
335
|
+
TurboAPI = RustIntegratedTurboAPI
|