This release is a pre-release and may not be stable for production use.
iii-sdk
Python SDK for the iii engine.
Install
pip install iii-sdk
Hello World
from iii import register_worker
iii = register_worker("ws://localhost:49134")
def greet(data):
return {"message": f"Hello, {data['name']}!"}
iii.register_function("hello::greet", greet)
result = iii.trigger({"function_id": "hello::greet", "payload": {"name": "world"}})
print(result) # {"message": "Hello, world!"}
API
| Operation | Signature | Description |
|---|---|---|
| Initialize | register_worker(url, options?) |
Create an SDK instance and auto-connect |
| Register function | iii.register_function(id, handler) |
Register a function that can be invoked by name |
| Invoke (await result) | iii.trigger({"function_id": id, "payload": data}) |
Invoke a function and wait for the result |
| Invoke (fire-and-forget) | iii.trigger({"function_id": id, ..., "action": TriggerAction.Void()}) |
Fire-and-forget |
| Invoke (enqueue) | iii.trigger({"function_id": id, ..., "action": TriggerAction.Enqueue(queue="name")}) |
Route invocation through a named queue |
| Observe | iii.observe({"function_id": ..., "args": ..., "filters": ...}, target, options?) |
Invoke target on every matching event of a function |
| Current invocation | iii.current_invocation() |
The invocation the running handler serves |
| Shutdown | iii.shutdown() |
Disconnect and stop background thread |
register_worker() creates the SDK instance and auto-connects to the engine.
Registering Functions
def create_order(data):
return {"status_code": 201, "body": {"id": "123", "item": data["body"]["item"]}}
iii.register_function("orders::create", create_order)
Invoking Functions
result = iii.trigger({"function_id": "orders::create", "payload": {"body": {"item": "widget"}}})
trigger() blocks the calling thread; await iii.trigger_async(...) works from
any event loop. A call to a function this worker registered itself runs
in-process once the engine confirmed it (no frames on the wire unless someone
observes it): a sync handler on the calling thread, an async one on the
calling loop. Pass "dispatch": "remote" to go through the engine anyway, or
register the function with local_dispatch=False.
Observing Functions
Every function is observable. The target is a registered function; it receives
the Observed envelope (input, output or error, caller, ...).
def big_order(envelope):
notify(envelope["input"], envelope["output"])
iii.register_function("alerts::big-order", big_order)
observer = iii.observe(
{"function_id": "orders::create", "filters": {"input.body.total": {"$gte": 500}}},
"alerts::big-order",
{"max_in_flight": 4},
)
observer.ready() # or: await observer.ready_async()
# later
observer.dispose()
A function can hand its own events to the observers they belong to (owner delivery), and react when one is added or removed:
from iii import ObserveHooks, Observer
schedules: dict[str, set[Observer]] = {} # expression -> observers
class CronHooks(ObserveHooks):
delivery = "owner"
args_format = {"type": "object", "required": ["expression"],
"properties": {"expression": {"type": "string"}}}
def on_observer_added(self, obs):
schedules.setdefault(obs.args["expression"], set()).add(obs)
def on_observer_removed(self, obs):
schedules[obs.args["expression"]].discard(obs)
def on_tick(tick):
for obs in schedules.get(tick["expression"], ()):
obs.emit(tick) # the observer's filters are checked here
iii.register_function("cron::on-tick", on_tick, observe=CronHooks())
Inside any handler, iii.current_invocation() returns the running invocation
(function_id, invocation_id, caller, activation, observe_depth). Sync
handlers run on a thread of their own started in a copy of the caller's
context, so the current invocation and the OpenTelemetry context follow them.
Trigger APIs (deprecated)
register_trigger and register_trigger_type keep working in 0.25 but are
deprecated: they emit a DeprecationWarning and log a warning once per
process. Use iii.observe(source, target) and
register_function(id, handler, observe=ObserveHooks(...)) instead. See the
migration guide.
Development
Install in development mode
pip install -e .
Type checking
mypy src
Linting
ruff check src
Resources
Metadata
Release files for iii-sdk 0.25.0rc2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| iii_sdk-0.25.0rc2.tar.gz | 303.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| iii_sdk-0.25.0rc2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 403.4 kB
Release files / iii_sdk-0.25.0rc2.tar.gz
| Download URL | iii_sdk-0.25.0rc2.tar.gz |
|---|---|
| Size | 303.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b7f76e5ed2ecd81923cc4dc48cb53e3fa4f2b482f0bc137c93250018b20bed66
|
|
BLAKE2b-256 checksum How to use checksums |
6681f18fe12dea3d3019e5871b8d968bfe201440e17282bdc1cb36c374672a56
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / iii_sdk-0.25.0rc2-py3-none-any.whl
| Download URL | iii_sdk-0.25.0rc2-py3-none-any.whl |
|---|---|
| Size | 99.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9774aad08a503aa661576af433ba1ed86deed0e1e5b5d46fd3d6bcd395717fb6
|
|
BLAKE2b-256 checksum How to use checksums |
537658c6d36da221acf44e3a3becab86bb53af6e1850cbb410c81a80543c2c53
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Security for iii-sdk 0.25.0rc2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.