tigrbl-runtime 0.4.5.dev4__py3-none-any.whl → 0.4.5.dev8__py3-none-any.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.
- tigrbl_runtime/channel/_asgi_jsonrpc.py +11 -0
- tigrbl_runtime/channel/_asgi_send.py +79 -5
- tigrbl_runtime/channel/_asgi_webtransport.py +31 -4
- tigrbl_runtime/executors/packed/_shared.py +2 -3
- tigrbl_runtime/executors/packed/batch.py +7 -46
- tigrbl_runtime/executors/packed/execute.py +43 -18
- tigrbl_runtime/executors/packed/input_compile.py +21 -12
- tigrbl_runtime/executors/packed/input_extract.py +1 -1
- tigrbl_runtime/executors/packed/route_select.py +98 -20
- tigrbl_runtime/executors/packed/route_select_websocket.py +34 -0
- tigrbl_runtime/lowering_services.py +22 -0
- {tigrbl_runtime-0.4.5.dev4.dist-info → tigrbl_runtime-0.4.5.dev8.dist-info}/METADATA +15 -11
- {tigrbl_runtime-0.4.5.dev4.dist-info → tigrbl_runtime-0.4.5.dev8.dist-info}/RECORD +16 -14
- {tigrbl_runtime-0.4.5.dev4.dist-info → tigrbl_runtime-0.4.5.dev8.dist-info}/WHEEL +1 -1
- {tigrbl_runtime-0.4.5.dev4.dist-info → tigrbl_runtime-0.4.5.dev8.dist-info}/licenses/LICENSE +0 -0
- {tigrbl_runtime-0.4.5.dev4.dist-info → tigrbl_runtime-0.4.5.dev8.dist-info}/licenses/NOTICE +0 -0
|
@@ -36,3 +36,14 @@ def _resolve_jsonrpc_endpoint(ctx: Any, channel: OpChannel) -> str | None:
|
|
|
36
36
|
if path == _normalize_path(mapped_path):
|
|
37
37
|
return endpoint
|
|
38
38
|
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
_NO_ERROR_DATA = object()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _jsonrpc_error_payload(code: int, message: str, request_id: Any, *, data: Any = _NO_ERROR_DATA) -> dict[str, Any]:
|
|
45
|
+
"""Construct the existing compatibility error envelope without choosing policy."""
|
|
46
|
+
error: dict[str, Any] = {"code": code, "message": message}
|
|
47
|
+
if data is not _NO_ERROR_DATA:
|
|
48
|
+
error["data"] = data
|
|
49
|
+
return {"jsonrpc": "2.0", "error": error, "id": request_id}
|
|
@@ -19,6 +19,52 @@ from ._asgi_webtransport import (
|
|
|
19
19
|
)
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
def _webtransport_event_projection(ctx: Any) -> Mapping[str, Any]:
|
|
23
|
+
projection = ctx.get("channel_event_projection")
|
|
24
|
+
if isinstance(projection, Mapping):
|
|
25
|
+
return projection
|
|
26
|
+
channel = ctx.get("channel")
|
|
27
|
+
state = getattr(channel, "state", None) if channel is not None else None
|
|
28
|
+
if isinstance(state, dict):
|
|
29
|
+
projection = state.get("event_projection")
|
|
30
|
+
if isinstance(projection, Mapping):
|
|
31
|
+
return projection
|
|
32
|
+
return {}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _is_receive_only_webtransport_stream(ctx: Any) -> bool:
|
|
36
|
+
projection = _webtransport_event_projection(ctx)
|
|
37
|
+
if (
|
|
38
|
+
projection.get("lane") == "unidi_client_stream"
|
|
39
|
+
or projection.get("exchange") == "client_stream"
|
|
40
|
+
):
|
|
41
|
+
return True
|
|
42
|
+
message = ctx.get("channel_message")
|
|
43
|
+
return bool(
|
|
44
|
+
isinstance(message, Mapping)
|
|
45
|
+
and message.get("type") == "webtransport.stream.receive"
|
|
46
|
+
and message.get("stream_direction") == "client_to_server"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _validate_receive_only_webtransport_output(payload: Any) -> None:
|
|
51
|
+
if payload is None:
|
|
52
|
+
return
|
|
53
|
+
if not isinstance(payload, Mapping) or not any(
|
|
54
|
+
key in payload
|
|
55
|
+
for key in ("bidirectional_streams", "unidirectional_streams", "datagrams")
|
|
56
|
+
):
|
|
57
|
+
raise ValueError(
|
|
58
|
+
"cannot automatically reply on a client_to_server WebTransport stream; "
|
|
59
|
+
"return None or emit an explicit server_to_client stream or datagram"
|
|
60
|
+
)
|
|
61
|
+
if payload.get("bidirectional_streams"):
|
|
62
|
+
raise ValueError(
|
|
63
|
+
"bidirectional_streams output is invalid for a client_to_server "
|
|
64
|
+
"WebTransport stream; use unidirectional_streams or datagrams"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
22
68
|
async def _send_session_payload(
|
|
23
69
|
env: Any,
|
|
24
70
|
payload: Any,
|
|
@@ -87,11 +133,15 @@ async def _send_webtransport_payload(env: Any, ctx: Any, payload: Any) -> None:
|
|
|
87
133
|
for key in ("bidirectional_streams", "unidirectional_streams", "datagrams")
|
|
88
134
|
)
|
|
89
135
|
if structured_payload:
|
|
90
|
-
if
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
136
|
+
if (
|
|
137
|
+
message_type
|
|
138
|
+
in {
|
|
139
|
+
"webtransport.stream.receive",
|
|
140
|
+
"webtransport.datagram.receive",
|
|
141
|
+
}
|
|
142
|
+
and message not in queue
|
|
143
|
+
):
|
|
144
|
+
queue.append(message)
|
|
95
145
|
if not queue:
|
|
96
146
|
raise ValueError(
|
|
97
147
|
"structured WebTransport payloads require inbound stream.receive or datagram.receive events"
|
|
@@ -125,6 +175,12 @@ async def _send_webtransport_payload(env: Any, ctx: Any, payload: Any) -> None:
|
|
|
125
175
|
)
|
|
126
176
|
if payload is not None:
|
|
127
177
|
base = {**message, "session_id": session_id}
|
|
178
|
+
temp = getattr(ctx, "temp", None)
|
|
179
|
+
dispatch = temp.get("dispatch") if isinstance(temp, Mapping) else None
|
|
180
|
+
if isinstance(dispatch, Mapping):
|
|
181
|
+
request = dispatch.get("rpc") or dispatch.get("rpc_envelope")
|
|
182
|
+
if isinstance(request, Mapping) and request.get("id") is not None:
|
|
183
|
+
base["jsonrpc_request_id"] = request.get("id")
|
|
128
184
|
if structured_payload:
|
|
129
185
|
for event in _webtransport_structured_payload_events(
|
|
130
186
|
session_id=session_id,
|
|
@@ -151,6 +207,14 @@ async def _send_webtransport_payload(env: Any, ctx: Any, payload: Any) -> None:
|
|
|
151
207
|
)
|
|
152
208
|
await _run_webtransport_hooks(env, ctx, direction="send", message=event)
|
|
153
209
|
await send(event)
|
|
210
|
+
if isinstance(state, dict):
|
|
211
|
+
raw_queue = state.get("receive_queue")
|
|
212
|
+
consumed = min(len(queue), len(raw_queue or ()))
|
|
213
|
+
if isinstance(raw_queue, deque):
|
|
214
|
+
for _ in range(consumed):
|
|
215
|
+
raw_queue.popleft()
|
|
216
|
+
elif isinstance(raw_queue, list):
|
|
217
|
+
del raw_queue[:consumed]
|
|
154
218
|
else:
|
|
155
219
|
event = _webtransport_payload_event(base=base, payload=payload)
|
|
156
220
|
if isinstance(session, WebTransportSessionState):
|
|
@@ -222,6 +286,16 @@ async def send_transport_via_channel(env: Any, ctx: Any) -> None:
|
|
|
222
286
|
if payload is None:
|
|
223
287
|
payload = getattr(ctx, "result", None)
|
|
224
288
|
if scope_type == "webtransport":
|
|
289
|
+
if _is_receive_only_webtransport_stream(ctx):
|
|
290
|
+
_validate_receive_only_webtransport_output(payload)
|
|
291
|
+
if payload is None:
|
|
292
|
+
if isinstance(state, dict):
|
|
293
|
+
state["egress_disposition"] = "suppressed_receive_only"
|
|
294
|
+
state["emitted"] = False
|
|
295
|
+
if isinstance(egress, dict):
|
|
296
|
+
egress["response_suppressed"] = True
|
|
297
|
+
egress["suppression_reason"] = "receive_only_stream"
|
|
298
|
+
return
|
|
225
299
|
await _send_webtransport_payload(env, ctx, payload)
|
|
226
300
|
else:
|
|
227
301
|
await _send_session_payload(
|
|
@@ -15,9 +15,12 @@ from tigrbl_kernel.channel_taxonomy import (
|
|
|
15
15
|
webtransport_event_metadata as _webtransport_event_metadata,
|
|
16
16
|
)
|
|
17
17
|
from tigrbl_kernel.webtransport_events import validate_webtransport_event_payload
|
|
18
|
+
from tigrbl_kernel.webtransport_session_plan import WebTransportSessionPlan
|
|
18
19
|
from tigrbl_atoms.runtime_channel import WebTransportSessionState
|
|
19
20
|
from tigrbl_typing.channel import OpChannel
|
|
20
21
|
|
|
22
|
+
MAX_EAGER_DRAIN_EVENTS = 16
|
|
23
|
+
|
|
21
24
|
|
|
22
25
|
def _webtransport_scope_state(env: Any) -> dict[str, Any]:
|
|
23
26
|
scope = getattr(env, "scope", {}) or {}
|
|
@@ -242,6 +245,20 @@ async def _receive_webtransport_session_messages(
|
|
|
242
245
|
queue: deque[Mapping[str, Any]] = deque()
|
|
243
246
|
session: WebTransportSessionState | None = None
|
|
244
247
|
|
|
248
|
+
session_plan = shared_state.get("session_plan")
|
|
249
|
+
if not isinstance(session_plan, WebTransportSessionPlan):
|
|
250
|
+
session_plan = WebTransportSessionPlan()
|
|
251
|
+
shared_state["session_plan"] = session_plan
|
|
252
|
+
|
|
253
|
+
async def _receive_record_or_event() -> Mapping[str, Any]:
|
|
254
|
+
if session_plan.has_pending():
|
|
255
|
+
return session_plan.pop_pending()
|
|
256
|
+
while True:
|
|
257
|
+
raw = _enrich_webtransport_message(env, await receive())
|
|
258
|
+
session_plan.project_receive(raw)
|
|
259
|
+
if session_plan.has_pending():
|
|
260
|
+
return session_plan.pop_pending()
|
|
261
|
+
|
|
245
262
|
def _ensure_session(message: Mapping[str, Any]) -> WebTransportSessionState | None:
|
|
246
263
|
nonlocal session
|
|
247
264
|
session_id = message.get("session_id") or _webtransport_scope_session_id(env)
|
|
@@ -292,6 +309,8 @@ async def _receive_webtransport_session_messages(
|
|
|
292
309
|
channel="receive",
|
|
293
310
|
payload={**message, "session_id": state.get("session_id")},
|
|
294
311
|
)
|
|
312
|
+
state["event_projection"] = dict(projection)
|
|
313
|
+
ctx["channel_event_projection"] = dict(projection)
|
|
295
314
|
if current_session is not None:
|
|
296
315
|
current_session.apply_event(
|
|
297
316
|
event=message_type,
|
|
@@ -334,18 +353,26 @@ async def _receive_webtransport_session_messages(
|
|
|
334
353
|
return
|
|
335
354
|
ctx["channel_message"] = message
|
|
336
355
|
|
|
337
|
-
message = await
|
|
356
|
+
message = await _receive_record_or_event()
|
|
338
357
|
await _record_payload_message(message)
|
|
339
358
|
if message.get("type") == "webtransport.connect":
|
|
340
359
|
if "channel_message" not in ctx:
|
|
341
|
-
message = await
|
|
360
|
+
message = await _receive_record_or_event()
|
|
342
361
|
await _record_payload_message(message)
|
|
343
362
|
|
|
344
|
-
|
|
363
|
+
drained_events = 0
|
|
364
|
+
while (
|
|
365
|
+
shared_state.get("eager_drain") is True
|
|
366
|
+
and not state.get("disconnected")
|
|
367
|
+
and drained_events < MAX_EAGER_DRAIN_EVENTS
|
|
368
|
+
):
|
|
345
369
|
try:
|
|
346
|
-
message = await asyncio.wait_for(
|
|
370
|
+
message = await asyncio.wait_for(
|
|
371
|
+
_receive_record_or_event(), timeout=0.001
|
|
372
|
+
)
|
|
347
373
|
except TimeoutError:
|
|
348
374
|
break
|
|
349
375
|
await _record_payload_message(message)
|
|
376
|
+
drained_events += 1
|
|
350
377
|
|
|
351
378
|
state["receive_queue"] = queue
|
|
@@ -13,7 +13,7 @@ from types import SimpleNamespace
|
|
|
13
13
|
from tigrbl_atoms._ctx import _ctx_view
|
|
14
14
|
from tigrbl_core.config.constants import __JSONRPC_DEFAULT_ENDPOINT_MAPPINGS__
|
|
15
15
|
from tigrbl_atoms.types import build_error_ctx, error_phase_for, select_error_edge
|
|
16
|
-
from
|
|
16
|
+
from tigrbl_core.schema.materialize import _materialize_field_value
|
|
17
17
|
from tigrbl_atoms.atoms.transport.websocket_unary import (
|
|
18
18
|
DirectWebSocketUnary as _DirectWebSocketUnary,
|
|
19
19
|
)
|
|
@@ -91,6 +91,7 @@ from tigrbl_kernel.packed_selectors import (
|
|
|
91
91
|
resolve_hot_exact_websocket_routes as _kernel_resolve_hot_exact_websocket_routes,
|
|
92
92
|
resolve_program_id_from_exact_route as _kernel_resolve_program_id_from_exact_route,
|
|
93
93
|
resolve_program_id_from_exact_websocket as _kernel_resolve_program_id_from_exact_websocket,
|
|
94
|
+
resolve_webtransport_program as _kernel_resolve_webtransport_program,
|
|
94
95
|
)
|
|
95
96
|
from tigrbl_typing.status.exceptions import HTTPException
|
|
96
97
|
from tigrbl_typing.phases import canonicalize_phase_input as normalize_phase
|
|
@@ -165,5 +166,3 @@ __all__ = [
|
|
|
165
166
|
for name in globals()
|
|
166
167
|
if not name.startswith("__") or name == "__JSONRPC_DEFAULT_ENDPOINT_MAPPINGS__"
|
|
167
168
|
]
|
|
168
|
-
|
|
169
|
-
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from ._shared import *
|
|
4
|
+
from ...channel._asgi_jsonrpc import _jsonrpc_error_payload
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class _PackedBatchMixin:
|
|
@@ -210,15 +211,7 @@ class _PackedBatchMixin:
|
|
|
210
211
|
|
|
211
212
|
rpc_code = _HTTP_TO_RPC.get(int(status_code), -32603)
|
|
212
213
|
data = dict(detail) if isinstance(detail, Mapping) else {"detail": detail}
|
|
213
|
-
return
|
|
214
|
-
"jsonrpc": "2.0",
|
|
215
|
-
"error": {
|
|
216
|
-
"code": rpc_code,
|
|
217
|
-
"message": ERROR_MESSAGES.get(rpc_code, "Internal error"),
|
|
218
|
-
"data": data,
|
|
219
|
-
},
|
|
220
|
-
"id": rpc_id,
|
|
221
|
-
}
|
|
214
|
+
return _jsonrpc_error_payload(rpc_code, ERROR_MESSAGES.get(rpc_code, 'Internal error'), rpc_id, data=data)
|
|
222
215
|
|
|
223
216
|
@staticmethod
|
|
224
217
|
def _reject_jsonrpc_wrapper_keys(
|
|
@@ -273,11 +266,7 @@ class _PackedBatchMixin:
|
|
|
273
266
|
for item in batch_payload:
|
|
274
267
|
if not isinstance(item, Mapping):
|
|
275
268
|
responses.append(
|
|
276
|
-
|
|
277
|
-
"jsonrpc": "2.0",
|
|
278
|
-
"error": {"code": -32600, "message": "Invalid Request"},
|
|
279
|
-
"id": None,
|
|
280
|
-
}
|
|
269
|
+
_jsonrpc_error_payload(-32600, 'Invalid Request', None)
|
|
281
270
|
)
|
|
282
271
|
continue
|
|
283
272
|
|
|
@@ -285,25 +274,13 @@ class _PackedBatchMixin:
|
|
|
285
274
|
method = item.get("method")
|
|
286
275
|
if not isinstance(method, str):
|
|
287
276
|
responses.append(
|
|
288
|
-
|
|
289
|
-
"jsonrpc": "2.0",
|
|
290
|
-
"error": {"code": -32601, "message": "Method not found"},
|
|
291
|
-
"id": rpc_id,
|
|
292
|
-
}
|
|
277
|
+
_jsonrpc_error_payload(-32601, 'Method not found', rpc_id)
|
|
293
278
|
)
|
|
294
279
|
continue
|
|
295
280
|
|
|
296
281
|
if not callable(router_or_app) or not scope:
|
|
297
282
|
responses.append(
|
|
298
|
-
{
|
|
299
|
-
"jsonrpc": "2.0",
|
|
300
|
-
"error": {
|
|
301
|
-
"code": -32603,
|
|
302
|
-
"message": "Internal error",
|
|
303
|
-
"data": {"detail": "RPC invoker unavailable."},
|
|
304
|
-
},
|
|
305
|
-
"id": rpc_id,
|
|
306
|
-
}
|
|
283
|
+
_jsonrpc_error_payload(-32603, 'Internal error', rpc_id, data={'detail': 'RPC invoker unavailable.'})
|
|
307
284
|
)
|
|
308
285
|
continue
|
|
309
286
|
|
|
@@ -323,15 +300,7 @@ class _PackedBatchMixin:
|
|
|
323
300
|
await router_or_app(dict(scope), _receive, _send)
|
|
324
301
|
except Exception as exc:
|
|
325
302
|
responses.append(
|
|
326
|
-
{
|
|
327
|
-
"jsonrpc": "2.0",
|
|
328
|
-
"error": {
|
|
329
|
-
"code": -32603,
|
|
330
|
-
"message": "Internal error",
|
|
331
|
-
"data": {"detail": str(exc)},
|
|
332
|
-
},
|
|
333
|
-
"id": rpc_id,
|
|
334
|
-
}
|
|
303
|
+
_jsonrpc_error_payload(-32603, 'Internal error', rpc_id, data={'detail': str(exc)})
|
|
335
304
|
)
|
|
336
305
|
continue
|
|
337
306
|
|
|
@@ -349,15 +318,7 @@ class _PackedBatchMixin:
|
|
|
349
318
|
try:
|
|
350
319
|
decoded = json.loads(response_body.decode("utf-8"))
|
|
351
320
|
except Exception:
|
|
352
|
-
decoded = {
|
|
353
|
-
"jsonrpc": "2.0",
|
|
354
|
-
"error": {
|
|
355
|
-
"code": -32603,
|
|
356
|
-
"message": "Internal error",
|
|
357
|
-
"data": {"detail": response_body.decode("utf-8", errors="replace")},
|
|
358
|
-
},
|
|
359
|
-
"id": rpc_id,
|
|
360
|
-
}
|
|
321
|
+
decoded = _jsonrpc_error_payload(-32603, 'Internal error', rpc_id, data={'detail': response_body.decode('utf-8', errors='replace')})
|
|
361
322
|
if isinstance(decoded, Mapping):
|
|
362
323
|
response = dict(decoded)
|
|
363
324
|
if response.get("id") is None:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from ._shared import *
|
|
4
|
+
from ...channel._asgi_jsonrpc import _jsonrpc_error_payload
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class _PackedExecuteMixin:
|
|
@@ -29,7 +30,11 @@ class _PackedExecuteMixin:
|
|
|
29
30
|
):
|
|
30
31
|
request = self._ensure_hot_request(ctx, hot)
|
|
31
32
|
body_bytes = await self._ensure_body_bytes(ctx, hot)
|
|
32
|
-
if
|
|
33
|
+
if (
|
|
34
|
+
request is not None
|
|
35
|
+
and body_bytes is not None
|
|
36
|
+
and hasattr(request, "body")
|
|
37
|
+
):
|
|
33
38
|
request.body = body_bytes
|
|
34
39
|
if not hot.parsed_json_loaded:
|
|
35
40
|
parsed = None
|
|
@@ -40,7 +45,10 @@ class _PackedExecuteMixin:
|
|
|
40
45
|
parsed = None
|
|
41
46
|
hot.parsed_json = parsed
|
|
42
47
|
hot.parsed_json_loaded = True
|
|
43
|
-
if
|
|
48
|
+
if (
|
|
49
|
+
isinstance(hot.parsed_json, Mapping)
|
|
50
|
+
and hot.parsed_json.get("jsonrpc") == "2.0"
|
|
51
|
+
):
|
|
44
52
|
params = hot.parsed_json.get("params", {})
|
|
45
53
|
if isinstance(params, Mapping) and set(params) == {"params"}:
|
|
46
54
|
egress = temp.setdefault("egress", {})
|
|
@@ -62,21 +70,30 @@ class _PackedExecuteMixin:
|
|
|
62
70
|
)
|
|
63
71
|
return
|
|
64
72
|
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
if hot.scope_type == "webtransport":
|
|
74
|
+
program_id = self._prime_webtransport_program(ctx, env, plan, packed)
|
|
75
|
+
else:
|
|
76
|
+
program_id = self._require_program_id_from_ctx(ctx)
|
|
77
|
+
if program_id < 0 and hot.scope_type != "webtransport":
|
|
67
78
|
program_id = self._prime_exact_route_program(ctx, env, packed)
|
|
68
|
-
if program_id < 0:
|
|
69
|
-
program_id = await self._prime_exact_jsonrpc_program(
|
|
70
|
-
|
|
71
|
-
)
|
|
72
|
-
if program_id < 0:
|
|
79
|
+
if program_id < 0 and hot.scope_type != "webtransport":
|
|
80
|
+
program_id = await self._prime_exact_jsonrpc_program(ctx, env, plan, packed)
|
|
81
|
+
if program_id < 0 and hot.scope_type != "webtransport":
|
|
73
82
|
program_id = self._prime_exact_channel_program(ctx, env, plan, packed)
|
|
74
83
|
if program_id < 0:
|
|
75
84
|
program_id = await self._probe_ingress_for_program(ctx, plan, packed)
|
|
76
85
|
if program_id < 0:
|
|
77
86
|
scope = getattr(env, "scope", {}) or {}
|
|
78
|
-
|
|
79
|
-
|
|
87
|
+
wt_selection = temp.get("webtransport_selection")
|
|
88
|
+
disposition = getattr(wt_selection, "disposition", None)
|
|
89
|
+
rpc_envelope = getattr(wt_selection, "rpc_envelope", None)
|
|
90
|
+
if disposition in {"method_not_found", "invalid_jsonrpc"}:
|
|
91
|
+
ctx.result = _jsonrpc_error_payload(
|
|
92
|
+
-32601 if disposition == "method_not_found" else -32600,
|
|
93
|
+
"Method not found" if disposition == "method_not_found" else "Invalid Request",
|
|
94
|
+
rpc_envelope.get("id") if isinstance(rpc_envelope, Mapping) else None,
|
|
95
|
+
)
|
|
96
|
+
if disposition in {"lifecycle", "method_not_found", "invalid_jsonrpc"} or isinstance(hot.egress_transport_response, dict):
|
|
80
97
|
await _send_transport_response(env, ctx)
|
|
81
98
|
return
|
|
82
99
|
|
|
@@ -176,7 +193,7 @@ class _PackedExecuteMixin:
|
|
|
176
193
|
error_phase_segments = self._resolve_error_segments(
|
|
177
194
|
packed,
|
|
178
195
|
program_id,
|
|
179
|
-
|
|
196
|
+
)
|
|
180
197
|
|
|
181
198
|
try:
|
|
182
199
|
await self._run_hot_jsonrpc_security_dependencies(
|
|
@@ -190,10 +207,11 @@ class _PackedExecuteMixin:
|
|
|
190
207
|
opview = getattr(ctx, "opview", None)
|
|
191
208
|
schema_in = getattr(opview, "schema_in", None)
|
|
192
209
|
field_names = tuple(getattr(schema_in, "fields", ()) or ())
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
210
|
+
if field_names:
|
|
211
|
+
self._reject_jsonrpc_wrapper_keys(
|
|
212
|
+
hot.parsed_json.get("params", {}),
|
|
213
|
+
field_names=field_names,
|
|
214
|
+
)
|
|
197
215
|
await self._resolve_program_runner(
|
|
198
216
|
packed,
|
|
199
217
|
program_id,
|
|
@@ -252,8 +270,15 @@ class _PackedExecuteMixin:
|
|
|
252
270
|
status_code = int(getattr(std, "status_code", 500) or 500)
|
|
253
271
|
persistence_error = self._is_persistence_exception(exc)
|
|
254
272
|
if persistence_error:
|
|
255
|
-
status_code
|
|
256
|
-
|
|
273
|
+
if status_code >= 500:
|
|
274
|
+
status_code = 500
|
|
275
|
+
detail = "Internal error"
|
|
276
|
+
else:
|
|
277
|
+
detail = (
|
|
278
|
+
"Conflict"
|
|
279
|
+
if status_code == 409
|
|
280
|
+
else "Persistence constraint failed"
|
|
281
|
+
)
|
|
257
282
|
payload = self._jsonrpc_error_payload(
|
|
258
283
|
ctx,
|
|
259
284
|
status_code,
|
|
@@ -102,7 +102,9 @@ class _PackedInputCompileMixin:
|
|
|
102
102
|
if not (0 <= slot_id < len(field_names)):
|
|
103
103
|
continue
|
|
104
104
|
field_name = field_names[slot_id]
|
|
105
|
-
field_meta =
|
|
105
|
+
field_meta = (
|
|
106
|
+
by_field.get(field_name, {}) if isinstance(by_field, Mapping) else {}
|
|
107
|
+
)
|
|
106
108
|
source_mask = self._hot_int_at(
|
|
107
109
|
packed,
|
|
108
110
|
"param_shape_source_masks",
|
|
@@ -156,7 +158,11 @@ class _PackedInputCompileMixin:
|
|
|
156
158
|
)
|
|
157
159
|
paired_index = getattr(opview, "paired_index", {}) or {}
|
|
158
160
|
for field_name, desc in paired_index.items():
|
|
159
|
-
if
|
|
161
|
+
if (
|
|
162
|
+
isinstance(field_name, str)
|
|
163
|
+
and field_name
|
|
164
|
+
and field_name not in field_index
|
|
165
|
+
):
|
|
160
166
|
reserved_input_keys.add(field_name)
|
|
161
167
|
alias = desc.get("alias") if isinstance(desc, Mapping) else None
|
|
162
168
|
if isinstance(alias, str) and alias:
|
|
@@ -217,18 +223,15 @@ class _PackedInputCompileMixin:
|
|
|
217
223
|
continue
|
|
218
224
|
|
|
219
225
|
target_type = field_plan.py_type
|
|
220
|
-
if value is not None and
|
|
221
|
-
|
|
222
|
-
value,
|
|
223
|
-
|
|
224
|
-
allow=field_plan.coerce,
|
|
225
|
-
)
|
|
226
|
-
if not ok:
|
|
226
|
+
if value is not None and target_type is not None:
|
|
227
|
+
try:
|
|
228
|
+
new_val = _materialize_field_value(value, target_type)
|
|
229
|
+
except Exception:
|
|
227
230
|
errors.append(
|
|
228
231
|
{
|
|
229
232
|
"field": field_plan.field_name,
|
|
230
233
|
"code": "type_mismatch",
|
|
231
|
-
"message":
|
|
234
|
+
"message": f"Could not materialize {target_type!s}.",
|
|
232
235
|
}
|
|
233
236
|
)
|
|
234
237
|
continue
|
|
@@ -267,7 +270,9 @@ class _PackedInputCompileMixin:
|
|
|
267
270
|
)
|
|
268
271
|
|
|
269
272
|
hot.compiled_in_invalid = bool(errors)
|
|
270
|
-
hot.compiled_in_errors =
|
|
273
|
+
hot.compiled_in_errors = (
|
|
274
|
+
tuple(dict(error) for error in errors) if errors else None
|
|
275
|
+
)
|
|
271
276
|
hot.compiled_in_coerced = tuple(coerced)
|
|
272
277
|
hot.in_present_names = tuple(
|
|
273
278
|
field_names[idx]
|
|
@@ -306,7 +311,11 @@ class _PackedInputCompileMixin:
|
|
|
306
311
|
|
|
307
312
|
absent.append(field_plan.field_name)
|
|
308
313
|
default_fn = field_plan.default_factory
|
|
309
|
-
if
|
|
314
|
+
if (
|
|
315
|
+
callable(default_fn)
|
|
316
|
+
and field_plan.in_enabled
|
|
317
|
+
and not field_plan.is_virtual
|
|
318
|
+
):
|
|
310
319
|
if view is None:
|
|
311
320
|
view = _ctx_view(ctx)
|
|
312
321
|
try:
|
|
@@ -185,7 +185,7 @@ class _PackedInputExtractMixin:
|
|
|
185
185
|
body_payload = hot.parsed_json
|
|
186
186
|
if hot.transport_kind_id == _TRANSPORT_KIND_JSONRPC and isinstance(body_payload, dict):
|
|
187
187
|
body_payload = body_payload.get("params", {})
|
|
188
|
-
if hot.transport_kind_id == _TRANSPORT_KIND_JSONRPC:
|
|
188
|
+
if hot.transport_kind_id == _TRANSPORT_KIND_JSONRPC and plan.field_names:
|
|
189
189
|
self._reject_jsonrpc_wrapper_keys(
|
|
190
190
|
body_payload,
|
|
191
191
|
field_names=plan.field_names,
|
|
@@ -1,34 +1,110 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from ._shared import *
|
|
4
|
+
from .route_select_websocket import _PackedWebSocketRouteSelectMixin
|
|
4
5
|
|
|
5
6
|
|
|
6
|
-
class _PackedRouteSelectMixin:
|
|
7
|
-
|
|
8
|
-
self,
|
|
9
|
-
plan: KernelPlan,
|
|
10
|
-
packed: PackedKernel,
|
|
11
|
-
) -> Mapping[tuple[str, str], int]:
|
|
12
|
-
return _kernel_resolve_hot_exact_websocket_routes(
|
|
13
|
-
plan,
|
|
14
|
-
packed,
|
|
15
|
-
self._hot_exact_websocket_route_cache,
|
|
16
|
-
)
|
|
7
|
+
class _PackedRouteSelectMixin(_PackedWebSocketRouteSelectMixin):
|
|
8
|
+
|
|
17
9
|
|
|
18
|
-
def
|
|
10
|
+
def _prime_webtransport_program(
|
|
19
11
|
self,
|
|
12
|
+
ctx: _Ctx,
|
|
13
|
+
env: Any,
|
|
20
14
|
plan: KernelPlan,
|
|
21
15
|
packed: PackedKernel,
|
|
22
|
-
protocol: str,
|
|
23
|
-
path: str,
|
|
24
16
|
) -> int:
|
|
25
|
-
|
|
17
|
+
del packed
|
|
18
|
+
hot = self._ensure_hot_ctx(ctx, env)
|
|
19
|
+
if hot.scope_type != "webtransport" or not hot.path:
|
|
20
|
+
return -1
|
|
21
|
+
message = ctx.get("channel_message")
|
|
22
|
+
if not isinstance(message, Mapping):
|
|
23
|
+
return -1
|
|
24
|
+
|
|
25
|
+
selection = _kernel_resolve_webtransport_program(
|
|
26
26
|
plan,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
path=hot.path,
|
|
28
|
+
message=message,
|
|
29
|
+
)
|
|
30
|
+
temp = getattr(ctx, "temp", None)
|
|
31
|
+
if not isinstance(temp, dict):
|
|
32
|
+
return -1
|
|
33
|
+
temp["webtransport_selection"] = selection
|
|
34
|
+
if selection.path_params is not None:
|
|
35
|
+
path_params = dict(selection.path_params)
|
|
36
|
+
hot.path_params = path_params
|
|
37
|
+
hot.route_path_params = path_params
|
|
38
|
+
temp.setdefault("route", {})["path_params"] = path_params
|
|
39
|
+
temp.setdefault("dispatch", {})["path_params"] = path_params
|
|
40
|
+
channel = ctx.get("channel")
|
|
41
|
+
if channel is not None:
|
|
42
|
+
channel.path_params = path_params
|
|
43
|
+
|
|
44
|
+
if selection.program_id < 0:
|
|
45
|
+
return -1
|
|
46
|
+
|
|
47
|
+
protocol = (
|
|
48
|
+
"webtransport.jsonrpc"
|
|
49
|
+
if selection.rpc_envelope is not None
|
|
50
|
+
else "webtransport"
|
|
31
51
|
)
|
|
52
|
+
selector = hot.path
|
|
53
|
+
if selection.rpc_envelope is not None:
|
|
54
|
+
rpc_method = selection.rpc_envelope.get("method")
|
|
55
|
+
selector = f"{hot.path}:{rpc_method}"
|
|
56
|
+
params = selection.rpc_envelope.get("params")
|
|
57
|
+
hot.parsed_json = dict(selection.rpc_envelope)
|
|
58
|
+
hot.parsed_json_loaded = True
|
|
59
|
+
hot.route_rpc_envelope = dict(selection.rpc_envelope)
|
|
60
|
+
hot.dispatch_rpc_envelope = dict(selection.rpc_envelope)
|
|
61
|
+
hot.dispatch_jsonrpc_request_id = selection.rpc_envelope.get("id")
|
|
62
|
+
hot.dispatch_rpc_method = (
|
|
63
|
+
rpc_method if isinstance(rpc_method, str) else None
|
|
64
|
+
)
|
|
65
|
+
hot.route_payload = dict(params) if isinstance(params, Mapping) else {}
|
|
66
|
+
route = temp.setdefault("route", {})
|
|
67
|
+
dispatch = temp.setdefault("dispatch", {})
|
|
68
|
+
route["rpc_envelope"] = dict(selection.rpc_envelope)
|
|
69
|
+
route["payload"] = hot.route_payload
|
|
70
|
+
dispatch["rpc"] = dict(selection.rpc_envelope)
|
|
71
|
+
dispatch["rpc_method"] = rpc_method
|
|
72
|
+
dispatch["parsed_payload"] = hot.route_payload
|
|
73
|
+
|
|
74
|
+
program_id = int(selection.program_id)
|
|
75
|
+
hot.program_id = program_id
|
|
76
|
+
hot.route_program_id = program_id
|
|
77
|
+
hot.route_opmeta_index = program_id
|
|
78
|
+
hot.route_protocol = protocol
|
|
79
|
+
hot.route_selector = selector
|
|
80
|
+
hot.dispatch_binding_protocol = protocol
|
|
81
|
+
hot.dispatch_binding_selector = selector
|
|
82
|
+
hot.dispatch_channel_protocol = "webtransport"
|
|
83
|
+
hot.dispatch_channel_selector = hot.path
|
|
84
|
+
hot.transport_kind_id = (
|
|
85
|
+
_TRANSPORT_KIND_JSONRPC
|
|
86
|
+
if selection.rpc_envelope is not None
|
|
87
|
+
else _TRANSPORT_KIND_CHANNEL
|
|
88
|
+
)
|
|
89
|
+
temp["program_id"] = program_id
|
|
90
|
+
temp.setdefault("route", {}).update(
|
|
91
|
+
{
|
|
92
|
+
"program_id": program_id,
|
|
93
|
+
"opmeta_index": program_id,
|
|
94
|
+
"protocol": protocol,
|
|
95
|
+
"selector": selector,
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
temp.setdefault("dispatch", {}).update(
|
|
99
|
+
{
|
|
100
|
+
"binding_protocol": protocol,
|
|
101
|
+
"binding_selector": selector,
|
|
102
|
+
"channel_protocol": "webtransport",
|
|
103
|
+
"channel_selector": hot.path,
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
ctx.path = hot.path
|
|
107
|
+
return program_id
|
|
32
108
|
|
|
33
109
|
def _prime_exact_channel_program(
|
|
34
110
|
self,
|
|
@@ -140,7 +216,9 @@ class _PackedRouteSelectMixin:
|
|
|
140
216
|
owner = ctx.get(owner_key)
|
|
141
217
|
mounts = getattr(owner, "_jsonrpc_endpoint_mounts", None)
|
|
142
218
|
if isinstance(mounts, Mapping):
|
|
143
|
-
endpoint =
|
|
219
|
+
endpoint = (
|
|
220
|
+
mounts.get(path) or mounts.get(hot.path) or mounts.get(f"{path}/")
|
|
221
|
+
)
|
|
144
222
|
if isinstance(endpoint, str) and endpoint:
|
|
145
223
|
return endpoint
|
|
146
224
|
for endpoint, mapped_path in __JSONRPC_DEFAULT_ENDPOINT_MAPPINGS__.items():
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ._shared import *
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class _PackedWebSocketRouteSelectMixin:
|
|
7
|
+
def _resolve_hot_exact_websocket_routes(
|
|
8
|
+
self,
|
|
9
|
+
plan: KernelPlan,
|
|
10
|
+
packed: PackedKernel,
|
|
11
|
+
) -> Mapping[tuple[str, str], int]:
|
|
12
|
+
return _kernel_resolve_hot_exact_websocket_routes(
|
|
13
|
+
plan,
|
|
14
|
+
packed,
|
|
15
|
+
self._hot_exact_websocket_route_cache,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _resolve_program_id_from_exact_websocket(
|
|
20
|
+
self,
|
|
21
|
+
plan: KernelPlan,
|
|
22
|
+
packed: PackedKernel,
|
|
23
|
+
protocol: str,
|
|
24
|
+
path: str,
|
|
25
|
+
) -> int:
|
|
26
|
+
return _kernel_resolve_program_id_from_exact_websocket(
|
|
27
|
+
plan,
|
|
28
|
+
packed,
|
|
29
|
+
protocol,
|
|
30
|
+
path,
|
|
31
|
+
self._hot_exact_websocket_route_cache,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Lazy runtime bridge to canonical kernel lowering services.
|
|
2
|
+
|
|
3
|
+
Functions retain their kernel identity. Loading one service does not import
|
|
4
|
+
unrelated protocol chains during concrete model initialization.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
__all__ = ["compile_opview_from_specs", "build_websocket_jsonrpc_session_handler"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def __getattr__(name: str) -> Any:
|
|
14
|
+
if name == "compile_opview_from_specs":
|
|
15
|
+
from tigrbl_kernel.opview_compiler import compile_opview_from_specs
|
|
16
|
+
|
|
17
|
+
return compile_opview_from_specs
|
|
18
|
+
if name == "build_websocket_jsonrpc_session_handler":
|
|
19
|
+
from tigrbl_kernel.protocol_chains.websocket import build_websocket_jsonrpc_session_handler
|
|
20
|
+
|
|
21
|
+
return build_websocket_jsonrpc_session_handler
|
|
22
|
+
raise AttributeError(name)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tigrbl-runtime
|
|
3
|
-
Version: 0.4.5.
|
|
3
|
+
Version: 0.4.5.dev8
|
|
4
4
|
Summary: Execution bridge for compiled Tigrbl kernel plans and narrow runtime context/carrier compatibility.
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -226,16 +226,16 @@ Classifier: Topic :: Internet :: WWW/HTTP
|
|
|
226
226
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
227
227
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
228
228
|
Requires-Dist: numba (>=0.61.2)
|
|
229
|
-
Requires-Dist: tigrbl-atoms
|
|
230
|
-
Requires-Dist: tigrbl-base
|
|
231
|
-
Requires-Dist: tigrbl-core
|
|
232
|
-
Requires-Dist: tigrbl-kernel
|
|
229
|
+
Requires-Dist: tigrbl-atoms (>=0.4.5.dev5)
|
|
230
|
+
Requires-Dist: tigrbl-base (>=0.4.5.dev6)
|
|
231
|
+
Requires-Dist: tigrbl-core (>=0.4.5.dev6)
|
|
232
|
+
Requires-Dist: tigrbl-kernel (>=0.4.5.dev5)
|
|
233
233
|
Requires-Dist: tigrbl-typing
|
|
234
234
|
Project-URL: Discord, https://discord.gg/K4YTAPapjR
|
|
235
235
|
Project-URL: Homepage, https://github.com/tigrbl/tigrbl
|
|
236
236
|
Project-URL: Issues, https://github.com/tigrbl/tigrbl/issues
|
|
237
237
|
Project-URL: Organization, https://github.com/tigrbl
|
|
238
|
-
Project-URL: Repository, https://github.com/tigrbl/tigrbl/tree/master/pkgs/
|
|
238
|
+
Project-URL: Repository, https://github.com/tigrbl/tigrbl/tree/master/pkgs/50_runtime/tigrbl_runtime
|
|
239
239
|
Description-Content-Type: text/markdown
|
|
240
240
|
|
|
241
241
|
<div align="center">
|
|
@@ -245,7 +245,7 @@ Description-Content-Type: text/markdown
|
|
|
245
245
|
<a href="https://pypi.org/project/tigrbl-runtime/"><img src="https://img.shields.io/pypi/v/tigrbl-runtime?label=PyPI" alt="PyPI version for tigrbl-runtime"/></a>
|
|
246
246
|
<a href="https://pypi.org/project/tigrbl-runtime/"><img src="https://static.pepy.tech/badge/tigrbl-runtime" alt="Downloads for tigrbl-runtime"/></a>
|
|
247
247
|
<a href="https://discord.gg/K4YTAPapjR"><img src="https://img.shields.io/badge/Discord-Join%20chat-5865F2?logo=discord&logoColor=white" alt="Discord community for tigrbl-runtime"/></a>
|
|
248
|
-
<a href="https://github.com/tigrbl/tigrbl/blob/master/pkgs/
|
|
248
|
+
<a href="https://github.com/tigrbl/tigrbl/blob/master/pkgs/50_runtime/tigrbl_runtime/README.md"><img src="https://hits.sh/github.com/tigrbl/tigrbl/blob/master/pkgs/50_runtime/tigrbl_runtime/README.md.svg?label=hits" alt="Repository hits for tigrbl-runtime README"/></a>
|
|
249
249
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-525252" alt="Apache 2.0 license"/></a>
|
|
250
250
|
<a href="pyproject.toml"><img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-3776ab" alt="Python versions 3.10 | 3.11 | 3.12 | 3.13 | 3.14 for tigrbl-runtime"/></a>
|
|
251
251
|
<a href="https://github.com/tigrbl/tigrbl/blob/master/docs/README.md"><img src="https://img.shields.io/badge/workspace-core-1f6feb" alt="Workspace group for tigrbl-runtime"/></a>
|
|
@@ -269,7 +269,7 @@ Framework maintainers, extension authors, and advanced users composing Tigrbl fr
|
|
|
269
269
|
|
|
270
270
|
## Where does tigrbl-runtime fit?
|
|
271
271
|
|
|
272
|
-
`tigrbl-runtime` lives at `pkgs/
|
|
272
|
+
`tigrbl-runtime` lives at `pkgs/50_runtime/tigrbl_runtime` and serves a focused layer in the split Tigrbl framework.
|
|
273
273
|
|
|
274
274
|
## How does tigrbl-runtime work?
|
|
275
275
|
|
|
@@ -291,7 +291,7 @@ pip install tigrbl-runtime
|
|
|
291
291
|
| Surface | Value |
|
|
292
292
|
|---|---|
|
|
293
293
|
| PyPI package | [`tigrbl-runtime`](https://pypi.org/project/tigrbl-runtime/) |
|
|
294
|
-
| Repository path | [`pkgs/
|
|
294
|
+
| Repository path | [`pkgs/50_runtime/tigrbl_runtime`](https://github.com/tigrbl/tigrbl/tree/master/pkgs/50_runtime/tigrbl_runtime) |
|
|
295
295
|
| Python import root | `tigrbl_runtime` |
|
|
296
296
|
| Console scripts | none declared |
|
|
297
297
|
| Entry points | none declared |
|
|
@@ -387,7 +387,7 @@ Choose `tigrbl-runtime` when the quick-answer table matches your use case. Choos
|
|
|
387
387
|
|
|
388
388
|
- Community: [Discord](https://discord.gg/K4YTAPapjR).
|
|
389
389
|
- Issues: [GitHub Issues](https://github.com/tigrbl/tigrbl/issues).
|
|
390
|
-
- Repository: [pkgs/
|
|
390
|
+
- Repository: [pkgs/50_runtime/tigrbl_runtime](https://github.com/tigrbl/tigrbl/tree/master/pkgs/50_runtime/tigrbl_runtime).
|
|
391
391
|
|
|
392
392
|
## Package-local Boundary
|
|
393
393
|
|
|
@@ -398,10 +398,14 @@ This README is the package-local distribution entry point for `tigrbl-runtime`.
|
|
|
398
398
|
- Package status: governed package in the `tigrbl/tigrbl` workspace.
|
|
399
399
|
- Governance source: [SSOT registry](https://github.com/tigrbl/tigrbl/blob/master/.ssot/registry.json).
|
|
400
400
|
- Release evidence: [publish workflow](https://github.com/tigrbl/tigrbl/actions/workflows/publish.yml) validates package builds, tests, GitHub release assets, and PyPI publication for managed packages.
|
|
401
|
-
- Local certification guard: `pkgs/
|
|
401
|
+
- Local certification guard: `pkgs/97_tests/tigrbl_tests/tests/unit/test_package_badges_and_notices.py` verifies every package README keeps the Discord badge, Apache 2.0 badge, explicit Python-version badge, `LICENSE`, and `NOTICE`.
|
|
402
402
|
- Scope note: this README documents the package boundary. Runtime feature support remains governed by `.ssot/` entities and the conformance docs linked below.
|
|
403
403
|
|
|
404
404
|
## License
|
|
405
405
|
|
|
406
406
|
Licensed under the Apache License, Version 2.0. See `LICENSE`, `NOTICE`, and the official [Apache 2.0 license text](https://www.apache.org/licenses/LICENSE-2.0).
|
|
407
407
|
|
|
408
|
+
## Lowering integration
|
|
409
|
+
|
|
410
|
+
`lowering_services` lazily exposes canonical kernel operation-view compilation and WebSocket JSON-RPC session construction to concrete integration code. It preserves the kernel function objects and keeps kernel imports out of concrete authoring modules.
|
|
411
|
+
|
|
@@ -3,11 +3,11 @@ tigrbl_runtime/callbacks.py,sha256=lVxlghq6GuS5JbCo4JeuCwRkwIoItjQLzShShLKzK1o,3
|
|
|
3
3
|
tigrbl_runtime/channel/__init__.py,sha256=myE6r8SGOX0WOdmoRWugJDw6sUhJWjI6lFibscgFvoU,451
|
|
4
4
|
tigrbl_runtime/channel/_asgi_completion.py,sha256=Ye_wsleVhpUNoZ_uW1TiJdcIoExS4ZW6tTvplW6BYoI,966
|
|
5
5
|
tigrbl_runtime/channel/_asgi_context.py,sha256=5ULCWsi_yhswCuXgI7swcVe-OFpgjqOs9oFT7wRjmaA,3802
|
|
6
|
-
tigrbl_runtime/channel/_asgi_jsonrpc.py,sha256
|
|
6
|
+
tigrbl_runtime/channel/_asgi_jsonrpc.py,sha256=-JBIB_IouR_EhlsEd0EtP39BdZOrFvOyJbLyq3O3450,1696
|
|
7
7
|
tigrbl_runtime/channel/_asgi_receive.py,sha256=Ry1YDQVHMJWxe9DhsnTABHiO9vLb0AuukD9cMiHtk5w,2076
|
|
8
8
|
tigrbl_runtime/channel/_asgi_scope.py,sha256=zMiSh-zmEKI-sHoFbz1Wog2IgdInGXSJLe2K_VyFMHk,2322
|
|
9
|
-
tigrbl_runtime/channel/_asgi_send.py,sha256=
|
|
10
|
-
tigrbl_runtime/channel/_asgi_webtransport.py,sha256
|
|
9
|
+
tigrbl_runtime/channel/_asgi_send.py,sha256=bgw5TVYwu3QShxRx5-dsMJFWkfqfR4Y1dFTgPIQ-PJs,12370
|
|
10
|
+
tigrbl_runtime/channel/_asgi_webtransport.py,sha256=r2TsMv4jlVEmpV1e4_nNqp9kRHCJMEFZbvXFEVjF2ho,13035
|
|
11
11
|
tigrbl_runtime/channel/asgi.py,sha256=_y0arRyNeQkzmsl7mcrTMp_1SXWDatvU23ztp8tgsCE,2307
|
|
12
12
|
tigrbl_runtime/channel/capabilities.py,sha256=x6pURZvGu8dM_uIMmBXhFKM4K3gtpiQocPMfhU9MtCA,1802
|
|
13
13
|
tigrbl_runtime/channel/websocket.py,sha256=IDf_KLmCoSXcKk5m389sZ2IT80orstgPZs_uF8fmT3A,4076
|
|
@@ -25,22 +25,24 @@ tigrbl_runtime/executors/ctx/hot_state.py,sha256=CjC_DXtYE93G7nsTT4AVD6VCrvE1k5q
|
|
|
25
25
|
tigrbl_runtime/executors/kernel_executor.py,sha256=QXMyZspKQX8CF7mjEecmor0NJprig4PO35qhAFsxh7g,2734
|
|
26
26
|
tigrbl_runtime/executors/numba_packed.py,sha256=ICntbbFDO05NOuW6F2sU0pre0Z2ipiJrgp9WOeU_Jd8,2930
|
|
27
27
|
tigrbl_runtime/executors/packed/__init__.py,sha256=0rTrOfFv0hc6uZHyuFt1vXO-T_v8sRVXH1GdqMsGiSM,118
|
|
28
|
-
tigrbl_runtime/executors/packed/_shared.py,sha256=
|
|
29
|
-
tigrbl_runtime/executors/packed/batch.py,sha256=
|
|
28
|
+
tigrbl_runtime/executors/packed/_shared.py,sha256=ggCnzYUNotdVkKwf0sSlt_UW2jrT3nq4JW3-P-4PYz8,6203
|
|
29
|
+
tigrbl_runtime/executors/packed/batch.py,sha256=UjgxpABAnT34yItOHlv-ndxd37DzsMlgeyJOiElWtN4,12514
|
|
30
30
|
tigrbl_runtime/executors/packed/error_edges.py,sha256=iKgqeSAER17H3qhHaVyltpxJ3MU_hdAcE6PSijsrwlc,11928
|
|
31
|
-
tigrbl_runtime/executors/packed/execute.py,sha256=
|
|
31
|
+
tigrbl_runtime/executors/packed/execute.py,sha256=_O4pyMIRXp4D3GuollEO-gLnuCerzJLJjSn7ZwVGB4A,15018
|
|
32
32
|
tigrbl_runtime/executors/packed/executor.py,sha256=oaywnJ-smqcqhQbvSQukLy94-A7RE76Sfl15BXyW8MU,3314
|
|
33
33
|
tigrbl_runtime/executors/packed/hot_access.py,sha256=2Xh0v79uHp_TqaqbkL918y6mEz6K0mpZPLCtTXy3eIw,7784
|
|
34
34
|
tigrbl_runtime/executors/packed/hot_context.py,sha256=xDO70_-WvIv0LemBLXweoJ9scPfSMK9e1YybgFAcEZM,12470
|
|
35
|
-
tigrbl_runtime/executors/packed/input_compile.py,sha256=
|
|
36
|
-
tigrbl_runtime/executors/packed/input_extract.py,sha256
|
|
35
|
+
tigrbl_runtime/executors/packed/input_compile.py,sha256=onSBcZrozBcBvRXbvr198G01qkDaE52TByLBBzZ3bc0,13154
|
|
36
|
+
tigrbl_runtime/executors/packed/input_extract.py,sha256=DXqOMSME062p9K1kx0gLzMBEjYT2OXB6xvYn-CY4siQ,13234
|
|
37
37
|
tigrbl_runtime/executors/packed/request_select.py,sha256=U1w3rPQMn7Uf0WLnHgiVqP49uOS-2Y1tM_KAFrEObwA,8763
|
|
38
|
-
tigrbl_runtime/executors/packed/route_select.py,sha256=
|
|
38
|
+
tigrbl_runtime/executors/packed/route_select.py,sha256=mL-bTNjjRTMmlgJFJ5aSbl16EVqSO7C5YxSnRvgQzTY,15349
|
|
39
|
+
tigrbl_runtime/executors/packed/route_select_websocket.py,sha256=XbVqPt0fX0sTGAdepl5QCdm7x1-zba5mCCGHULqDp3c,797
|
|
39
40
|
tigrbl_runtime/executors/packed/runner_resolve.py,sha256=R82Y9MBAlXDc3kKjGXeVOBtimmCprvKuOe8q8oX9NWA,15489
|
|
40
41
|
tigrbl_runtime/executors/packed/segments.py,sha256=-smyIbjgr3RLK_spWEGxo1E-8gJALBTwD0diaIA1hkg,12881
|
|
41
42
|
tigrbl_runtime/executors/phase.py,sha256=9HxJUbdh1-FLvhb6cGe3sOaz_Ej6xydDT9hUpVi2ERQ,1057
|
|
42
43
|
tigrbl_runtime/executors/phase_runner.py,sha256=OshCk29OFn-5IMBgLO5_23zLHsewgk_tXOMAXgBZNrg,9856
|
|
43
44
|
tigrbl_runtime/executors/types.py,sha256=r4GsnCFXXvbi8XA2vVl8rP9hCsV0W3r_HyTnQq0DS04,1127
|
|
45
|
+
tigrbl_runtime/lowering_services.py,sha256=1pasQDSPyVJCKRr1nGts68m0kMfJw6JG-5KLvuUq_iY,794
|
|
44
46
|
tigrbl_runtime/runtime/__init__.py,sha256=oy7pj5NN8inWiXhkdU5htTxxqYcsbgkeEBRbRIIz_hc,652
|
|
45
47
|
tigrbl_runtime/runtime/_typing_aliases.py,sha256=sOkj2em2t7nzf1z6VD7U5AHGxhDqmfdcSOzLl-mZXcc,108
|
|
46
48
|
tigrbl_runtime/runtime/base.py,sha256=ejEE0JIoYC41OcLm4ikUR23MfITVs9ruSv2EftX1-R4,882
|
|
@@ -51,8 +53,8 @@ tigrbl_runtime/runtime/response.py,sha256=SCJKN-hyjeVoKmlLdf_N7RkTyx_f6noCx-fSgL
|
|
|
51
53
|
tigrbl_runtime/runtime/runtime.py,sha256=gCIuwJkj_DmPzVfXAIpp5-3hEilv_yfyYvLDjrcBF7E,3624
|
|
52
54
|
tigrbl_runtime/runtime/system.py,sha256=5GWVYZscff1nUymCKJAEa7VYbqIeFfppV3seYyDxlvA,4301
|
|
53
55
|
tigrbl_runtime/semantics.py,sha256=UwPeNdKXJ421XoXqL3oyko5gi9vNrXkBKwk5X9Ug2So,9972
|
|
54
|
-
tigrbl_runtime-0.4.5.
|
|
55
|
-
tigrbl_runtime-0.4.5.
|
|
56
|
-
tigrbl_runtime-0.4.5.
|
|
57
|
-
tigrbl_runtime-0.4.5.
|
|
58
|
-
tigrbl_runtime-0.4.5.
|
|
56
|
+
tigrbl_runtime-0.4.5.dev8.dist-info/METADATA,sha256=0KZXUXZAqSxhnNq2HqZ4srK2EzuyBqSE0WUTyBo--3g,23717
|
|
57
|
+
tigrbl_runtime-0.4.5.dev8.dist-info/WHEEL,sha256=L-WvLdSBvJWHXv5zrQxDvNRnIgoBzLUpcbuiw19KCuQ,88
|
|
58
|
+
tigrbl_runtime-0.4.5.dev8.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
59
|
+
tigrbl_runtime-0.4.5.dev8.dist-info/licenses/NOTICE,sha256=EvJMTshzsWz43LiK-DeN2ZuLtrP49cxvlrFlJ8F_buc,221
|
|
60
|
+
tigrbl_runtime-0.4.5.dev8.dist-info/RECORD,,
|
{tigrbl_runtime-0.4.5.dev4.dist-info → tigrbl_runtime-0.4.5.dev8.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|