iii-sdk 0.11.3.dev2__tar.gz → 0.11.4.dev2__tar.gz
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.
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/PKG-INFO +1 -1
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/pyproject.toml +1 -1
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/iii.py +0 -214
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/types.py +0 -15
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_api_triggers.py +36 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_async_api.py +0 -18
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_bridge.py +10 -4
- iii_sdk-0.11.4.dev2/tests/test_queue_integration.py +358 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_rbac_workers.py +18 -6
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_state.py +71 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_streams.py +114 -6
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_sync_api.py +2 -3
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/uv.lock +1 -1
- iii_sdk-0.11.3.dev2/tests/test_queue_integration.py +0 -136
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/.gitignore +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/README.md +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/__init__.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/channels.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/errors.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/format_utils.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/iii_constants.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/iii_types.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/logger.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/otel_worker_gauges.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/state.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/stream.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/telemetry.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/telemetry_exporters.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/telemetry_types.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/triggers.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/utils.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/src/iii/worker_metrics.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/conftest.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_channel_close_delay.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_context_propagation.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_data_channels.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_errors.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_format_utils.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_healthcheck.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_hold_process.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_http_external_functions_integration.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_iii_registration_dedup.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_init_api.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_invocation_exception.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_logger_function_ids.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_logger_otel.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_middleware.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_pubsub.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_register_function_args.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_streams_runtime_annotations.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_telemetry.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_telemetry_exporters.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_telemetry_types.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_trace_helpers.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_trigger_metadata.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_utils.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_worker_metadata.py +0 -0
- {iii_sdk-0.11.3.dev2 → iii_sdk-0.11.4.dev2}/tests/test_worker_metrics.py +0 -0
|
@@ -27,7 +27,6 @@ from .iii_constants import (
|
|
|
27
27
|
InitOptions,
|
|
28
28
|
)
|
|
29
29
|
from .iii_types import (
|
|
30
|
-
FunctionInfo,
|
|
31
30
|
HttpInvocationConfig,
|
|
32
31
|
InvocationResultMessage,
|
|
33
32
|
InvokeFunctionMessage,
|
|
@@ -44,13 +43,10 @@ from .iii_types import (
|
|
|
44
43
|
StreamChannelRef,
|
|
45
44
|
TriggerActionEnqueue,
|
|
46
45
|
TriggerActionVoid,
|
|
47
|
-
TriggerInfo,
|
|
48
46
|
TriggerRequest,
|
|
49
|
-
TriggerTypeInfo,
|
|
50
47
|
UnregisterFunctionMessage,
|
|
51
48
|
UnregisterTriggerMessage,
|
|
52
49
|
UnregisterTriggerTypeMessage,
|
|
53
|
-
WorkerInfo,
|
|
54
50
|
)
|
|
55
51
|
from .stream import (
|
|
56
52
|
IStream,
|
|
@@ -128,11 +124,6 @@ class III:
|
|
|
128
124
|
self._reconnect_task: asyncio.Task[None] | None = None
|
|
129
125
|
self._running = False
|
|
130
126
|
self._receiver_task: asyncio.Task[None] | None = None
|
|
131
|
-
self._functions_available_callbacks: set[
|
|
132
|
-
Callable[[list[FunctionInfo]], None]
|
|
133
|
-
] = set()
|
|
134
|
-
self._functions_available_trigger: Trigger | None = None
|
|
135
|
-
self._functions_available_function_id: str | None = None
|
|
136
127
|
self._reconnection_config = (
|
|
137
128
|
self._options.reconnection_config or DEFAULT_RECONNECTION_CONFIG
|
|
138
129
|
)
|
|
@@ -1096,148 +1087,6 @@ class III:
|
|
|
1096
1087
|
invocation_id=invocation_id,
|
|
1097
1088
|
)
|
|
1098
1089
|
|
|
1099
|
-
def list_functions(self) -> list[FunctionInfo]:
|
|
1100
|
-
"""List all functions registered with the engine across all workers.
|
|
1101
|
-
|
|
1102
|
-
Returns:
|
|
1103
|
-
A list of ``FunctionInfo`` objects describing each function.
|
|
1104
|
-
|
|
1105
|
-
Examples:
|
|
1106
|
-
>>> for fn in iii.list_functions():
|
|
1107
|
-
... print(fn.function_id, fn.description)
|
|
1108
|
-
"""
|
|
1109
|
-
return self._run_on_loop(self.list_functions_async())
|
|
1110
|
-
|
|
1111
|
-
async def list_functions_async(self) -> list[FunctionInfo]:
|
|
1112
|
-
"""List all functions registered with the engine across all workers.
|
|
1113
|
-
|
|
1114
|
-
Returns:
|
|
1115
|
-
A list of ``FunctionInfo`` objects describing each function.
|
|
1116
|
-
|
|
1117
|
-
Examples:
|
|
1118
|
-
>>> for fn in await iii.list_functions_async():
|
|
1119
|
-
... print(fn.function_id, fn.description)
|
|
1120
|
-
"""
|
|
1121
|
-
result = await self.trigger_async(
|
|
1122
|
-
{"function_id": "engine::functions::list", "payload": {}}
|
|
1123
|
-
)
|
|
1124
|
-
functions_data = result.get("functions", [])
|
|
1125
|
-
return [FunctionInfo(**f) for f in functions_data]
|
|
1126
|
-
|
|
1127
|
-
def list_workers(self) -> list[WorkerInfo]:
|
|
1128
|
-
"""List all workers currently connected to the engine.
|
|
1129
|
-
|
|
1130
|
-
Returns:
|
|
1131
|
-
A list of ``WorkerInfo`` objects with worker metadata.
|
|
1132
|
-
|
|
1133
|
-
Examples:
|
|
1134
|
-
>>> for w in iii.list_workers():
|
|
1135
|
-
... print(w.name, w.worker_id)
|
|
1136
|
-
"""
|
|
1137
|
-
return self._run_on_loop(self.list_workers_async())
|
|
1138
|
-
|
|
1139
|
-
async def list_workers_async(self) -> list[WorkerInfo]:
|
|
1140
|
-
"""List all workers currently connected to the engine.
|
|
1141
|
-
|
|
1142
|
-
Returns:
|
|
1143
|
-
A list of ``WorkerInfo`` objects with worker metadata.
|
|
1144
|
-
|
|
1145
|
-
Examples:
|
|
1146
|
-
>>> for w in await iii.list_workers_async():
|
|
1147
|
-
... print(w.name, w.worker_id)
|
|
1148
|
-
"""
|
|
1149
|
-
result = await self.trigger_async(
|
|
1150
|
-
{"function_id": "engine::workers::list", "payload": {}}
|
|
1151
|
-
)
|
|
1152
|
-
workers_data = result.get("workers", [])
|
|
1153
|
-
return [WorkerInfo(**w) for w in workers_data]
|
|
1154
|
-
|
|
1155
|
-
def list_triggers(self, include_internal: bool = False) -> list[TriggerInfo]:
|
|
1156
|
-
"""List all triggers registered with the engine.
|
|
1157
|
-
|
|
1158
|
-
Args:
|
|
1159
|
-
include_internal: If ``True``, include engine-internal triggers
|
|
1160
|
-
(e.g. ``functions-available``). Defaults to ``False``.
|
|
1161
|
-
|
|
1162
|
-
Returns:
|
|
1163
|
-
A list of ``TriggerInfo`` objects.
|
|
1164
|
-
|
|
1165
|
-
Examples:
|
|
1166
|
-
>>> triggers = iii.list_triggers()
|
|
1167
|
-
>>> internal = iii.list_triggers(include_internal=True)
|
|
1168
|
-
"""
|
|
1169
|
-
return self._run_on_loop(self.list_triggers_async(include_internal))
|
|
1170
|
-
|
|
1171
|
-
async def list_triggers_async(
|
|
1172
|
-
self, include_internal: bool = False
|
|
1173
|
-
) -> list[TriggerInfo]:
|
|
1174
|
-
"""List all triggers registered with the engine.
|
|
1175
|
-
|
|
1176
|
-
Args:
|
|
1177
|
-
include_internal: If ``True``, include engine-internal triggers
|
|
1178
|
-
(e.g. ``functions-available``). Defaults to ``False``.
|
|
1179
|
-
|
|
1180
|
-
Returns:
|
|
1181
|
-
A list of ``TriggerInfo`` objects.
|
|
1182
|
-
|
|
1183
|
-
Examples:
|
|
1184
|
-
>>> triggers = await iii.list_triggers_async()
|
|
1185
|
-
>>> internal = await iii.list_triggers_async(include_internal=True)
|
|
1186
|
-
"""
|
|
1187
|
-
result = await self.trigger_async(
|
|
1188
|
-
{
|
|
1189
|
-
"function_id": "engine::triggers::list",
|
|
1190
|
-
"payload": {"include_internal": include_internal},
|
|
1191
|
-
}
|
|
1192
|
-
)
|
|
1193
|
-
triggers_data = result.get("triggers", [])
|
|
1194
|
-
return [TriggerInfo(**t) for t in triggers_data]
|
|
1195
|
-
|
|
1196
|
-
def list_trigger_types(
|
|
1197
|
-
self, include_internal: bool = False
|
|
1198
|
-
) -> list[TriggerTypeInfo]:
|
|
1199
|
-
"""List all trigger types registered with the engine.
|
|
1200
|
-
|
|
1201
|
-
Args:
|
|
1202
|
-
include_internal: If ``True``, include engine-internal trigger
|
|
1203
|
-
types (e.g. ``engine::functions-available``). Defaults to ``False``.
|
|
1204
|
-
|
|
1205
|
-
Returns:
|
|
1206
|
-
A list of ``TriggerTypeInfo`` objects with ``trigger_request_format``
|
|
1207
|
-
and ``call_request_format`` schemas.
|
|
1208
|
-
|
|
1209
|
-
Examples:
|
|
1210
|
-
>>> trigger_types = iii.list_trigger_types()
|
|
1211
|
-
>>> for tt in trigger_types:
|
|
1212
|
-
... print(tt.id, tt.trigger_request_format)
|
|
1213
|
-
"""
|
|
1214
|
-
return self._run_on_loop(self.list_trigger_types_async(include_internal))
|
|
1215
|
-
|
|
1216
|
-
async def list_trigger_types_async(
|
|
1217
|
-
self, include_internal: bool = False
|
|
1218
|
-
) -> list[TriggerTypeInfo]:
|
|
1219
|
-
"""List all trigger types registered with the engine.
|
|
1220
|
-
|
|
1221
|
-
Args:
|
|
1222
|
-
include_internal: If ``True``, include engine-internal trigger
|
|
1223
|
-
types (e.g. ``engine::functions-available``). Defaults to ``False``.
|
|
1224
|
-
|
|
1225
|
-
Returns:
|
|
1226
|
-
A list of ``TriggerTypeInfo`` objects with ``trigger_request_format``
|
|
1227
|
-
and ``call_request_format`` schemas.
|
|
1228
|
-
|
|
1229
|
-
Examples:
|
|
1230
|
-
>>> trigger_types = await iii.list_trigger_types_async()
|
|
1231
|
-
"""
|
|
1232
|
-
result = await self.trigger_async(
|
|
1233
|
-
{
|
|
1234
|
-
"function_id": "engine::trigger-types::list",
|
|
1235
|
-
"payload": {"include_internal": include_internal},
|
|
1236
|
-
}
|
|
1237
|
-
)
|
|
1238
|
-
types_data = result.get("trigger_types", [])
|
|
1239
|
-
return [TriggerTypeInfo(**t) for t in types_data]
|
|
1240
|
-
|
|
1241
1090
|
def create_channel(self, buffer_size: int | None = None) -> Channel:
|
|
1242
1091
|
"""Create a streaming channel pair for worker-to-worker data transfer.
|
|
1243
1092
|
|
|
@@ -1341,69 +1190,6 @@ class III:
|
|
|
1341
1190
|
)
|
|
1342
1191
|
asyncio.run_coroutine_threadsafe(self._send(msg), self._loop)
|
|
1343
1192
|
|
|
1344
|
-
def on_functions_available(
|
|
1345
|
-
self, callback: Callable[[list[FunctionInfo]], None]
|
|
1346
|
-
) -> Callable[[], None]:
|
|
1347
|
-
"""Subscribe to function-availability events from the engine.
|
|
1348
|
-
|
|
1349
|
-
The callback fires whenever the set of available functions changes
|
|
1350
|
-
(e.g. a new worker connects or a function is unregistered).
|
|
1351
|
-
|
|
1352
|
-
Args:
|
|
1353
|
-
callback (Callable[[list[FunctionInfo]], None]): Receives the
|
|
1354
|
-
current list of ``FunctionInfo`` objects each time
|
|
1355
|
-
availability changes.
|
|
1356
|
-
|
|
1357
|
-
Returns:
|
|
1358
|
-
A callable that unsubscribes when called. Calling the
|
|
1359
|
-
returned function removes the callback and, if no callbacks
|
|
1360
|
-
remain, tears down the internal trigger.
|
|
1361
|
-
|
|
1362
|
-
Examples:
|
|
1363
|
-
>>> def on_change(functions):
|
|
1364
|
-
... print("Available:", [f.function_id for f in functions])
|
|
1365
|
-
>>> unsub = iii.on_functions_available(on_change)
|
|
1366
|
-
>>> # later ...
|
|
1367
|
-
>>> unsub()
|
|
1368
|
-
"""
|
|
1369
|
-
self._functions_available_callbacks.add(callback)
|
|
1370
|
-
|
|
1371
|
-
if not self._functions_available_trigger:
|
|
1372
|
-
if not self._functions_available_function_id:
|
|
1373
|
-
self._functions_available_function_id = (
|
|
1374
|
-
f"iii.on_functions_available.{uuid.uuid4()}"
|
|
1375
|
-
)
|
|
1376
|
-
|
|
1377
|
-
function_id = self._functions_available_function_id
|
|
1378
|
-
if function_id not in self._functions:
|
|
1379
|
-
|
|
1380
|
-
async def handler(data: dict[str, Any]) -> None:
|
|
1381
|
-
functions_data = data.get("functions", [])
|
|
1382
|
-
functions = [FunctionInfo(**f) for f in functions_data]
|
|
1383
|
-
for cb in list(self._functions_available_callbacks):
|
|
1384
|
-
cb(functions)
|
|
1385
|
-
|
|
1386
|
-
self.register_function({"id": function_id}, handler)
|
|
1387
|
-
|
|
1388
|
-
self._functions_available_trigger = self.register_trigger(
|
|
1389
|
-
{
|
|
1390
|
-
"type": "engine::functions-available",
|
|
1391
|
-
"function_id": function_id,
|
|
1392
|
-
"config": {},
|
|
1393
|
-
}
|
|
1394
|
-
)
|
|
1395
|
-
|
|
1396
|
-
def unsubscribe() -> None:
|
|
1397
|
-
self._functions_available_callbacks.discard(callback)
|
|
1398
|
-
if (
|
|
1399
|
-
len(self._functions_available_callbacks) == 0
|
|
1400
|
-
and self._functions_available_trigger
|
|
1401
|
-
):
|
|
1402
|
-
self._functions_available_trigger.unregister()
|
|
1403
|
-
self._functions_available_trigger = None
|
|
1404
|
-
|
|
1405
|
-
return unsubscribe
|
|
1406
|
-
|
|
1407
1193
|
def create_stream(self, stream_name: str, stream: IStream[Any]) -> None:
|
|
1408
1194
|
"""Register a custom stream implementation, overriding the engine default.
|
|
1409
1195
|
|
|
@@ -10,7 +10,6 @@ from typing import TYPE_CHECKING, Any, Awaitable, Callable, Generic, Protocol, T
|
|
|
10
10
|
from pydantic import BaseModel, ConfigDict, Field
|
|
11
11
|
|
|
12
12
|
from .iii_types import (
|
|
13
|
-
FunctionInfo,
|
|
14
13
|
HttpInvocationConfig,
|
|
15
14
|
RegisterFunctionInput,
|
|
16
15
|
RegisterFunctionMessage,
|
|
@@ -19,7 +18,6 @@ from .iii_types import (
|
|
|
19
18
|
RegisterTriggerTypeInput,
|
|
20
19
|
RegisterTriggerTypeMessage,
|
|
21
20
|
StreamChannelRef,
|
|
22
|
-
TriggerInfo,
|
|
23
21
|
TriggerRequest,
|
|
24
22
|
)
|
|
25
23
|
from .stream import IStream
|
|
@@ -80,11 +78,6 @@ class RemoteServiceFunctionData(BaseModel):
|
|
|
80
78
|
handler: RemoteFunctionHandler
|
|
81
79
|
|
|
82
80
|
|
|
83
|
-
# Type aliases for registration inputs
|
|
84
|
-
# Callback type for functions available event
|
|
85
|
-
FunctionsAvailableCallback = Callable[[list[FunctionInfo]], None]
|
|
86
|
-
|
|
87
|
-
|
|
88
81
|
class IIIClient(Protocol):
|
|
89
82
|
"""Protocol for III client implementations."""
|
|
90
83
|
|
|
@@ -108,18 +101,10 @@ class IIIClient(Protocol):
|
|
|
108
101
|
|
|
109
102
|
def unregister_trigger_type(self, trigger_type: RegisterTriggerTypeInput | dict[str, Any]) -> None: ...
|
|
110
103
|
|
|
111
|
-
def list_trigger_types(self, include_internal: bool = False) -> list[Any]: ...
|
|
112
|
-
|
|
113
104
|
def create_channel(self, buffer_size: int | None = None) -> Channel: ...
|
|
114
105
|
|
|
115
106
|
def create_stream(self, stream_name: str, stream: IStream[Any]) -> None: ...
|
|
116
107
|
|
|
117
|
-
def list_functions(self) -> list[FunctionInfo]: ...
|
|
118
|
-
|
|
119
|
-
def list_triggers(self, include_internal: bool = False) -> list[TriggerInfo]: ...
|
|
120
|
-
|
|
121
|
-
def on_functions_available(self, callback: FunctionsAvailableCallback) -> Callable[[], None]: ...
|
|
122
|
-
|
|
123
108
|
def shutdown(self) -> None: ...
|
|
124
109
|
|
|
125
110
|
|
|
@@ -194,6 +194,42 @@ async def test_custom_status_code(engine_http_url, iii_client: III):
|
|
|
194
194
|
trigger.unregister()
|
|
195
195
|
|
|
196
196
|
|
|
197
|
+
@pytest.mark.asyncio
|
|
198
|
+
async def test_content_type_on_api_response_return(engine_http_url, iii_client: III):
|
|
199
|
+
"""Returning an ApiResponse dict with headers should set the response Content-Type."""
|
|
200
|
+
xml_body = '<?xml version="1.0" encoding="UTF-8"?><note><to>user</to><body>hello</body></note>'
|
|
201
|
+
|
|
202
|
+
def handler(_input_data):
|
|
203
|
+
return {
|
|
204
|
+
"status_code": 200,
|
|
205
|
+
"headers": {"Content-Type": "text/xml"},
|
|
206
|
+
"body": xml_body,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
fn_ref = iii_client.register_function({"id": "test.api.xml.return.py"}, handler)
|
|
210
|
+
trigger = iii_client.register_trigger(
|
|
211
|
+
{
|
|
212
|
+
"type": "http",
|
|
213
|
+
"function_id": "test.api.xml.return.py",
|
|
214
|
+
"config": {
|
|
215
|
+
"api_path": "test/py/xml-return",
|
|
216
|
+
"http_method": "POST",
|
|
217
|
+
},
|
|
218
|
+
}
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
time.sleep(0.3)
|
|
222
|
+
|
|
223
|
+
async with aiohttp.ClientSession() as session:
|
|
224
|
+
async with session.post(f"{engine_http_url}/test/py/xml-return") as resp:
|
|
225
|
+
assert resp.status == 200
|
|
226
|
+
assert resp.headers.get("content-type") == "text/xml"
|
|
227
|
+
assert await resp.text() == xml_body
|
|
228
|
+
|
|
229
|
+
fn_ref.unregister()
|
|
230
|
+
trigger.unregister()
|
|
231
|
+
|
|
232
|
+
|
|
197
233
|
@pytest.mark.asyncio
|
|
198
234
|
async def test_download_pdf_streaming(engine_http_url, iii_client: III):
|
|
199
235
|
"""Stream a PDF file as a download response."""
|
|
@@ -10,21 +10,6 @@ def test_trigger_async_is_coroutine_function():
|
|
|
10
10
|
assert inspect.iscoroutinefunction(III.trigger_async)
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def test_list_functions_async_is_coroutine_function():
|
|
14
|
-
assert hasattr(III, "list_functions_async")
|
|
15
|
-
assert inspect.iscoroutinefunction(III.list_functions_async)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def test_list_workers_async_is_coroutine_function():
|
|
19
|
-
assert hasattr(III, "list_workers_async")
|
|
20
|
-
assert inspect.iscoroutinefunction(III.list_workers_async)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def test_list_triggers_async_is_coroutine_function():
|
|
24
|
-
assert hasattr(III, "list_triggers_async")
|
|
25
|
-
assert inspect.iscoroutinefunction(III.list_triggers_async)
|
|
26
|
-
|
|
27
|
-
|
|
28
13
|
def test_create_channel_async_is_coroutine_function():
|
|
29
14
|
assert hasattr(III, "create_channel_async")
|
|
30
15
|
assert inspect.iscoroutinefunction(III.create_channel_async)
|
|
@@ -44,9 +29,6 @@ def test_async_methods_have_docstrings():
|
|
|
44
29
|
"""All public async methods must have docstrings."""
|
|
45
30
|
async_methods = [
|
|
46
31
|
"trigger_async",
|
|
47
|
-
"list_functions_async",
|
|
48
|
-
"list_workers_async",
|
|
49
|
-
"list_triggers_async",
|
|
50
32
|
"create_channel_async",
|
|
51
33
|
"connect_async",
|
|
52
34
|
"shutdown_async",
|
|
@@ -4,7 +4,7 @@ import asyncio
|
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
-
from iii import TriggerAction
|
|
7
|
+
from iii import FunctionInfo, TriggerAction
|
|
8
8
|
from iii.iii import III
|
|
9
9
|
|
|
10
10
|
|
|
@@ -21,7 +21,10 @@ async def wait_for(condition, timeout=5.0, interval=0.1):
|
|
|
21
21
|
@pytest.mark.asyncio
|
|
22
22
|
async def test_connect_successfully(iii_client: III):
|
|
23
23
|
"""SDK connects to the engine and can list functions."""
|
|
24
|
-
|
|
24
|
+
result = iii_client.trigger(
|
|
25
|
+
{"function_id": "engine::functions::list", "payload": {}}
|
|
26
|
+
)
|
|
27
|
+
functions = [FunctionInfo(**f) for f in result.get("functions", [])]
|
|
25
28
|
assert isinstance(functions, list)
|
|
26
29
|
|
|
27
30
|
|
|
@@ -83,13 +86,16 @@ async def test_invoke_function_fire_and_forget(iii_client: III):
|
|
|
83
86
|
|
|
84
87
|
@pytest.mark.asyncio
|
|
85
88
|
async def test_list_registered_functions(iii_client: III):
|
|
86
|
-
"""Registered function IDs appear in
|
|
89
|
+
"""Registered function IDs appear in the engine functions list."""
|
|
87
90
|
fn1 = iii_client.register_function({"id": "test.bridge.py.list.func1"}, lambda _: {})
|
|
88
91
|
fn2 = iii_client.register_function({"id": "test.bridge.py.list.func2"}, lambda _: {})
|
|
89
92
|
await asyncio.sleep(0.3)
|
|
90
93
|
|
|
91
94
|
try:
|
|
92
|
-
|
|
95
|
+
result = iii_client.trigger(
|
|
96
|
+
{"function_id": "engine::functions::list", "payload": {}}
|
|
97
|
+
)
|
|
98
|
+
functions = [FunctionInfo(**f) for f in result.get("functions", [])]
|
|
93
99
|
function_ids = [f.function_id for f in functions]
|
|
94
100
|
|
|
95
101
|
assert "test.bridge.py.list.func1" in function_ids
|