cleat-sdk 0.3.0__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.
Files changed (50) hide show
  1. cleat_sdk/__init__.py +105 -0
  2. cleat_sdk/_wit/__init__.py +1 -0
  3. cleat_sdk/_wit/componentize_py_async_support/__init__.py +570 -0
  4. cleat_sdk/_wit/componentize_py_async_support/futures.py +149 -0
  5. cleat_sdk/_wit/componentize_py_async_support/streams.py +345 -0
  6. cleat_sdk/_wit/componentize_py_runtime.pyi +34 -0
  7. cleat_sdk/_wit/componentize_py_types.py +32 -0
  8. cleat_sdk/_wit/poll_loop.py +440 -0
  9. cleat_sdk/_wit/wit_world/__init__.py +84 -0
  10. cleat_sdk/_wit/wit_world/imports/__init__.py +0 -0
  11. cleat_sdk/_wit/wit_world/imports/durable_call.py +51 -0
  12. cleat_sdk/_wit/wit_world/imports/durable_children.py +60 -0
  13. cleat_sdk/_wit/wit_world/imports/durable_cron.py +45 -0
  14. cleat_sdk/_wit/wit_world/imports/durable_extended_lifecycle.py +42 -0
  15. cleat_sdk/_wit/wit_world/imports/durable_fetch.py +24 -0
  16. cleat_sdk/_wit/wit_world/imports/durable_handlers.py +45 -0
  17. cleat_sdk/_wit/wit_world/imports/durable_identity.py +26 -0
  18. cleat_sdk/_wit/wit_world/imports/durable_lifecycle.py +48 -0
  19. cleat_sdk/_wit/wit_world/imports/durable_lock.py +26 -0
  20. cleat_sdk/_wit/wit_world/imports/durable_messaging.py +26 -0
  21. cleat_sdk/_wit/wit_world/imports/durable_promises.py +38 -0
  22. cleat_sdk/_wit/wit_world/imports/durable_scope.py +34 -0
  23. cleat_sdk/_wit/wit_world/imports/durable_signals.py +39 -0
  24. cleat_sdk/_wit/wit_world/imports/durable_sleep.py +36 -0
  25. cleat_sdk/_wit/wit_world/imports/durable_state.py +21 -0
  26. cleat_sdk/_wit/wit_world/imports/durable_stream_state.py +34 -0
  27. cleat_sdk/_wit/wit_world/imports/durable_version.py +26 -0
  28. cleat_sdk/_wit/wit_world/imports/outcomes.py +66 -0
  29. cleat_sdk/_wit/wit_world/imports/plugin.py +32 -0
  30. cleat_sdk/client.py +292 -0
  31. cleat_sdk/defer.py +138 -0
  32. cleat_sdk/entry.py +621 -0
  33. cleat_sdk/host_calls.py +3751 -0
  34. cleat_sdk/langchain/__init__.py +9 -0
  35. cleat_sdk/langchain/callbacks.py +461 -0
  36. cleat_sdk/langgraph/__init__.py +9 -0
  37. cleat_sdk/langgraph/checkpoint.py +439 -0
  38. cleat_sdk/local_host.py +1583 -0
  39. cleat_sdk/memory.py +346 -0
  40. cleat_sdk/plugins.py +1066 -0
  41. cleat_sdk/py.typed +0 -0
  42. cleat_sdk/signal_envelope.py +80 -0
  43. cleat_sdk/test_harness.py +711 -0
  44. cleat_sdk/types.py +514 -0
  45. cleat_sdk/version.py +33 -0
  46. cleat_sdk/vet.py +1147 -0
  47. cleat_sdk-0.3.0.dist-info/METADATA +950 -0
  48. cleat_sdk-0.3.0.dist-info/RECORD +50 -0
  49. cleat_sdk-0.3.0.dist-info/WHEEL +5 -0
  50. cleat_sdk-0.3.0.dist-info/top_level.txt +1 -0
cleat_sdk/__init__.py ADDED
@@ -0,0 +1,105 @@
1
+ """
2
+ cleat-sdk: Python SDK for the Cleat cleat execution framework.
3
+
4
+ Provides the HostCalls class for making cleat API calls from WASM workflows,
5
+ the @cleat_entry decorator for marking workflow entry points, and memory
6
+ helpers for the Cleat ABI.
7
+
8
+ Quick start:
9
+ from cleat_sdk import HostCalls, cleat_entry
10
+
11
+ @cleat_entry
12
+ def my_workflow(h: HostCalls, name: str) -> str:
13
+ h.log(f"Hello, {name}!")
14
+ resp = h.call("my-service", "DoThing", {"name": name})
15
+ return resp
16
+ """
17
+
18
+ from .client import CleatClient
19
+ from .entry import cleat_entry, virtual_object
20
+ from .host_calls import (
21
+ INFINITE_TIMEOUT_MS,
22
+ ChildResult,
23
+ ChildWorkflowOptions,
24
+ CleatCallError,
25
+ CleatCallPermanentError,
26
+ CleatCallTimeoutError,
27
+ CleatCallTransientError,
28
+ HostCalls,
29
+ PromiseResult,
30
+ RetryPolicy,
31
+ SignalResult,
32
+ SuspendSentinel,
33
+ )
34
+ from .langchain.callbacks import CleatCallbackHandler
35
+ from .langgraph.checkpoint import CleatCheckpointer
36
+ from .local_host import LocalHostCalls
37
+ from .plugins import (
38
+ AwaitEventResult,
39
+ AwaitWebhookResult,
40
+ BlobGetResult,
41
+ BlobPutResult,
42
+ EvaluateFlagResult,
43
+ LLMChatResult,
44
+ LLMEmbedResult,
45
+ LLMListModelsResult,
46
+ PgVectorDeleteResult,
47
+ PgVectorSearchResult,
48
+ PgVectorUpsertResult,
49
+ Plugins,
50
+ ProduceResult,
51
+ ResolveIncidentResult,
52
+ SendMessageResult,
53
+ SendWebhookResult,
54
+ StreamEvent,
55
+ TriggerIncidentResult,
56
+ )
57
+ from .test_harness import CallRecord, CleatTestHarness
58
+ from .types import ChildWorkflow, CleatDefer, Saga, SagaStep, SagaStepResult, TerminalError
59
+
60
+ __all__ = [
61
+ "INFINITE_TIMEOUT_MS",
62
+ "AwaitEventResult",
63
+ "AwaitWebhookResult",
64
+ "BlobGetResult",
65
+ "BlobPutResult",
66
+ "CallRecord",
67
+ "ChildResult",
68
+ "ChildWorkflow",
69
+ "ChildWorkflowOptions",
70
+ "CleatCallError",
71
+ "CleatCallPermanentError",
72
+ "CleatCallTimeoutError",
73
+ "CleatCallTransientError",
74
+ "CleatCallbackHandler",
75
+ "CleatCheckpointer",
76
+ "CleatClient",
77
+ "CleatDefer",
78
+ "CleatTestHarness",
79
+ "EvaluateFlagResult",
80
+ "HostCalls",
81
+ "LLMChatResult",
82
+ "LLMEmbedResult",
83
+ "LLMListModelsResult",
84
+ "LocalHostCalls",
85
+ "PgVectorDeleteResult",
86
+ "PgVectorSearchResult",
87
+ "PgVectorUpsertResult",
88
+ "Plugins",
89
+ "ProduceResult",
90
+ "PromiseResult",
91
+ "ResolveIncidentResult",
92
+ "RetryPolicy",
93
+ "Saga",
94
+ "SagaStep",
95
+ "SagaStepResult",
96
+ "SendMessageResult",
97
+ "SendWebhookResult",
98
+ "SignalResult",
99
+ "StreamEvent",
100
+ "SuspendSentinel",
101
+ "TerminalError",
102
+ "TriggerIncidentResult",
103
+ "cleat_entry",
104
+ "virtual_object",
105
+ ]
@@ -0,0 +1 @@
1
+ # Generated WIT bindings for cleat host calls
@@ -0,0 +1,570 @@
1
+ """This module contains code to integrate Component Model async features with
2
+ Python's `asyncio` framework.
3
+
4
+ Most of the code here is used internally by code generated by `componentize-py`
5
+ for worlds which use async features. As of this writing, the only item meant to
6
+ be used directly by application code is the `spawn` function.
7
+
8
+ """
9
+ import asyncio
10
+ import componentize_py_runtime
11
+ import subprocess
12
+
13
+ from os import PathLike
14
+ from socket import AddressFamily, AddressInfo, SocketKind, socket
15
+ from concurrent.futures import Executor
16
+ from componentize_py_types import Result, Ok, Err
17
+ from dataclasses import dataclass
18
+ from typing import Any, Optional, TypeVar, TypeVarTuple, Callable, IO, Literal, cast
19
+ from contextvars import ContextVar, Context
20
+ from collections.abc import Coroutine, Awaitable, Sequence
21
+ from asyncio.protocols import BaseProtocol
22
+ from asyncio.transports import Transport, WriteTransport, DatagramTransport, SubprocessTransport, ReadTransport
23
+ from asyncio.base_events import Server
24
+
25
+ try:
26
+ from ssl import SSLContext
27
+ except:
28
+ pass
29
+
30
+ @dataclass
31
+ class _FutureState:
32
+ waitable_set: int | None
33
+ futures: dict[int, Any]
34
+ handles: list[asyncio.Handle]
35
+ pending_count: int
36
+
37
+ class _ReturnCode:
38
+ COMPLETED = 0
39
+ DROPPED = 1
40
+ CANCELLED = 2
41
+
42
+ class _CallbackCode:
43
+ EXIT = 0
44
+ YIELD = 1
45
+ WAIT = 2
46
+ POLL = 3
47
+
48
+ class _Event:
49
+ NONE = 0
50
+ SUBTASK = 1
51
+ STREAM_READ = 2
52
+ STREAM_WRITE = 3
53
+ FUTURE_READ = 4
54
+ FUTURE_WRITE = 5
55
+ CANCELLED = 6
56
+
57
+ class _Status:
58
+ STARTING = 0
59
+ STARTED = 1
60
+ RETURNED = 2
61
+ START_CANCELLED = 3
62
+ RETURN_CANCELLED = 4
63
+
64
+ _T = TypeVar("_T")
65
+ _Ts = TypeVarTuple("_Ts")
66
+ _ProtocolT = TypeVar("_ProtocolT", bound=BaseProtocol)
67
+
68
+ async def _noop() -> None:
69
+ pass
70
+
71
+ class _Loop(asyncio.AbstractEventLoop):
72
+ def __init__(self) -> None:
73
+ self.running: bool = False
74
+ self.exception: Optional[Any] = None
75
+
76
+ def poll(self, future_state: _FutureState) -> None:
77
+ while True:
78
+ handles = future_state.handles
79
+ future_state.handles = []
80
+ for handle in handles:
81
+ if not handle._cancelled:
82
+ handle._run()
83
+
84
+ if self.exception is not None:
85
+ raise self.exception
86
+
87
+ if len(handles) == 0 and len(future_state.handles) == 0:
88
+ return
89
+
90
+ def get_debug(self) -> bool:
91
+ return False
92
+
93
+ def is_running(self) -> bool:
94
+ return self.running
95
+
96
+ def is_closed(self) -> bool:
97
+ return not self.running
98
+
99
+ def stop(self) -> None:
100
+ self.running = False
101
+
102
+ def close(self) -> None:
103
+ self.running = False
104
+
105
+ def shutdown_asyncgens(self) -> Coroutine[Any, Any, None]:
106
+ return _noop()
107
+
108
+ def call_exception_handler(self, context: dict[str, Any]) -> None:
109
+ self.exception = context.get('exception', None)
110
+
111
+ def call_soon(self,
112
+ callback: Callable[[*_Ts], object],
113
+ *args: *_Ts,
114
+ context: Context | None = None) -> asyncio.Handle:
115
+ global _future_state
116
+
117
+ if context is not None:
118
+ future_state = context[_future_state]
119
+ handle = asyncio.Handle(callback, args, self, context)
120
+ future_state.handles.append(handle)
121
+ return handle
122
+ else:
123
+ raise AssertionError
124
+
125
+ def create_task(self, coroutine: Coroutine[Any, Any, _T], name: str | None = None, context: Context | None = None) -> asyncio.Task[_T]:
126
+ return asyncio.Task(coroutine, loop=self, context=context)
127
+
128
+ def create_future(self) -> asyncio.Future[Any]:
129
+ return asyncio.Future(loop=self)
130
+
131
+ # The remaining methods should be irrelevant for our purposes and thus unimplemented
132
+
133
+ def run_until_complete(self, future: Awaitable[_T]) -> _T:
134
+ raise NotImplementedError
135
+
136
+ def run_forever(self) -> None:
137
+ raise NotImplementedError
138
+
139
+ async def shutdown_default_executor(self) -> None:
140
+ raise NotImplementedError
141
+
142
+ def call_later(self,
143
+ delay: float,
144
+ callback: Callable[[*_Ts], object],
145
+ *args: *_Ts,
146
+ context: Context | None = None) -> asyncio.TimerHandle:
147
+ raise NotImplementedError
148
+
149
+ def call_at(self,
150
+ when: float,
151
+ callback: Callable[[*_Ts], object],
152
+ *args: *_Ts,
153
+ context: Context | None = None) -> asyncio.TimerHandle:
154
+ raise NotImplementedError
155
+
156
+ def time(self) -> float:
157
+ raise NotImplementedError
158
+
159
+ def call_soon_threadsafe(self,
160
+ callback: Callable[[*_Ts], object],
161
+ *args: *_Ts,
162
+ context: Context | None = None) -> asyncio.Handle:
163
+ raise NotImplementedError
164
+
165
+ def run_in_executor(self,
166
+ executor: Executor | None,
167
+ func: Callable[[*_Ts], _T],
168
+ *args: *_Ts) -> asyncio.Future[_T]:
169
+ raise NotImplementedError
170
+
171
+ def set_default_executor(self, executor: Executor) -> None:
172
+ raise NotImplementedError
173
+
174
+ async def getaddrinfo(
175
+ self,
176
+ host: bytes | str | None,
177
+ port: bytes | str | int | None,
178
+ *,
179
+ family: int = 0,
180
+ type: int = 0,
181
+ proto: int = 0,
182
+ flags: int = 0,
183
+ ) -> list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int] | tuple[str, int, int, int]]]:
184
+ raise NotImplementedError
185
+
186
+ async def getnameinfo(self,
187
+ sockaddr: tuple[str, int] | tuple[str, int, int, int],
188
+ flags: int = 0) -> tuple[str, str]:
189
+ raise NotImplementedError
190
+
191
+ async def create_connection(
192
+ self,
193
+ protocol_factory: Callable[[], _ProtocolT],
194
+ host: str | None = ...,
195
+ port: int | None = ...,
196
+ *,
197
+ ssl: bool | SSLContext | None = None,
198
+ family: int = 0,
199
+ proto: int = 0,
200
+ flags: int = 0,
201
+ sock: socket | None = None,
202
+ local_addr: tuple[str, int] | None = None,
203
+ server_hostname: str | None = None,
204
+ ssl_handshake_timeout: float | None = None,
205
+ ssl_shutdown_timeout: float | None = None,
206
+ happy_eyeballs_delay: float | None = None,
207
+ interleave: int | None = None,
208
+ ) -> tuple[Transport, _ProtocolT]:
209
+ raise NotImplementedError
210
+
211
+ async def create_server(
212
+ self,
213
+ protocol_factory: Callable[[], BaseProtocol],
214
+ host: str | Sequence[str] | None = None,
215
+ port: int | None = None,
216
+ *,
217
+ family: int = AddressFamily.AF_UNSPEC,
218
+ flags: int = AddressInfo.AI_PASSIVE,
219
+ sock: socket | None = ...,
220
+ backlog: int = 100,
221
+ ssl: bool | SSLContext | None = None,
222
+ reuse_address: bool | None = None,
223
+ reuse_port: bool | None = None,
224
+ keep_alive: bool | None = None,
225
+ ssl_handshake_timeout: float | None = None,
226
+ ssl_shutdown_timeout: float | None = None,
227
+ start_serving: bool = True,
228
+ ) -> Server:
229
+ raise NotImplementedError
230
+
231
+ async def sendfile(self,
232
+ transport: WriteTransport,
233
+ file: IO[bytes],
234
+ offset: int = 0,
235
+ count: int | None = None,
236
+ *,
237
+ fallback: bool = True
238
+ ) -> int:
239
+ raise NotImplementedError
240
+
241
+ async def start_tls(
242
+ self,
243
+ transport: WriteTransport,
244
+ protocol: BaseProtocol,
245
+ sslcontext: SSLContext,
246
+ *,
247
+ server_side: bool = False,
248
+ server_hostname: str | None = None,
249
+ ssl_handshake_timeout: float | None = None,
250
+ ssl_shutdown_timeout: float | None = None,
251
+ ) -> Transport | None:
252
+ raise NotImplementedError
253
+
254
+ async def create_unix_connection(
255
+ self,
256
+ protocol_factory: Callable[[], _ProtocolT],
257
+ path: str | None = None,
258
+ *,
259
+ ssl: bool | SSLContext | None = None,
260
+ sock: socket | None = None,
261
+ server_hostname: str | None = None,
262
+ ssl_handshake_timeout: float | None = None,
263
+ ssl_shutdown_timeout: float | None = None,
264
+ ) -> tuple[Transport, _ProtocolT]:
265
+ raise NotImplementedError
266
+
267
+ async def create_unix_server(
268
+ self,
269
+ protocol_factory: Callable[[], BaseProtocol],
270
+ path: str | PathLike[str] | None = None,
271
+ *,
272
+ sock: socket | None = None,
273
+ backlog: int = 100,
274
+ ssl: bool | SSLContext | None = None,
275
+ ssl_handshake_timeout: float | None = None,
276
+ ssl_shutdown_timeout: float | None = None,
277
+ start_serving: bool = True,
278
+ ) -> Server:
279
+ raise NotImplementedError
280
+
281
+ async def connect_accepted_socket(
282
+ self,
283
+ protocol_factory: Callable[[], _ProtocolT],
284
+ sock: socket,
285
+ *,
286
+ ssl: bool | SSLContext | None = None,
287
+ ssl_handshake_timeout: float | None = None,
288
+ ssl_shutdown_timeout: float | None = None,
289
+ ) -> tuple[Transport, _ProtocolT]:
290
+ raise NotImplementedError
291
+
292
+ async def create_datagram_endpoint(
293
+ self,
294
+ protocol_factory: Callable[[], _ProtocolT],
295
+ local_addr: tuple[str, int] | str | None = None,
296
+ remote_addr: tuple[str, int] | str | None = None,
297
+ *,
298
+ family: int = 0,
299
+ proto: int = 0,
300
+ flags: int = 0,
301
+ reuse_address: bool | None = None,
302
+ reuse_port: bool | None = None,
303
+ allow_broadcast: bool | None = None,
304
+ sock: socket | None = None,
305
+ ) -> tuple[DatagramTransport, _ProtocolT]:
306
+ raise NotImplementedError
307
+
308
+ async def connect_read_pipe(self,
309
+ protocol_factory: Callable[[], _ProtocolT],
310
+ pipe: Any) -> tuple[ReadTransport, _ProtocolT]:
311
+ raise NotImplementedError
312
+
313
+ async def connect_write_pipe(self,
314
+ protocol_factory: Callable[[], _ProtocolT],
315
+ pipe: Any) -> tuple[WriteTransport, _ProtocolT]:
316
+ raise NotImplementedError
317
+
318
+ async def subprocess_shell(
319
+ self,
320
+ protocol_factory: Callable[[], _ProtocolT],
321
+ cmd: bytes | str,
322
+ *,
323
+ stdin: int | IO[Any] | None = -1,
324
+ stdout: int | IO[Any] | None = -1,
325
+ stderr: int | IO[Any] | None = -1,
326
+ universal_newlines: Literal[False] = False,
327
+ shell: Literal[True] = True,
328
+ bufsize: Literal[0] = 0,
329
+ encoding: None = None,
330
+ errors: None = None,
331
+ text: Literal[False] | None = None,
332
+ **kwargs: Any,
333
+ ) -> tuple[SubprocessTransport, _ProtocolT]:
334
+ raise NotImplementedError
335
+
336
+ async def subprocess_exec(
337
+ self,
338
+ protocol_factory: Callable[[], _ProtocolT],
339
+ program: Any,
340
+ *args: Any,
341
+ stdin: int | IO[Any] | None = -1,
342
+ stdout: int | IO[Any] | None = -1,
343
+ stderr: int | IO[Any] | None = -1,
344
+ universal_newlines: Literal[False] = False,
345
+ shell: Literal[False] = False,
346
+ bufsize: Literal[0] = 0,
347
+ encoding: None = None,
348
+ errors: None = None,
349
+ **kwargs: Any,
350
+ ) -> tuple[SubprocessTransport, _ProtocolT]:
351
+ raise NotImplementedError
352
+
353
+ def add_reader(self, fd: Any, callback: Callable[[*_Ts], Any], *args: *_Ts) -> None:
354
+ raise NotImplementedError
355
+
356
+ def remove_reader(self, fd: Any) -> bool:
357
+ raise NotImplementedError
358
+
359
+ def add_writer(self, fd: Any, callback: Callable[[*_Ts], Any], *args: *_Ts) -> None:
360
+ raise NotImplementedError
361
+
362
+ def remove_writer(self, fd: Any) -> bool:
363
+ raise NotImplementedError
364
+
365
+ async def sock_recv(self, sock: socket, nbytes: int) -> bytes:
366
+ raise NotImplementedError
367
+
368
+ async def sock_recv_into(self, sock: socket, buf: Any) -> int:
369
+ raise NotImplementedError
370
+
371
+ async def sock_recvfrom(self, sock: socket, bufsize: int) -> tuple[bytes, Any]:
372
+ raise NotImplementedError
373
+
374
+ async def sock_recvfrom_into(self, sock: socket, buf: Any, nbytes: int = 0) -> tuple[int, Any]:
375
+ raise NotImplementedError
376
+
377
+ async def sock_sendall(self, sock: socket, data: Any) -> None:
378
+ raise NotImplementedError
379
+
380
+ async def sock_sendto(self, sock: socket, data: Any, address: Any) -> int:
381
+ raise NotImplementedError
382
+
383
+ async def sock_connect(self, sock: socket, address: Any) -> None:
384
+ raise NotImplementedError
385
+
386
+ async def sock_accept(self, sock: socket) -> tuple[socket, Any]:
387
+ raise NotImplementedError
388
+
389
+ async def sock_sendfile(
390
+ self,
391
+ sock: socket,
392
+ file: IO[bytes],
393
+ offset: int = 0,
394
+ count: int | None = None,
395
+ *,
396
+ fallback: bool | None = None
397
+ ) -> int:
398
+ raise NotImplementedError
399
+
400
+ def add_signal_handler(self, sig: int, callback: Callable[[*_Ts], object], *args: *_Ts) -> None:
401
+ raise NotImplementedError
402
+
403
+ def remove_signal_handler(self, sig: int) -> bool:
404
+ raise NotImplementedError
405
+
406
+ def set_task_factory(self, factory: Any | None) -> None:
407
+ raise NotImplementedError
408
+
409
+ def get_task_factory(self) -> None:
410
+ raise NotImplementedError
411
+
412
+ def get_exception_handler(self) -> None:
413
+ raise NotImplementedError
414
+
415
+ def set_exception_handler(self, handler: Any | None) -> None:
416
+ raise NotImplementedError
417
+
418
+ def default_exception_handler(self, context: dict[str, Any]) -> None:
419
+ raise NotImplementedError
420
+
421
+ def set_debug(self, enabled: bool) -> None:
422
+ raise NotImplementedError
423
+
424
+ _future_state: ContextVar[_FutureState] = ContextVar("_future_state")
425
+ _loop = _Loop()
426
+ asyncio.set_event_loop(_loop)
427
+ _loop.running = True
428
+ asyncio.events._set_running_loop(_loop)
429
+
430
+ def _set_future_state(future_state: _FutureState) -> None:
431
+ global _future_state
432
+
433
+ _future_state.set(future_state)
434
+
435
+ async def _return_result(export_index: int, borrows: int, coroutine: Any) -> None:
436
+ global _future_state
437
+
438
+ try:
439
+ try:
440
+ result: Result[Any, Any] = Ok(await coroutine)
441
+ except Err as e:
442
+ result = e
443
+
444
+ componentize_py_runtime.call_task_return(export_index, borrows, result)
445
+ except Exception as e:
446
+ _loop.exception = e
447
+
448
+ assert _future_state.get().pending_count > 0
449
+ _future_state.get().pending_count -= 1
450
+
451
+ def first_poll(export_index: int, borrows: int, coroutine: Any) -> int:
452
+ """Internal function called by generated code for exported functions.
453
+
454
+ This is not meant to be called by application code.
455
+
456
+ """
457
+ context = Context()
458
+ future_state = _FutureState(None, {}, [], 1)
459
+ context.run(_set_future_state, future_state)
460
+ asyncio.create_task(_return_result(export_index, borrows, coroutine), context=context)
461
+ return _poll(future_state)
462
+
463
+ def _poll(future_state: _FutureState) -> int:
464
+ global _loop
465
+
466
+ _loop.poll(future_state)
467
+
468
+ if future_state.pending_count == 0:
469
+ if future_state.waitable_set is not None:
470
+ componentize_py_runtime.waitable_set_drop(future_state.waitable_set)
471
+
472
+ return _CallbackCode.EXIT
473
+ else:
474
+ waitable_set = future_state.waitable_set
475
+ assert waitable_set is not None
476
+ componentize_py_runtime.context_set(future_state)
477
+ return _CallbackCode.WAIT | (waitable_set << 4)
478
+
479
+ def callback(event0: int, event1: int, event2: int) -> int:
480
+ """Internal function called by generated code for exported functions.
481
+
482
+ This is not meant to be called by application code.
483
+
484
+ """
485
+ future_state = componentize_py_runtime.context_get()
486
+ componentize_py_runtime.context_set(None)
487
+
488
+ match event0:
489
+ case _Event.NONE:
490
+ pass
491
+ case _Event.SUBTASK:
492
+ match event2:
493
+ case _Status.STARTING:
494
+ raise AssertionError
495
+ case _Status.STARTED:
496
+ pass
497
+ case _Status.RETURNED:
498
+ componentize_py_runtime.waitable_join(event1, 0)
499
+ componentize_py_runtime.subtask_drop(event1)
500
+ future_state.futures.pop(event1).set_result(event2)
501
+ case _:
502
+ # todo
503
+ raise NotImplementedError
504
+ case _Event.STREAM_READ | _Event.STREAM_WRITE | _Event.FUTURE_READ | _Event.FUTURE_WRITE:
505
+ componentize_py_runtime.waitable_join(event1, 0)
506
+ future_state.futures.pop(event1).set_result(event2)
507
+ case _:
508
+ # todo
509
+ raise NotImplementedError
510
+
511
+ return _poll(future_state)
512
+
513
+ async def await_result[T](result: Result[T, tuple[int, int]]) -> T:
514
+ """Internal function called by generated code for imported functions.
515
+
516
+ This is not meant to be called directly by application code.
517
+
518
+ """
519
+ global _loop
520
+ global _future_state
521
+
522
+ if isinstance(result, Ok):
523
+ return result.value
524
+ else:
525
+ future_state = _future_state.get()
526
+ waitable, promise = result.value
527
+ future = _loop.create_future()
528
+ future_state.futures[waitable] = future
529
+
530
+ if future_state.waitable_set is None:
531
+ future_state.waitable_set = componentize_py_runtime.waitable_set_new()
532
+ componentize_py_runtime.waitable_join(waitable, future_state.waitable_set)
533
+
534
+ return cast(T, componentize_py_runtime.promise_get_result(await future, promise))
535
+
536
+ async def _wrap_spawned(coroutine: Any) -> None:
537
+ global _future_state
538
+
539
+ try:
540
+ await coroutine
541
+ except Exception as e:
542
+ _loop.exception = e
543
+
544
+ assert _future_state.get().pending_count > 0
545
+ _future_state.get().pending_count -= 1
546
+
547
+ def spawn(coroutine: Any) -> None:
548
+ """Spawn an awaitable to be executed as part of the current Component Model
549
+ task.
550
+
551
+ Each call to an async function exported by a component creates a new
552
+ Component Model task which is represented as a Python awaitable. By
553
+ default, that task will exit after returning a value. However, this
554
+ function may be used to do additional work concurrently, and that additional
555
+ work may extend the lifetime of the task arbitrarily after it has returned a
556
+ value.
557
+
558
+ For example, implementing `wasi:http/service@0.3.x` involves returning a
559
+ `response` resource, but sending the response body must happen after
560
+ returning since it involves writing to a stream which the caller will only
561
+ have received as part of the `response` resource. Therefore, the only way
562
+ to write the response body is by using this function. See the `http-p3`
563
+ example in the `componentize-py` Git repository for what that looks like.
564
+
565
+ """
566
+ global _future_state
567
+
568
+ _future_state.get().pending_count += 1
569
+
570
+ asyncio.create_task(_wrap_spawned(coroutine))