Tensnap Python Bindings
Python bindings for the TenSnap protocol v0.3 runtime.
Installation
Requires Python 3.10+ and websockets>=13.0. The server uses the websockets.asyncio API introduced in websockets 13.0.
pip install tensnap
Quick Start
import asyncio
from tensnap import (
SimulationScenario,
agent,
agent_layer,
chart,
env,
grid_layer,
params,
)
@agent(x="position[0]", y="position[1]")
class Bird:
def __init__(self, bird_id: int, position: tuple[int, int]):
self.id = bird_id
self.position = position
@grid_layer()
@agent_layer("birds")
@env(id="main")
class Aviary:
def __init__(self):
self.width = 20
self.height = 10
self.birds = [Bird(1, (2, 3)), Bird(2, (4, 5))]
def step(self) -> None:
for bird in self.birds:
x, y = bird.position
bird.position = (x + 1, y)
@chart("population", "Population")
def population(self) -> int:
return len(self.birds)
@params(include=["speed"])
class Config:
speed = 1.0
scenario = SimulationScenario(port=8765)
model = Aviary()
config = Config()
scenario.add_all(model)
scenario.add_all(config)
async def main() -> None:
await scenario.register_model_handler(model_step=model.step)
await scenario.run()
if __name__ == "__main__":
asyncio.run(main())
tensnap.bindings is the unified attach/readback surface for Python bindings. Use it for decorators such as env, grid_layer, and agent_layer, and for readback helpers such as environment_binding, layer_bindings, and bindings.
Layer and item binding fields now support these common forms:
- omitted fields,
None, orauto()for one-time same-name discovery from the class or initialized instance - selector strings such as
x="pos[0]"oritem_iterable_projector="agents"when the source path differs from the output field or layer id - literal values such as
coord_offset="float"oricon="circle"; arbitrary strings still default to selectors unless they match a known literal value, CSS-like string such as"#3498db", or JSON-like string - explicit helpers such as
attr("pos[0]"),value("red"),auto(), andskip() - direct callables such as
size=lambda agent: agent.radius
None/auto() field discovery is resolved once when instances initialize or when layer_bindings(instance) materializes an instance-specific binding, so fields assigned in __init__ are picked up without scanning during projection:
from tensnap import agent, agent_layer, grid_layer, value
@agent(x="position[0]", y="position[1]", color=value("red"))
class Bird:
def __init__(self, bird_id: int, position: tuple[int, int]):
self.id = bird_id
self.position = position
@grid_layer()
@agent_layer("birds")
class Aviary:
def __init__(self):
self.width = 20
self.height = 10
self.birds = [Bird(1, (2, 3))]
SimulationScenario registers the renderer-driven built-in actions start, step, and reset during construction. The initial synchronized state is always time 0, and the first simulated tick emitted by start or step is 1. For most targets, use scenario.add_all(target) to register available environment/layer, chart, and action bindings. Parameters are opt-in with @params(...) or an explicit config.
register_model_handler(model_init=None, model_step=None, model_reset=None) lets you keep reset distinct from init. If model_reset is omitted, the default handler falls back to model_init.
Reset reconciles declarations with strict CRUD, deletes the previous agent set before creating reset state, clears chart history, and preserves stable trajectory configuration so the renderer can apply on_reset.
add_all(..., dry_run=True) and the targeted add_* dry-run modes report registry ids without mutating the scenario. Mesa lifecycle helpers use this to unregister and rebuild model-owned registrations cleanly.
Trajectory layers expose length, width, color, z_index, on_agent_delete, on_state_sync, and on_reset as first-class decorator arguments. See the Python API reference for their lifecycle semantics.
Model-owned mappings, matrices, and indexed columns can use map_agent_layer, matrix_agent_layer, and indexed_agent_layer without creating visual agent wrappers. Built-in layer metadata goes in direct decorator keywords; custom metadata goes in metadata={...}. Source layers also accept direct color, icon, and size shortcuts. See the Python API reference for the conflict rules, projection forms, and restore behavior.
Monitors use explicit create/update/delete protocol operations. Scene checkpoint callbacks work with model data only: provide both checkpoint_capture and checkpoint_restore, and the binding infers the wire encoding. Pass restore=None to @scene_restore(...) for checkpoint-only models. Every connection starts with a simulator_info handshake containing stable model identity, per-instance identity, schema compatibility, and declared capabilities.
For Mesa models, prefer BoundModelReinitializer from tensnap or tensnap.bindings.lifecycle. Use bind_kwargs(...) to expose constructor keyword arguments as resettable parameters when needed. When a constructor kwarg and a model-owned parameter publish the same source field during register_model(), the model parameter keeps UI ownership and the reinitializer aliases its reset value from the model field instead of registering a duplicate kwarg parameter. configure_reinit(...) now applies the default Mesa cleanup automatically when no explicit cleanup callback is provided.
Projection ownership
Standalone built-in item projectors return independent nested snapshots. Eligible automatic layer scans can compare with previous owned snapshots and reuse equal read-only nested fields before materializing changed values; this does not guarantee outer-record identity. Custom callbacks keep safe publication copying and may explicitly declare independent output to avoid it. Layer options control projector and publication copying separately: disabling publication copying does not cancel an owning projector. Configure policies before use, and treat published records as read-only. See the shared ownership contract and focused benchmark commands for the language-specific options, overrides, and source-value rules.
Examples
Example simulations are located in the repository root:
examples/python/- Standard Python examples (flock, hk, sirs)examples/python_mesa/- Mesa-based examples (cgol, sugarscape, mushroom)
Documentation
Full documentation: https://github.com/billstark001/tensnap
License
See LICENSE file in the repository root.
IP family
See the networking guide for auto (default), ipv4, and ipv6 policies, IPv6 URLs, and shared ephemeral ports.
Metadata
Release files for tensnap 0.3.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tensnap-0.3.3.tar.gz | 137.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tensnap-0.3.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 254.6 kB
Release files / tensnap-0.3.3.tar.gz
| Download URL | tensnap-0.3.3.tar.gz |
|---|---|
| Size | 137.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5b7e2bb240a8f47c3cd33ad4cc00125bde8c36823ca1b991c1090db82cff64d0
|
|
BLAKE2b-256 checksum How to use checksums |
e3eaad3155316b368aa6916f706e394b2cbc6c9f5075ae2f620f4582a83e785f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 17, 2026.
Transparency logRelease files / tensnap-0.3.3-py3-none-any.whl
| Download URL | tensnap-0.3.3-py3-none-any.whl |
|---|---|
| Size | 117.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e697fb14c5dfa84cd3ecbf520cfeec1257e0c9b09ac182b04974315a94169495
|
|
BLAKE2b-256 checksum How to use checksums |
57dc807493a565381b2a62417360ce26d39076a061861dc96fe2addaeb950b0f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 17, 2026.
Transparency log