Skip to content

Tutorial 8 · Python SDK

Goal: drive the whole session lifecycle from Python — create, exec, pause, resume — with typed errors instead of status-code parsing, and see the one-line migration path from E2B.

Prereq: the one-time setup key, and the SDK installed:

pip install barista-sdk        # not yet on PyPI — until then, from this repo:
pip install -e sdk/python

The client resolves its config from arguments or the environment — same variables the CLI uses, plus the key:

export BARISTA_URL=https://beta.barista.sh
export BARISTA_API_KEY=<your-api-key>

Hello world

from barista_sdk import Session

s = Session.open("hello")                   # get-or-create by name (platform default template)
print(s.exec(["echo", "hi"]).stdout)        # hi — waits through boot for you

open is the DO-shaped door (bar-052): the name is the API — created if missing, reused if it exists, and exec wakes it if it's parked. Pass template="cc" (from tutorial 7) to pick the image; Session.create(...) remains for explicit create-only semantics (it refuses an existing name with 409 — never a silent overwrite).

The full lifecycle

from barista_sdk import NotFound, NotReady, Session

s = Session.create(
    name="worker1",
    template="cc",                    # or image= + digest=
    metadata={"team": "ml", "task": "eval"},   # find it later: barista ls --meta team=ml
    size="medium",                    # 2 vCPU / 2 GiB (small is the default)
    idle_pause_s=300,                 # auto-pause after 5 idle minutes (0 = never)
)

code, out, err = s.exec(["uname", "-a"])     # ExecResult unpacks as a 3-tuple

s.pause()                # freeze: memory kept, compute released
s.resume()               # continue the same live process
s.exec(["date"])         # picks up where it left off

s.delete()

Two behaviors worth knowing:

  • A paused session wakes on exec. The gateway resumes a paused session when you exec into it, so after an auto-pause your next s.exec(...) just works — added latency, not an error.
  • Readiness is absorbed (bar-052): exec waits through a session's boot or wake by default (ready_timeout=60), so there is no retry loop to write. Only when the bound elapses do you see NotReady (the gateway's retryable 409, carrying retry_after); pass wait=False if you'd rather handle it immediately yourself:
try:
    s.exec(["cat", "/tmp/ready"], wait=False)   # one attempt, no absorbed wait
except NotReady as e:
    print(f"still coming up — retry in {e.retry_after}s")

NotFound, AuthError, and APIError cover the rest — branch on types, not status codes.

A live run of the lifecycle and the E2B shim: NotReady retry, exec, pause, resume, delete

Coming from E2B

barista_sdk.e2b exposes E2B's Sandbox shape over sessions, so switching is an import-line change for the supported subset — and commands.run does the NotReady polling above for you:

from barista_sdk.e2b import Sandbox

sbx = Sandbox(template="cc")                 # or image= + digest=
result = sbx.commands.run("echo hi")         # polls while the sandbox boots
print(result.stdout, result.exit_code)       # hi  0

sbx.pause()
sbx = Sandbox.connect(sbx.sandbox_id)        # resumes — memory preserved
sbx.kill()

The honest differences from real E2B (no persistent run_code kernel, timeout= maps to pause-keeping-memory instead of a kill, in-place set_timeout raises) live in the reference: ../sdk-python.md, and in the barista_sdk.e2b module docstring's compatibility table.