graph
community[skill]
How the Zig-backed instance graph works (GraphView/NodeReference/EdgeReference), the real Python API surface, and the invariants around allocation, attributes, and cleanup. Use when working with low-level graph APIs, memory management, or building systems that traverse the instance graph.
$
/plugin install atopiledetails
Graph Module
The faebryk.core.graph module is a thin Python wrapper around the Zig graph implementation.
Source-of-truth for behavior is:
- Zig implementation:
src/faebryk/core/zig/src/graph/graph.zig - Python bindings:
src/faebryk/core/zig/src/python/graph/graph_py.zig - Public Python API surface (stubs):
src/faebryk/core/zig/gen/graph/graph.pyi
Quick Start
from faebryk.core.graph import GraphView
g = GraphView.create()
try:
_ = g.create_and_insert_node()
finally:
g.destroy()
Relevant Files
- Python wrapper/re-export:
src/faebryk/core/graph.py - Zig graph core:
src/faebryk/core/zig/src/graph/graph.zig - Zig → Python wrappers:
src/faebryk/core/zig/src/python/graph/graph_py.zig - Generated type stubs:
src/faebryk/core/zig/gen/graph/graph.pyi
Dependants (Call Sites)
src/faebryk/core/node.py(FabLL: nodes/traits are graph-backed)src/atopile/compiler/gentypegraph.py(compiler constructs typegraphs/instances via graph APIs)src/faebryk/core/graph_render.py(graph visualization)
How to Work With / Develop / Test
Mental Model
NodeReference/EdgeReference: value-like handles (UUIDs) into global backing storage in Zig.GraphView: a membership + adjacency view over those references (per-view arena + maps + bitsets).BoundNode/BoundEdge: “reference + owning GraphView pointer” wrappers used for traversal helpers.
Core Invariants (do not violate)
- No direct constructors:
GraphView(),NodeReference(),EdgeReference()are not meant to be called; use the exposed factory methods.GraphView.create()NodeReference.create(**attrs)EdgeReference.create(source=..., target=..., edge_type=..., **attrs)
- Explicit cleanup:
GraphView.create()allocates a Zig-side graph on the C allocator; it is freed only byGraphView.destroy().- Do not rely on Python GC to reclaim Zig allocations.
- Attribute limits: node/edge dynamic attributes are fixed-capacity in Zig (currently 6 entries). Exceeding this is a hard failure.
- Edge type width: edge types are
u8in Zig; treat them as0..255in Python (hashing/modulo happens on the Zig side). - Self node exists:
GraphView.initinserts aself_node; counts include it.
API Cheatsheet (matches src/faebryk/core/zig/gen/graph/graph.pyi)
from faebryk.core.graph import GraphView, Node, Edge
g = GraphView.create()
try:
n1 = g.create_and_insert_node() # -> BoundNode
n2 = Node.create(name="n2") # -> NodeReference (not inserted yet)
bn2 = g.insert_node(node=n2) # -> BoundNode
e = Edge.create(source=n1.node(), target=bn2.node(), edge_type=7, name="link")
_be = g.insert_edge(edge=e) # -> BoundEdge
finally:
g.destroy()
Debugging
GraphView.__repr__()printsGraphView(id=..., |V|=..., |E|=...)from Zig.- Graph wrapper has a stress test:
python -m faebryk.core.graph(runstest_graph_garbage_collection).
Development Workflow
- Zig changes: edit
src/faebryk/core/zig/src/graph/*. - Rebuild:
ato dev compile(importsfaebryk.core.zig, which compiles in editable installs). - If you add/remove exposed methods: update the wrapper in
src/faebryk/core/zig/src/python/graph/graph_py.zigand ensure stubs regenerate.
Testing
Key test entrypoints:
- Python:
python -m faebryk.core.graph - Zig:
zig test src/faebryk/core/zig/src/graph/graph.zig
technical
- github
- atopile/atopile
- stars
- 3177
- license
- MIT
- contributors
- 41
- last commit
- 2026-04-03T20:56:37Z
- file
- .claude/skills/graph/SKILL.md