Pyzig Module
SkillDev toolsGuides your agent in writing and fixing Zig-Python bindings, including wrapper generation and ownership rules.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Pyzig Module skill
About this capability
How the Zig↔Python binding layer works (pyzig), including build-on-import, wrapper generation patterns, ownership rules, and where to add new exported APIs. Use when adding Zig-Python bindings, modifying native extensions, or debugging C-API interactions.
What this skill tells your AI
The instructions your AI receives, as published by atopile/atopile in .claude/skills/pyzig/SKILL.md and read by ahel’s review.
pyzig is the Zig↔Python interoperability layer used by Faebryk’s native modules (graph, sexp, faebryk typegraph, …).
There are three distinct layers to keep straight:
- Python loader/glue:
src/faebryk/core/zig/__init__.py(build-on-import +.pyisyncing) - Zig build:
src/faebryk/core/zig/build.zig(buildspyzig.so+pyzig_sexp.so, generates stubs) - Zig binding utilities:
src/faebryk/core/zig/src/pyzig/*(wrapper generation + minimal C-API surface)
Quick Start
ato dev compile
python -c "import faebryk.core.zig; import faebryk.core.graph"
Relevant Files
- Python-side loader/build glue:
src/faebryk/core/zig/__init__.py(ZIG_NORECOMPILE,ZIG_RELEASEMODE, lock, stub syncing)
- Zig build + stub generation:
src/faebryk/core/zig/build.zig(builds extensions + runs.pyigenerator)
- Core pyzig utilities:
src/faebryk/core/zig/src/pyzig/pybindings.zig(minimal CPython C-API declarations)src/faebryk/core/zig/src/pyzig/pyzig.zig(wrapper generation helpers)src/faebryk/core/zig/src/pyzig/type_registry.zig(global type-object registry)src/faebryk/core/zig/src/pyzig/pyi.zig(stub generation helpers)
- Example consumers:
src/faebryk/core/zig/src/python/graph/graph_py.zigsrc/faebryk/core/zig/src/python/sexp/sexp_py.zig
Dependants (Call Sites)
- Graph bindings:
src/faebryk/core/zig/src/python/graph/* - Sexp bindings:
src/faebryk/core/zig/src/python/sexp/* - TypeGraph bindings:
src/faebryk/core/zig/src/python/faebryk/*(and friends)
How to Work With / Develop / Test
Core Concepts
- Direct binding: pyzig calls the CPython C-API directly (no cffi/ctypes).
- Wrapper types: most exposed Zig structs become Python heap types via
wrap_in_python(...)/wrap_in_python_simple(...). - Global type registry: prevents re-creating Python
PyTypeObjects for the same Zig type (type_registry). - No direct
__init__(by default): many “reference” types are not meant to be user-constructed;pyzigoften installs an init that raises. - Debug handle: generated wrappers include
__zig_address__()to help debug pointer identity.
Development Workflow
- Edit Zig:
- binding helpers:
src/faebryk/core/zig/src/pyzig/* - module wrappers:
src/faebryk/core/zig/src/python/**
- binding helpers:
- Rebuild native modules:
ato dev compile(importsfaebryk.core.zig; editable installs compile-on-import)- set
ZIG_RELEASEMODE=ReleaseFast|ReleaseSafe|Debugas needed
- If you changed stubs/output:
- ensure
src/faebryk/core/zig/gen/**gets updated (this is driven bysrc/faebryk/core/zig/__init__.py)
- ensure
Testing
- Smoke tests are usually through downstream modules:
python -m faebryk.core.graph(GraphView allocation/cleanup stress)ato dev test --llm test/core/solver(heavy user of graph + bindings via many subsystems)
Best Practices
- Assume mistakes segfault: treat changes here like unsafe systems programming.
- Be explicit about ownership:
- if a wrapper allocates Zig memory, define how it is freed (explicit
.destroy()vstp_dealloccalling.deinit()). - if you duplicate input buffers (sexp does), expose a
free(...)path and document it.
- if a wrapper allocates Zig memory, define how it is freed (explicit
- Don’t rely on Python GC for Zig arenas unless you intentionally installed a
tp_deallocthat callsdeinit. - Stub hygiene matters: keep the
.pyisurface accurate; many callers rely on types for navigation.
Build-on-import behavior (important)
src/faebryk/core/zig/__init__.py is responsible for:
- compiling extensions in editable installs (unless
ZIG_NORECOMPILE=1) - loading
pyzig.soandpyzig_sexp.sofromsrc/faebryk/core/zig/zig-out/lib/ - copying + formatting generated
.pyifiles intosrc/faebryk/core/zig/gen/**(black + ruff)
Signals
- GitHub stars
- 4k
- Forks
- 234
- Last commit
- Jun 2026
Advanced
- Catalog kind
- skill
- Gateway key
pyzig- Source
- github.com/atopile/atopile