turboapi 0.4.12__cp313-cp313-win_amd64.whl → 0.4.13__cp313-cp313-win_amd64.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 +55 -5
- turboapi/rust_integration.py +5 -82
- turboapi/turbonet.cp313-win_amd64.pyd +0 -0
- {turboapi-0.4.12.dist-info → turboapi-0.4.13.dist-info}/METADATA +1 -1
- {turboapi-0.4.12.dist-info → turboapi-0.4.13.dist-info}/RECORD +6 -6
- {turboapi-0.4.12.dist-info → turboapi-0.4.13.dist-info}/WHEEL +0 -0
turboapi/request_handler.py
CHANGED
|
@@ -18,6 +18,11 @@ class RequestBodyParser:
|
|
|
18
18
|
"""
|
|
19
19
|
Parse JSON body and extract parameters for handler.
|
|
20
20
|
|
|
21
|
+
Supports multiple patterns:
|
|
22
|
+
1. Single parameter (dict/list/Model) - receives entire body
|
|
23
|
+
2. Multiple parameters - extracts fields from JSON
|
|
24
|
+
3. Satya Model - validates entire body
|
|
25
|
+
|
|
21
26
|
Args:
|
|
22
27
|
body: Raw request body bytes
|
|
23
28
|
handler_signature: Signature of the handler function
|
|
@@ -34,9 +39,42 @@ class RequestBodyParser:
|
|
|
34
39
|
raise ValueError(f"Invalid JSON body: {e}")
|
|
35
40
|
|
|
36
41
|
parsed_params = {}
|
|
42
|
+
params_list = list(handler_signature.parameters.items())
|
|
43
|
+
|
|
44
|
+
# PATTERN 1: Single parameter that should receive entire body
|
|
45
|
+
# Examples: handler(data: dict), handler(items: list), handler(request: Model)
|
|
46
|
+
if len(params_list) == 1:
|
|
47
|
+
param_name, param = params_list[0]
|
|
48
|
+
|
|
49
|
+
# Check if parameter is a Satya Model
|
|
50
|
+
try:
|
|
51
|
+
is_satya_model = inspect.isclass(param.annotation) and issubclass(param.annotation, Model)
|
|
52
|
+
except Exception:
|
|
53
|
+
is_satya_model = False
|
|
54
|
+
|
|
55
|
+
if is_satya_model:
|
|
56
|
+
# Validate entire JSON body against Satya model
|
|
57
|
+
try:
|
|
58
|
+
validated_model = param.annotation.model_validate(json_data)
|
|
59
|
+
parsed_params[param_name] = validated_model
|
|
60
|
+
return parsed_params
|
|
61
|
+
except Exception as e:
|
|
62
|
+
raise ValueError(f"Validation error for {param_name}: {e}")
|
|
63
|
+
|
|
64
|
+
# If annotated as dict or list, pass entire body
|
|
65
|
+
elif param.annotation in (dict, list) or param.annotation == inspect.Parameter.empty:
|
|
66
|
+
parsed_params[param_name] = json_data
|
|
67
|
+
return parsed_params
|
|
68
|
+
|
|
69
|
+
# Check for typing.Dict, typing.List, etc.
|
|
70
|
+
origin = get_origin(param.annotation)
|
|
71
|
+
if origin in (dict, list):
|
|
72
|
+
parsed_params[param_name] = json_data
|
|
73
|
+
return parsed_params
|
|
37
74
|
|
|
38
|
-
#
|
|
39
|
-
|
|
75
|
+
# PATTERN 2: Multiple parameters - extract individual fields
|
|
76
|
+
# Example: handler(name: str, age: int, email: str)
|
|
77
|
+
for param_name, param in params_list:
|
|
40
78
|
if param.annotation == inspect.Parameter.empty:
|
|
41
79
|
# No type annotation, try to match by name
|
|
42
80
|
if param_name in json_data:
|
|
@@ -138,9 +176,21 @@ class ResponseHandler:
|
|
|
138
176
|
if isinstance(content, Model):
|
|
139
177
|
content = content.model_dump()
|
|
140
178
|
|
|
141
|
-
#
|
|
142
|
-
|
|
143
|
-
|
|
179
|
+
# Recursively convert any nested Satya models in dicts/lists
|
|
180
|
+
def make_serializable(obj):
|
|
181
|
+
if isinstance(obj, Model):
|
|
182
|
+
return obj.model_dump()
|
|
183
|
+
elif isinstance(obj, dict):
|
|
184
|
+
return {k: make_serializable(v) for k, v in obj.items()}
|
|
185
|
+
elif isinstance(obj, (list, tuple)):
|
|
186
|
+
return [make_serializable(item) for item in obj]
|
|
187
|
+
elif isinstance(obj, (str, int, float, bool, type(None))):
|
|
188
|
+
return obj
|
|
189
|
+
else:
|
|
190
|
+
# Try to convert to string for unknown types
|
|
191
|
+
return str(obj)
|
|
192
|
+
|
|
193
|
+
content = make_serializable(content)
|
|
144
194
|
|
|
145
195
|
return {
|
|
146
196
|
"content": content,
|
turboapi/rust_integration.py
CHANGED
|
@@ -128,91 +128,14 @@ class RustIntegratedTurboAPI(TurboAPI):
|
|
|
128
128
|
# Create enhanced handler with automatic body parsing
|
|
129
129
|
enhanced_handler = create_enhanced_handler(route.handler, route)
|
|
130
130
|
|
|
131
|
-
#
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
|
131
|
+
# For now, just register the original handler
|
|
132
|
+
# TODO: Implement request data passing from Rust to enable enhanced handler
|
|
133
|
+
# The Rust server currently calls handlers with call0() (no arguments)
|
|
134
|
+
# We need to modify the Rust server to pass request data
|
|
212
135
|
self.rust_server.add_route(
|
|
213
136
|
route.method.value,
|
|
214
137
|
route.path,
|
|
215
|
-
|
|
138
|
+
enhanced_handler # Register enhanced handler directly
|
|
216
139
|
)
|
|
217
140
|
|
|
218
141
|
print(f"{CHECK_MARK} Registered {route.method.value} {route.path} with Rust server")
|
|
Binary file
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
turboapi-0.4.
|
|
2
|
-
turboapi-0.4.
|
|
1
|
+
turboapi-0.4.13.dist-info/METADATA,sha256=Cx_c3TzPTzH999Vc5NxdbruBeIuVIISBgN1xXWMVNdM,1482
|
|
2
|
+
turboapi-0.4.13.dist-info/WHEEL,sha256=TJQY77QRLvXq32tEs9ATmwKO6NAOtuKOAw50eyiSmWU,96
|
|
3
3
|
turboapi/__init__.py,sha256=r9Fphtu9ruHFUhSpBMAGxY5en2wvcnsE1nMp2DDRM6w,692
|
|
4
4
|
turboapi/async_limiter.py,sha256=x2qkloPbg2YelDNUXKya2BwBTq5zVxDHxuaQspIgYBg,2416
|
|
5
5
|
turboapi/async_pool.py,sha256=UVm0A-0jIN4V43jY8a5XEU_L0SSyWGMV2bs5FiQGr2M,4489
|
|
@@ -7,11 +7,11 @@ turboapi/decorators.py,sha256=jjJrIXZ3y_yJ231ar24hS09OCDtTqmYA7arpIOcr2kk,1788
|
|
|
7
7
|
turboapi/main_app.py,sha256=_rH5xUahFvyqk8Y9O4rs7v5m1q4_AbkBHitg04KL6O4,11678
|
|
8
8
|
turboapi/middleware.py,sha256=iqtklH5_GMICuAmmxMBfaFSNZkR8wHSNbwhNscGe-pA,11200
|
|
9
9
|
turboapi/models.py,sha256=VCU68f9MGtDdFb4crsx2e0SHghICg8zjU8OumfdpZLQ,5363
|
|
10
|
-
turboapi/request_handler.py,sha256=
|
|
10
|
+
turboapi/request_handler.py,sha256=xS9b6HqAGUDRp8D4O09i6jhxmw56771DfvjhRHaMDwU,10752
|
|
11
11
|
turboapi/routing.py,sha256=iCbty56a2J9qnCtxIHQtYf66ZoKVxgISxwCxYvGmgEs,7746
|
|
12
|
-
turboapi/rust_integration.py,sha256=
|
|
12
|
+
turboapi/rust_integration.py,sha256=d1xqC8cX8kehDlRuDQhVbMPw-cQysmI2ZR-j29JD8GA,10676
|
|
13
13
|
turboapi/security.py,sha256=-XgwBhiqQZdfU7oKLHi-3xN_UwlKiQxpfSQ6kTA0ko8,17230
|
|
14
14
|
turboapi/server_integration.py,sha256=drUhhTasWgQfyhFiAaHKd987N3mnE0qkMab1ylmqd4c,18340
|
|
15
|
-
turboapi/turbonet.cp313-win_amd64.pyd,sha256=
|
|
15
|
+
turboapi/turbonet.cp313-win_amd64.pyd,sha256=cIBGcGkdVilk6CEYDc_zD5QatdYSiAWOYyNrG9oBFcw,3664384
|
|
16
16
|
turboapi/version_check.py,sha256=z3O1vIJsWmG_DO271ayYWSwaDfgpFnfJzYRYyowKYMc,9625
|
|
17
|
-
turboapi-0.4.
|
|
17
|
+
turboapi-0.4.13.dist-info/RECORD,,
|
|
File without changes
|