turboapi 0.5.2__cp313-cp313-win_amd64.whl → 0.5.21__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.
@@ -229,9 +229,12 @@ class RequestBodyParser:
229
229
  """
230
230
  if not body:
231
231
  return {}
232
-
232
+
233
233
  try:
234
- json_data = json.loads(body.decode('utf-8'))
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 (json.JSONDecodeError, UnicodeDecodeError):
352
- # Keep as string for HTML/Text responses
353
- body = body.decode('utf-8')
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
 
@@ -43,6 +43,10 @@ def classify_handler(handler, route) -> tuple[str, dict[str, str], dict]:
43
43
  if BaseModel is not None and inspect.isclass(annotation) and issubclass(annotation, BaseModel):
44
44
  # Found a model parameter - use fast model path (sync only for now)
45
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
46
50
  continue # Don't add to param_types
47
51
  except TypeError:
48
52
  pass
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: turboapi
3
- Version: 0.5.2
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
@@ -10,20 +10,20 @@ turboapi\main_app.py,sha256=27MWIQaJKfNEUnkjSbGR76E0GNlJk-P4ZqokUAH1j4I,13780
10
10
  turboapi\middleware.py,sha256=xmuzCm07OIdEtMI_PJcrE5GQg7l0PfQTLEbIbZnuJSE,11077
11
11
  turboapi\models.py,sha256=LxWzSy3GXl9BvI8j6hsX4XuQRrspFyLfg4lwi-kwuPE,4606
12
12
  turboapi\openapi.py,sha256=sn5KpV_-50OJZv5NM8pWpLrjmb0abFg9k2sSDMZz5Fs,7517
13
- turboapi\request_handler.py,sha256=4MSekNk2upQVAdySjIK18NV5RfSCcdqUcwIUtaZJVCE,24370
13
+ turboapi\request_handler.py,sha256=9fY-OJ4Jgfy99zBJ7sA0JYb2syQIoiuL7j2rCmRZQI4,25212
14
14
  turboapi\responses.py,sha256=Ry64gbK993s0LUf3jgdBAAAMAnR_6d-o8EDPCAv3vqQ,6261
15
15
  turboapi\routing.py,sha256=mZdDBQqKOfIvPJrLplKrC3sEqqic79qPEMFl0Iw0ugM,7821
16
- turboapi\rust_integration.py,sha256=pn5q_AvVQw27lwxHko5y7-iE51GtL1c_YT5XrrfKD0g,15695
16
+ turboapi\rust_integration.py,sha256=TfP4zOqE4NOUlVr_Lheu6apQ5cZVx2JWJ0kvGHF-nf8,15918
17
17
  turboapi\security.py,sha256=KBzyDkF8CN9-hOEUoRRBTqG0Zc1OtwI7aFInDJYusLM,18064
18
18
  turboapi\server_integration.py,sha256=drUhhTasWgQfyhFiAaHKd987N3mnE0qkMab1ylmqd4c,18340
19
19
  turboapi\staticfiles.py,sha256=w_y4cw0ncYjdO8kZN_LZz5rQ5Iz8OE7TiWBtgAeBZGo,2952
20
20
  turboapi\status.py,sha256=MT9i17mLjksTnX3d5Vr7Wt1M6GVj5SsQ78p1XktRWvQ,3153
21
21
  turboapi\templating.py,sha256=0UKtBLA-MQNutwFOV8sy7gIOLy94dsAQnpkIb4GrBnw,2160
22
22
  turboapi\testclient.py,sha256=g4gm-Nj84JjMPk90Y1TDjZ7sKXzn_Mcf0XrEQvHyU-k,10990
23
- turboapi\turbonet.cp313-win_amd64.pyd,sha256=oqQGb6MefZ2cWcQFSEuxCfsKBueal9-Rju38SxH7Me0,5146624
23
+ turboapi\turbonet.cp313-win_amd64.pyd,sha256=4uwTpJdP9VLI9SXZ7wwH4FsLO-VcD7mizDDi9QGumoo,5142016
24
24
  turboapi\version_check.py,sha256=z3O1vIJsWmG_DO271ayYWSwaDfgpFnfJzYRYyowKYMc,9625
25
25
  turboapi\websockets.py,sha256=FV3tcCXKutgnkkmyJCdTVgGB5eNBLHT3MBPmXAx9Rzk,4356
26
- turboapi-0.5.2.dist-info\METADATA,sha256=_G7zPH9sdzWImSQgpuhpJJDEFDTg-XHQUrpqcFmHejA,18054
27
- turboapi-0.5.2.dist-info\WHEEL,sha256=n_BmF69IyGtioVWE9c3M_zsEfe6-xMZy1v5HCL_6qE0,97
28
- turboapi-0.5.2.dist-info\licenses\LICENSE,sha256=BcaHZdCsG_B-lpQab7W63JiSB8wlewkjk9i1MXss26A,1088
29
- turboapi-0.5.2.dist-info\RECORD,,
26
+ turboapi-0.5.21.dist-info\METADATA,sha256=fhcunMLbHUGX-x7cZyeHM6Bh7szKPgH1NGssZ8rZsWQ,18055
27
+ turboapi-0.5.21.dist-info\WHEEL,sha256=n_BmF69IyGtioVWE9c3M_zsEfe6-xMZy1v5HCL_6qE0,97
28
+ turboapi-0.5.21.dist-info\licenses\LICENSE,sha256=BcaHZdCsG_B-lpQab7W63JiSB8wlewkjk9i1MXss26A,1088
29
+ turboapi-0.5.21.dist-info\RECORD,,