🐍 p3: native_async decorator + sse_native + async markers (ADR-089)

swadhinbiswas committed 2 months ago
364904a
1 parent b750899
commit 364904a
1 file changed
+48 -0
Showing 1 changed file+48-0
β–Όcrates/justapi-py/python/justapi/app.py
+48-0
398
398
return func
399
399
return wrapper
400
400
401
+
402
+def native_async(func=None):
403
+ """Mark a handler as **native-async** β€” its framework operations (DB
404
+ queries, SSE streaming, HTTP calls, serialization) run in Rust at native
405
+ speed, and the async dispatch uses the callback-driven resolution path
406
+ (no blocking wait, no thread hop). The user's own Python logic still runs
407
+ at Python speed (that is physics); everything the framework does for the
408
+ request is Rust (ADR-089).
409
+
410
+ from justapi import JustAPIApp, native_async
411
+
412
+ app = JustAPIApp()
413
+
414
+ @app.get("/users/{id}")
415
+ @native_async
416
+ async def get_user(request):
417
+ row = await app.db.fetch_one("SELECT * FROM users WHERE id = ?",
418
+ [request.path_params["id"]])
419
+ return row
420
+
421
+ Use it on any async route whose heavy lifting is DB / IO / streaming.
422
+ """
423
+ def wrapper(f):
424
+ f.__native_async__ = True
425
+ return f
426
+
427
+ if func is None:
428
+ return wrapper
429
+ return wrapper(func)
430
+
401
431
class _AppState:
402
432
def __setattr__(self, name, value):
403
433
self.__dict__[name] = value
1034
1064
wrapper = sync_wrapper
1035
1065
1036
1066
wrapper._needs_request = needs_request
1067
+ # Per-handler flag: async handler (or async deps). Rust routes async
1068
+ # handlers to the parallel async pool (Granian model, ADR-087).
1069
+ wrapper._is_async = is_async
1070
+ # native_async marker: propagates `__native_async__` to the wrapper so
1071
+ # Rust can route it to the fastest dispatch (ADR-089).
1072
+ wrapper._is_native_async = bool(getattr(handler, "__native_async__", False))
1037
1073
return wrapper
1038
1074
1039
1075
def _resolve_batch_config(self, func):
1899
1935
self._app.get(path, self._wrap_sse_handler(handler))
1900
1936
return handler
1901
1937
1938
+ def sse_native(self, path: str, count: int = 10, interval_ms: int = 100):
1939
+ """Register a **Rust-native** Server-Sent Events route (ADR-088).
1940
+
1941
+ The server streams ``count`` events at ``interval_ms`` spacing, generated
1942
+ entirely in Rust (tokio + mpsc + streaming response). No Python handler,
1943
+ no GIL, no generator pump β€” the stream runs at native speed.
1944
+
1945
+ app.sse_native("/events", count=1000, interval_ms=0) # 1000 events, no delay
1946
+ """
1947
+ self._app.sse_native(path, count, interval_ms)
1948
+ return self
1949
+
1902
1950
def websocket(self, path: str, handler=None):
1903
1951
"""Register a WebSocket route.
1904
1952
1905
1953