This repository implements a small functional-execution engine on top of a graph. The core loop walks a graph of Unit (node) objects via Connection (edge) objects using a Stepper. Read this first to be productive:
-
Source layout
src/hyperway/graph/graph.pyandsrc/hyperway/graph/base.py: Graph storage and helpers. Graph is a thin defaultdict of connections keyed by edge id and node-a id.src/hyperway/edges.py: Connection and PartialConnection implementations plusmake_edge. A Connection binds A → [through?] → B and supportspluck()for A→(through)→B in one call.src/hyperway/nodes.py: Unit (node) wrapper and helpers (as_unit,as_units,is_unit). Unit.process applies sentinel rules; Nodes.process is a raw pass-through.src/hyperway/stepper.py: StepperC, the execution engine.StepperC.call_one_*drives A, [wire]→B, merging, and branch-end stashing.src/hyperway/packer.py:argspack/ArgsPackfor moving (args, kwargs) between nodes and wires.src/hyperway/writer.pyanddocs/*: graphviz rendering and docs.src/hyperway/tools.py: small factory/util functions used in examples and tests.
-
Big-picture data flow
- Build edges:
g.add(a, b, through=optional_wire)org.connect(a, b, c, ...)(chains pairs). Callables are wrapped viaas_unit(...)automatically. - Execute: create a stepper and step:
g.stepper_prepare(start_unit, *args, **kw)thens = g.stepper(); s.step()repeatedly, or- For a single edge,
connection.pluck(*a, **kw)runs A then [through] then B.
- Stepper rows contain next-caller and an
ArgsPack. Ends are stashed instepper.stash(unlessstash_ends=False). - Merge nodes: set
unit.merge_node=Trueandstepper.concat_aware=Trueto combine multiple incoming rows into one call (seerow_concat).
- Build edges:
-
Project conventions and patterns
- Left-associative execution: edges run strictly A → [through] → B; no algebraic precedence (see README “Order of Operation”).
- All inter-node values move via
ArgsPack; wire functions must returnargspack(...). - Node uniqueness:
Unit(func)objects are unique per wrap; reuse a specific Unit by pre-wrapping withas_unitand reusing that instance. - Sentinel handling:
Unit.processtreats a single positional arg equal tounit.sentinalas “no value” and strips it; useas_unit(func, sentinal=None)to suppress passing None to no-arg functions (see tests/test_sentinal.py). Nodessubclass bypasses sentinel logic and forwards args as-is.
-
Typical developer workflows
- Tests: run fast with pytest
- Quick run:
./quick_test.sh(uses venv at.venv) - Or from repo root:
pytest -qorpytest -v --cov
- Quick run:
- Examples and playground:
workspace/contains many runnable examples and graphviz renders. - Docs: see
README.md(rich overview) anddocs/(stepper.md,topology.md). Rendering graphs requiresgraphviz(seedocs/graphviz-install.md).
- Tests: run fast with pytest
-
Key APIs and examples
- Build a connection and pluck
from hyperway.tools import factory as f; from hyperway.edges import make_edgec = make_edge(f.add_1, f.add_2);assert c.pluck(1) == 4- With wire transform:
c = make_edge(f.add_1, f.add_2, through=lambda v,*a,**kw: argspack(v*2, **kw))
- Graph and stepper
from hyperway.graph import Graph;g = Graph()chain = g.connect(f.add_10, f.add_20, f.add_30);first = chain[0].ag.stepper_prepare(first, 10);s = g.stepper();rows = s.step(); continue stepping until rows empty; results appear ins.stash.
- Merge-node example
u = as_unit(print); u.merge_node = True;s = g.stepper(); s.concat_aware = True
- Build a connection and pluck
-
Gotchas to keep in mind
- Wire functions must return an
ArgsPackviaargspack; returning raw values will break downstream. Connection.__call__runs only A; usepluck()to run A → [through] → B.- When there are no outgoing connections from a Unit,
StepperC.end_branchstashes the result;leaf()onUnithandles this case. - Some modules print debug traces (e.g., resolve/connection calls). Keep outputs minimal in tests.
- Wire functions must return an
-
Where to look when extending
- New node behaviors: subclass
Unit(seeNodes) and overrideprocess/leafas needed. - Alternate graph strategies: extend
GraphBaseor composition aroundGraph. - Stepper policies: adjust
row_concat,concat_aware, or replaceexpandviaset_global_expand.
- New node behaviors: subclass
-
Runbook
- Install dev deps: ensure
pytestavailable (declared inpyproject.tomlas optionaldev). - Run tests:
./quick_test.shorpytest -q. - Render graph images: ensure
graphvizinstalled, then callg.write("name", directory="renders", direction="LR").
- Install dev deps: ensure
Use the concrete patterns in tests/ for style and integration expectations (e.g., tests/test_wire_func.py, tests/test_stepper.py, tests/test_sentinal.py, tests/test_nodes.py). Prefer adding small, focused tests that assert behavior via pluck() and stepper flows.
Run tests using the ./quick_test.sh script to ensure all functionality remains intact.