ofplang run
A runner for Object-flow Programming Language v0 — a YAML-based dataflow workflow IR with linear Object tracking. The language is defined in the ofplang/spec repository.
The runner drives an ofplang v0 workflow to completion against an execution backend, emitting an execution status document (spec §6/§7) as it progresses and routing typed view values through the workflow. It runs on a simulator — a simulated physical backend — so a full workflow can be exercised end to end without real hardware; the same dispatch contract targets real hardware later.
Status: the simulator, the runner, and a typed (dummy) value layer are implemented.
- Simulator (
ofplang.run.simulator) — a physical backend: devices, spots, transporters, and timed operations advanced on a clock. It validates every dispatch (an inconsistent plan is rejected), models timed up/down for a device or a transporter and injected operation failure, and at completion produces each operation's output view values via a device model (with none injected, the built-inscript_device_modelruns ascriptprocess — see below — and otherwise falls back todefault_device_model, which fills type defaults and carries Object outputs through from theirobjects.mapinputs; a custom / real model computes them).Simulatoris an abstract base over the runner'sBackendcontract; the concreteVirtualTimeSimulatoradvances time instantly (deterministic; the default) andRealTimeSimulatorpaces it to a wall clock (a hardware-free stand-in for a real backend).- Runner (
ofplang.run.runner) — two ways to drive a backend:
replayruns a given execution plan (spec §6) on the backend verbatim.runis a rolling-horizon loop: it callsofplang.scheduleeach tick, dispatches the work that can start now, advances the clock, and polls — replanning from the committed history as it goes. It re-routes around a downed machine — a device (its process modes and, by default, its spots' transports) or a transporter (the transports it carries) — polls at a fixed interval with completion-time estimation, absorbs duration variance (an operation running longer or shorter than planned), and stops the whole run if any activity fails (marking the abandoned work cancelled). Machine up/down, operation failure, and duration variance are scenario concerns injected from Python (not CLI flags).- Value layer — the runner resolves each port's type and view schema (§7), routes typed view values along the workflow's arcs (producer output → consumer input, across nested composites), contract-checks them, and assembles the whole-workflow outputs. A caller supplies the whole-workflow I/O as a single run boundary (
--boundary): one document with a per-port{spot, view}descriptor —spotplaces a boundary Object (§6.8),viewsupplies an input value — for the workflow's entry inputs and final outputs. Unsupplied entry views default. A workflow-embedded static literal (bind: {port: {value: …}}, §11) is seeded as that consumer input's value in place of a default. At run end the produced output views are echoed back into a result boundary of the same schema (--boundary-out). Non-script values are typed but still dummy — a real device backend plugs into the same seam later.- Python script processes (spec §22,
python_script_processes) — an atomic Pure-Data process may carry ascript: {language: python, code: …}section. The built-in device model runs it: the input port values are bound as locals, the code returns a mapping of the declared outputs, and those become the operation's computed output values — the first genuine (non-dummy) computation in the value layer. A script that raises, returns the wrong output names, or returns a non-conformant value fails runtime verification (§22.2) and stops the run gracefully like any other activity failure (the failed activity is markedfailed, its unstarted successorscancelled, exit 1). The script runs inline in real time but advances no simulation time; its outputs appear when the operation completes at itsend, and the environment mode'sdurationis the scheduler's estimate of the compute cost. Scripts run with full Python builtins and no import restriction (§22.3 permits an implementation to restrict these; this one does not).- Contracts (spec §9) — an atomic process may declare
contractswithrequires(preconditions overinputs.*.view) andensures(postconditions overinputs.*.viewandoutputs.*.view). The runner evaluates them at runtime against the actual view values:requiresbefore the operation runs (a violation stops it before dispatch),ensuresafter it completes. An atomicrequiresthat references only run/graph-phase inputs (§5.6) is knowable at run start, so it is checked there as a preflight — before any work is dispatched — rather than waiting for that (possibly late) operation. A violation — or a runtime evaluation error — stops the run gracefully like any activity failure (failed+ downstreamcancelled, exit 1). Static / graph-time contract checking (a fully-constant contract that is statically false, type errors, bad references) isofplang-validate's job and is not repeated here. Contracts are checked on atomic processes and on the top-level entry composite (main): the entry composite'srequiresis evaluated at run start over the whole-workflow inputs (a violation stops the run before any work runs) and itsensuresat run end over the whole-workflow inputs and outputs (a violation marks the completed run failed). Nested composite contracts are checked too, at each composite invocation's value boundary: itsrequiresonce its inputs are available (for an input fed by an upstream process this is mid-run, before the composite's body runs) and itsensuresonce its outputs are, a violation stopping the run gracefully at the composite boundary (its not-yet-run body cancelled). Because non-script process outputs are still typed defaults,ensuresbites mainly on script processes and boundary-supplied inputs until a real device backend computes physical outputs.- Failure observability — when a run stops (an injected activity failure, a script error, or a contract violation), the reason is exposed as a structured
RollingRunner.failure(a machine-readablekindcode, e.g.contract_requires/script_error, plus a human-readabledetail, thesubject, and the time), and the CLI prints it to stderr. The status document itself stays a valid §6 document (the reason is out of band). An optionalcontract_observercallback is invoked for every contract check (held or violated) — a trace hook for debugging.- Static view values (spec §7.4) — a type whose view field declares a
value:fixes that field to a constant for every value of the type. The runner projects it onto every view record it routes, so a Python script reading the field and a contract referencing it both see the static value (not a runtime default). A value that carries a conflicting one is forced to the static value.
Install
pip install ofplang-run
Requires Python 3.10+. Runtime dependencies (pulled in automatically) are PyYAML,
the sibling ofplang-schedule that
run (rolling-horizon) calls each tick, and
ofplang-validate used by the CLI
front door. The runner library never imports validate (and the replans
never re-validate), so it stays a one-shot CLI front door.
For development, install editable with the test extra from a clone:
pip install -e ".[test]"
Command line
ofp-run run <workflow> --env <env>
[--boundary DOC] [--boundary-out FILE] [--observation-out FILE]
[--poll-interval D] [--margin M] [--seed N] [--no-validate] [-o OUT]
ofp-run replay <plan> --env <env> [-o OUT]
run drives a v0 workflow to completion by replanning as it goes: each tick it
polls the backend and, when anything the scheduler reads has changed -- an operation
finished, a machine went down, a pending activity came due -- renders the committed
history as a status, calls the scheduler in-process -- handing it the workflow, the
environment and that status as documents, so nothing goes through a temporary file --
and dispatches the newly-runnable work. A
tick that changed none of those keeps the plan it already has, so a long protocol
costs one solve per activity event rather than one per unit of its makespan; what is
observed, and so the status produced, is the same either way. --boundary supplies the whole-workflow I/O as one document —
a boundary: mapping with a {spot, view} descriptor per entry input / final
output port. spot places a boundary Object on an environment spot (spec §6.8;
Object ports only); view supplies an input's view value (unsupplied entry views
default). The runner projects it into the scheduler's interface (spots only, so the
scheduler stays value-independent) and the seeded input values. --boundary-out
writes the result boundary — the same schema with each produced output's view
filled in — a run-local artifact, separate from the value-free status document. On
completion each pinned Object output is checked to have reached its declared spot.
--observation-out streams the observation document (see docs/OBSERVATION.md):
a YAML multi-document stream recording each completed activity's concrete input /
output view values (a transport's moved view), appended as each activity finishes —
the value-layer companion to the status document, also run-local.
--poll-interval sets the fixed polling interval (default 1). --max-ticks is the
non-termination guard: a run that takes more than that many ticks is given up on. One tick
is one poll interval, so the guard also caps the makespan a run can reach (the default
100000 with the default interval means 100000 time units); pass 0 for no limit when a
long virtual run needs it, which also gives up the protection against a backend whose clock
does not advance. --margin sets the
running-task margin: on each replan a still-running activity is pinned to end at
max(reported end, now + margin), so a positive margin is what keeps an
overrunning operation's successor from being planned at now and dispatched onto
a value that operation has not produced yet. The default is 0, which is only safe
with a backend whose operations cannot finish later than planned (the in-process
virtual-time simulator); against a wall-clock or real backend set it to at least
the poll interval, or a successor is refused with input_not_produced rather than
computing on a typed default. --no-validate
skips the one-shot ofplang-validate front-door check of the workflow — use it
when the workflow was already validated upstream (e.g. by the ofp umbrella CLI);
$import is still resolved and the capability gate still runs, since both are
structural rather than validation. replay runs a plan
produced by ofp-schedule verbatim on the simulator (no value layer). Both write
the final execution status as YAML (-o, else stdout). Exit codes: 0 success,
1 execution failed (an activity failed, or a replan is infeasible), 2
usage/input error.
This tool is also intended to be exposed as the run subcommand of the umbrella
ofp CLI (a separate repository in the ofplang organization).
The package lives under the ofplang PEP 420 namespace (ofplang.run), shared
across the organization's tools.
Tests
pytest
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ofplang_run-0.1.13.tar.gz.
File metadata
- Download URL: ofplang_run-0.1.13.tar.gz
- Upload date:
- Size: 196.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
23444be826174df8dbd4731a02091e6908ba939022c0e2d316be2fc02eb38a4d
|
|
| MD5 |
afa24ccc1c51814ae161d121f5033216
|
|
| BLAKE2b-256 |
3a023ccf2b376e3d08a69be96c107a327b44c17b8740f6300191cf9b4a0db3ae
|
Provenance
The following attestation bundles were made for ofplang_run-0.1.13.tar.gz:
Publisher:
publish.yml on ofplang/run
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ofplang_run-0.1.13.tar.gz -
Subject digest:
23444be826174df8dbd4731a02091e6908ba939022c0e2d316be2fc02eb38a4d - Sigstore transparency entry: 2514887812
- Sigstore integration time:
-
Permalink:
ofplang/run@5ba060c68c9ba967b6417a5db5f9a5db7666bdfb -
Branch / Tag:
refs/tags/v0.1.13 - Owner: https://github.com/ofplang
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5ba060c68c9ba967b6417a5db5f9a5db7666bdfb -
Trigger Event:
push
-
Statement type:
File details
Details for the file ofplang_run-0.1.13-py3-none-any.whl.
File metadata
- Download URL: ofplang_run-0.1.13-py3-none-any.whl
- Upload date:
- Size: 108.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e91511027a856f1c6a6aa6cd891b7cbc221db73c9ca98fdaed2aab1f303e14e
|
|
| MD5 |
e5a0096b74159b230d13370ee1381f12
|
|
| BLAKE2b-256 |
65128900ed38671eb7b1cf19096e3f439f5a20ac35894810fc78f24dd63f0dd1
|
Provenance
The following attestation bundles were made for ofplang_run-0.1.13-py3-none-any.whl:
Publisher:
publish.yml on ofplang/run
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ofplang_run-0.1.13-py3-none-any.whl -
Subject digest:
2e91511027a856f1c6a6aa6cd891b7cbc221db73c9ca98fdaed2aab1f303e14e - Sigstore transparency entry: 2514888115
- Sigstore integration time:
-
Permalink:
ofplang/run@5ba060c68c9ba967b6417a5db5f9a5db7666bdfb -
Branch / Tag:
refs/tags/v0.1.13 - Owner: https://github.com/ofplang
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5ba060c68c9ba967b6417a5db5f9a5db7666bdfb -
Trigger Event:
push
-
Statement type: