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.
- cleat_sdk/__init__.py +105 -0
- cleat_sdk/_wit/__init__.py +1 -0
- cleat_sdk/_wit/componentize_py_async_support/__init__.py +570 -0
- cleat_sdk/_wit/componentize_py_async_support/futures.py +149 -0
- cleat_sdk/_wit/componentize_py_async_support/streams.py +345 -0
- cleat_sdk/_wit/componentize_py_runtime.pyi +34 -0
- cleat_sdk/_wit/componentize_py_types.py +32 -0
- cleat_sdk/_wit/poll_loop.py +440 -0
- cleat_sdk/_wit/wit_world/__init__.py +84 -0
- cleat_sdk/_wit/wit_world/imports/__init__.py +0 -0
- cleat_sdk/_wit/wit_world/imports/durable_call.py +51 -0
- cleat_sdk/_wit/wit_world/imports/durable_children.py +60 -0
- cleat_sdk/_wit/wit_world/imports/durable_cron.py +45 -0
- cleat_sdk/_wit/wit_world/imports/durable_extended_lifecycle.py +42 -0
- cleat_sdk/_wit/wit_world/imports/durable_fetch.py +24 -0
- cleat_sdk/_wit/wit_world/imports/durable_handlers.py +45 -0
- cleat_sdk/_wit/wit_world/imports/durable_identity.py +26 -0
- cleat_sdk/_wit/wit_world/imports/durable_lifecycle.py +48 -0
- cleat_sdk/_wit/wit_world/imports/durable_lock.py +26 -0
- cleat_sdk/_wit/wit_world/imports/durable_messaging.py +26 -0
- cleat_sdk/_wit/wit_world/imports/durable_promises.py +38 -0
- cleat_sdk/_wit/wit_world/imports/durable_scope.py +34 -0
- cleat_sdk/_wit/wit_world/imports/durable_signals.py +39 -0
- cleat_sdk/_wit/wit_world/imports/durable_sleep.py +36 -0
- cleat_sdk/_wit/wit_world/imports/durable_state.py +21 -0
- cleat_sdk/_wit/wit_world/imports/durable_stream_state.py +34 -0
- cleat_sdk/_wit/wit_world/imports/durable_version.py +26 -0
- cleat_sdk/_wit/wit_world/imports/outcomes.py +66 -0
- cleat_sdk/_wit/wit_world/imports/plugin.py +32 -0
- cleat_sdk/client.py +292 -0
- cleat_sdk/defer.py +138 -0
- cleat_sdk/entry.py +621 -0
- cleat_sdk/host_calls.py +3751 -0
- cleat_sdk/langchain/__init__.py +9 -0
- cleat_sdk/langchain/callbacks.py +461 -0
- cleat_sdk/langgraph/__init__.py +9 -0
- cleat_sdk/langgraph/checkpoint.py +439 -0
- cleat_sdk/local_host.py +1583 -0
- cleat_sdk/memory.py +346 -0
- cleat_sdk/plugins.py +1066 -0
- cleat_sdk/py.typed +0 -0
- cleat_sdk/signal_envelope.py +80 -0
- cleat_sdk/test_harness.py +711 -0
- cleat_sdk/types.py +514 -0
- cleat_sdk/version.py +33 -0
- cleat_sdk/vet.py +1147 -0
- cleat_sdk-0.3.0.dist-info/METADATA +950 -0
- cleat_sdk-0.3.0.dist-info/RECORD +50 -0
- cleat_sdk-0.3.0.dist-info/WHEEL +5 -0
- 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))
|