Python binding#
Python uses a PyO3 extension containing the canonical Rust runtime. No Node, Chromium or simulation server is required. CPython 3.9+ is supported by the abi3 binding. A compatible prebuilt wheel needs no Rust toolchain.
Install#
python -m venv .venv
# Linux/macOS: source .venv/bin/activate
# Windows PowerShell: .venv\Scripts\Activate.ps1
python -m pip install computerworld==0.3.0
python -c "import computerworld; print(computerworld.__version__, computerworld.engine_version)"
# Expected: 0.3.0 0.3.0
PyPI carries wheels for Linux x86-64 and arm64 (manylinux2014), macOS arm64 and x86-64, and Windows x64, and no source distribution. 0.3.0 is the last public release of the engine and stays under the MIT License; newer engines run behind the hosted API, which the clients drive. Wheels contain the runtime, not an implicit company world. Download the tagged source archive for example world definitions and runnable Python demos, or supply your own definition. API and checkpoint compatibility may change between 0.x releases; keep the exact version with your episode records.
Build from source#
The engine's source is not public. Building it is for COMPUTACO and its partners, from the
engine checkout: python -m pip install . there builds the extension with maturin (a Rust
toolchain is required), and python examples/python/smoke.py checks it. To use ComputerWorld
without the source, install 0.3.0 above or use the hosted API.
Threading#
World, Environment and Snapshot may be passed to other threads and called from
them. A World and every Environment minted from it share one lock around the Rust
world, so calls from several threads are serialized, not concurrent: a call that
arrives while another is running waits for it. Nothing is silently dropped and nothing
races.
import threading
frame = {}
t = threading.Thread(target=lambda: frame.update(env.render(320, 240)))
t.start(); t.join() # works: the render happens on that thread
What threads do not buy you is parallel simulation. One World is one state machine,
and the order its actions land in is the order the threads took the lock — which is not
deterministic across runs. Keep an episode on one thread, or give each thread its own
World (World(definition, seed), or world.fork(world.snapshot()), whose result is an
independent world). Threads are for keeping a UI responsive or for driving independent
worlds side by side, not for splitting one episode.
If a call panics inside the simulation, the lock is poisoned and every later call on that
world raises RuntimeError naming the situation. That is a deliberate change from
earlier alpha builds, where the objects were marked unsendable and a call from a second
thread aborted the interpreter instead of raising. Nothing here aborts the host
process. crates/bindings/python/tests/test_threading.py asserts both halves: a call from a
second thread succeeds, and a poisoned world raises rather than dying.
Environment exposes id, step, observe, scene(width=1024, height=768) and
render(width=1024, height=768). Scene access requires a semantic.v1 or pixels.v1
observation grant. scene returns layout/interaction data without rasterization.
render returns {"width": int, "height": int, "rgba": bytes}. Use an image library
to encode those bytes as PNG; no host image library participates in simulation.
See programmatic computer use for desktop launch, keyboard input, pointer dragging, transformed hit targets, PNG output and owner checkpoint examples. The runnable computer interaction demo is intended as a starting point for an agent integration.
World owns reset, snapshots, portable export_snapshot()/import_snapshot(text),
definition(), trajectory(), state_hash() and privileged inspect(). Retain it
in the harness; provide only the environment to actor code. world.session(env.id)
reattaches a restricted handle after fork/import. Python only converts values; it
contains no separate command, service or desktop semantics.
The owner also supports world.add_computer(computer, node, links) and
world.remove_computer(id). These preserve other devices and service state.
See device lifecycle and checkpoint rules.
Every member of both handles is listed in the Python API. Pinning, and what changes between 0.x releases, is migration.