# Python SDK

> Decorate a function, call it, and get the result back — Lakeshore handles the rest.

## Install

The distribution is `dreamlake-lakeshore` (hyphen); the import path is
`dreamlake.lakeshore` (dot). `import lakeshore` is not a thing —
`dreamlake` is an implicit namespace package, so there is no top-level
`lakeshore` module.

```bash
pip install dreamlake-lakeshore
uv add dreamlake-lakeshore        # or with uv
```

```python
import dreamlake.lakeshore as dls
```

Python 3.11 or newer. The runtime dependencies are small and pure-Python:
`cloudpickle`, `httpx`, `msgpack`, `params-proto`, `python-ulid`,
`pyyaml`, `typing-extensions`.

Verify the install, and read the version from `importlib.metadata` —
`dreamlake.lakeshore.__version__` is a hardcoded `"0.0.0"` placeholder
that does not track releases:

```bash
python -c "import dreamlake.lakeshore as dls; print(dls.SyncQueue)"
python -c "from importlib.metadata import version; print(version('dreamlake-lakeshore'))"
```

### Extras

| Extra | Pulls in |
| --- | --- |
| `test` | `pytest`, `pytest-xdist` |
| `dev` | `test` plus `build`, `ruff` |
| `server` | nothing today — a reserved, deliberately empty extra |
| `all` | `test` + `server` + `dev` |

```bash
pip install "dreamlake-lakeshore[dev]"
```

> **Warning:** `pyproject.toml` declares no `[project.scripts]`. There is no
> `lakeshore-daemon` command — it was retired in 0.3.6. Run a worker with
> `python -m dreamlake.lakeshore.daemon`, or through the TypeScript CLI
> (`lakeshore worker start`, npm `@dreamlake/lakeshore`). See
> [Dispatch planes](/python-sdk/dispatch.md#the-worker-entrypoint).

## One pattern, end to end

```python
import dreamlake.lakeshore as dls

@dls.udf(queue="compute")
def square(x: int) -> int:
    return x * x

q = dls.SyncQueue("compute")
ids = [square.submit(i) for i in range(8)]        # durable string ids
results = [q.result(i, timeout=60.0) for i in ids]
```

The list comprehension is the spawn; the result loop is the barrier.
There is no Future type — a pending job **is** its invocation id, a
plain string you can persist and reconnect with from any process.

> **Note:** The SDK has no `gather`, no `as_completed`, and no `Future` class. Fan-out
> is a comprehension of `submit` calls plus a loop of `q.result`; on the
> async `Queue`, stdlib `asyncio.gather` composes over `q.result` coroutines.

## Data UDFs in one breath

A data UDF reads and writes files through `dls.run` and returns plain
**string keys**. File bytes never ride the msgpack wire — only keys do.

```python
@dls.udf
def splats_to_mesh(splats: str) -> str:
    mesh = extract(dls.run.read(splats))          # key -> local Path
    mesh.save(dls.run.write("process/mesh.ply"))  # key -> local Path, recorded
    return "process/mesh.ply"                     # return your keys

with dls.scope("scenes/0007"):                    # prefix for read/write
    key = splats_to_mesh("source/splats.ply")
```

## Surfaces

| Surface | Read for |
| --- | --- |
| [`@udf` decorator](/python-sdk/udf.md) | One decorator, four body kinds (sync / async / generator / async-gen), the `dls.run` I/O seam, the return-your-keys contract, and function transport. |
| [Queue API](/python-sdk/queue.md) | `SyncQueue` (blocking) and `Queue` (async) — submit / result / call / stream, worker verbs, and `Topic` pub/sub over the frame journal. |
| [Invocation ids](/python-sdk/invocations.md) | The no-Future model: ids as durable handles, reconnect-by-id, fan-out + collect, and live streaming via `q.stream`. |
| [Dispatch planes](/python-sdk/dispatch.md) | The minimal local plane (`Dispatch`) for dev and tests, `HttpDispatch` against the Node control plane, and the `run_worker` loop. |

## What's exported

`dreamlake.lakeshore.__all__` is exactly these 22 names:

```python
from dreamlake.lakeshore import (
    # Functions
    udf, UDF, thunk, Thunk,
    # The dls.run I/O seam
    scope, run, RunContext,
    # Queues
    Queue, SyncQueue, Topic, Job,
    # Dispatch planes
    Dispatch, HttpDispatch,
    # Worker
    run_worker,
    # Storage
    Storage, TemporaryCredentials,
    # .dreamrc config
    DreamRc, Provider, RunConfig, load_config, resolve,
)
```

Imports are lazy (PEP 562): `import dreamlake.lakeshore` loads almost
nothing, and purely local code (bare `@udf`, `dls.scope`, `dls.run`)
never pulls in httpx or msgpack.

> **Warning:** `dls.run` is the I/O proxy for the ambient run context —
> `dls.run.read` / `dls.run.write` / `dls.run.read_all` /
> `dls.run.write_all` / `dls.run.prefix` / `dls.run.root`. It does not run
> anything. The entry points that execute work are `UDF.__call__`,
> `.submit`, `.remote`, `.local`, the queue verbs, and `dls.run_worker`.

## Runnable examples

The SDK repo ships seven zero-infrastructure examples that run on an
in-memory `Dispatch(":memory:")` plane — no control plane, no daemon:

| Example | Shows |
| --- | --- |
| `examples/00_hello_udf.py` | submit → `run_worker(once=True)` → result |
| `examples/01_fan_out.py` | N submits, collect by id |
| `examples/02_pipeline.py` | two stages, the DAG is ordinary Python |
| `examples/03_body_kinds.py` | sync / async / generator / async-generator |
| `examples/04_ids_not_futures.py` | ids as durable handles |
| `examples/05_streams_topics.py` | `q.stream` frames and `Topic` pub/sub |
| `examples/06_dynamic_graph.py` | work discovered at run time |

## Try it end-to-end

Walk the [happy paths](/python-sdk/happy-paths.md) — five flows from "the
decorator works, no network" up to storage access from a worker.
