Skip to main content

readie-client

Run a Python function on a remote checkpoint-restore worker by decorating it.

from readie import remote
import asyncio


@remote
def add(a, b):
    return a + b


print(add(1, 2))  # blocking


async def add_async(a, b):
    return await add.aio(a, b)


print(asyncio.run(add_async(1, 2)))  # from an event loop

The decorated function is serialised with cloudpickle and executed by a Python interpreter inside a gVisor sandbox, so it must be picklable and its imports must exist in the worker's image.

Resource budgets

Declare how much memory a function needs on the decorator. Each budget takes a byte count or a size string ("512Mi", "4Gi"); an unset budget falls back to the cluster default.

@remote(gpu=True, memory="2Gi", max_memory="8Gi")
def train(rows): ...

memory is the container's initial limit and max_memory the ceiling the worker may auto-expand to when the function needs more; gpu_memory / max_gpu_memory are the same for GPU device memory. gpu=True sends the call to a GPU worker (a gpu_memory budget implies it too). The client also statically extracts the function's imports and forwards them so the router can pick a checkpoint that already imported them - this is automatic and needs no configuration.

Packages

Declare PyPI requirements a function needs beyond what the checkpoint's rootfs already carries. The executor installs them with uv before every call:

@remote(packages=["numpy==1.26.0", "requests"])
def fetch(url): ...

Entries are requirement strings (a bare name or a full spec like "numpy==1.26.0"), normalized and deduped by distribution name at decoration time - "Requests" and "requests" collapse to one entry. Installation is unconditional: it runs on every call, even when the package looks already present, since each call restores a fresh, isolated container with nothing to check that claim against.

make install   # sync the virtualenv from the lockfile
make test      # unit + in-process gRPC integration tests
make lint type # ruff + mypy --strict

Configuration options:

import readie

readie.configure(
    router_uri="localhost:50051", timeout=120.0, chunk_size=64 * 1024, stream_logs=True, tls=False
)

The following are configurable only from the environment:

READIE_AUTH_TOKEN bearer token for a router that requires one; unset sends none
READIE_TLS_CA connect over TLS; a CA path verifies the nginx server, else system roots. TLS is required for HTTPS router URIs

When a remote function raises

You get the real traceback, from the process that raised it:

try:
    train()
except readie.RemoteExecutionError as error:
    print(error.remote_type)  # "ValueError"
    print(error.remote_traceback)  # the frames inside the sandbox
    print(error.worker_id, error.container_id)

The exception object is not reconstructed. Unpickling it would need its class importable here, and for a library that exists only in the worker image the resulting ImportError would replace the real error with a confusing one.

EmptyResultError means something else entirely: the worker returned nothing at all, so the executor died before it could report an outcome. A function that returns None returns a value and does not land there.

Sessions

By default every call is independent and gets a fresh container. To reuse a warm container - and the Python state it holds - open a session:

with readie.default_client().session() as s:
    load_data.bind(s)()  # cold start
    train.bind(s)()  # resumes the same interpreter

Calls inside one session are serialised by the router: a session maps to one container running one interpreter behind one socket, so they cannot safely run at once. Unrelated sessions stay fully parallel.

client.session() is a convenience wrapper - Session itself is a plain, constructible class, so you are not limited to the context-manager form:

s = readie.Session()
load_data.bind(s)()
train.bind(s)()
# ... later, maybe in another function ...
s.close()

There are three equivalent ways to run a call inside a session - decoration time, a bound copy, or per-call:

@remote(session=s)  # every call through this name uses s
def f(): ...


g = train.bind(s)  # a copy of an existing @remote bound to s
# (the original is untouched, since it is
# module-level and shared)

readie.default_client().call(train.func, session=s)  # one-off, via the client directly

Session() also accepts an explicit id (Session(session_id="sess-...")), so a session can be reconstructed - in another process, or after this one restarts - as long as its container hasn't since been torn down (below).

Closing a session is purely local. It just stops this client using the id; no message reaches the router. The router, in turn, never removes a session on a timer or a cap of its own - only once the container behind it is actually gone (typically the worker's own pause TTL reclaiming it; see the worker README) does it retire the id. So a session can safely outlive close(), but the two cases that follow are not the same:

  • A session_id the router has genuinely never seen (including a brand-new Session()) is not an error - the call just cold-starts, exactly like a call with no session at all.
  • Reusing a session_id whose container the router has already reclaimed raises readie.SessionExpiredError instead of silently cold-starting under an id that looks like it should still carry warm state. Open a new Session rather than retrying the old one.

Metadata

Release files for readie 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for readie 0.3.0
File Size Uploaded Provenance
readie-0.3.0.tar.gz 64.2 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for readie 0.3.0
File Interpreter ABI Platform
readie-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 104.5 kB

Release files / readie-0.3.0.tar.gz

Download URL readie-0.3.0.tar.gz
Size 64.2 kB
Tags Source
SHA-256 checksum
How to use checksums
91090b391cdeb8083c15cf9ef3b13eac7805d849eca5d416ed7780891bba62ff
BLAKE2b-256 checksum
How to use checksums
d3d9416bfa4194177b6042c5e599a4f76c6c82aaa3e026b13afb5e63b1d2ea57
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.20 {"installer":{"name":"uv","version":"0.12.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.

Transparency log

Release files / readie-0.3.0-py3-none-any.whl

Download URL readie-0.3.0-py3-none-any.whl
Size 40.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a3fe1d112e0cc01dc73b933aa75c736479cc7ace8a5a01e90495a76b61dfde70
BLAKE2b-256 checksum
How to use checksums
1e479b06fc2594db2549ebbf3d7ae6ad13f160bfc642d012109e4262062d98ef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.20 {"installer":{"name":"uv","version":"0.12.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.

Transparency log

Security for readie 0.3.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for readie-0.3.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.

Transparency log

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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page