Skip to content

Quick start

Requirements

KlorPy is a single Python package (stdlib + asyncio only), Python ≥ 3.11.

$ cd prototype/python
$ python -m venv .venv && . .venv/bin/activate
$ pip install -e .        # klorpy + pytest

Your first choreography

Roles are plain objects (A, B, C, …). A choreography is an ordinary function decorated with @choreography:

from klorpy import A, B, choreography, move, simulate_chor

@choreography
def ping_pong(x):
    y = move(x, A, B)   # send x from A to B; the result lives at B
    return y

result = simulate_chor(ping_pong, {"x": 42})
assert result == {"A": NOOP, "B": 42}

What just happened:

  1. @choreography read the function's source, analyzed every value's location (here: x is available to both roles, y only to B),
  2. projected it into one small program per role: A sends x, B receives it and reports the result, and
  3. simulate_chor ran both projections concurrently, bridged by in-memory queues, and gathered each role's result. Roles with no result get NOOP.

The special operators

Form Meaning
A(x) / lift(x, A) lift an expression to a role
copy(x, A, B) communicate x A → B; result at {A, B}
move(x, A, B) A → B; result at {B}
narrow(x, {A}) restrict location to {A}
pack(a, b) / unpack([x, y], t) build / destructure tuple values
if / match / while choreographic control flow (see the language pages)

These special operators are recognized statically from the source — they never run. Using one outside a @choreography function raises a clear error.

Play one role

For a real deployment you run each role separately, against a transport you provide:

from klorpy import play_role

config = {
    "role": B.name,
    "send": send_fn,          # async (loc, value) -> None
    "recv": recv_fn,          # async (loc) -> value
    "locators": {A.name: "...address of A..."},
}
play_role(config, ping_pong, {"x": 99})

The ping_pong role A returns NOOP; role B returns 99.

Run over real TCP

See Networking (TCP) for the bundled TcpTransport and a two-process demo.

Run the examples

$ python examples.py        # one-process showcase
$ python examples_network.py  # A and B in two OS processes over TCP
$ python -m pytest tests/   # the test suite (47 tests)

Failure mode

Everything here validates at definition time. A mistyped choreography (a value used where it isn't located, a guard that only one role can see, a tuple moved across roles, …) raises ProjectionError the moment the decorator runs — never at runtime.