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.
@@ -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