Pyzig Module

SkillDev tools

Guides 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.

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 + .pyi syncing)
  • Zig build: src/faebryk/core/zig/build.zig (builds pyzig.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 .pyi generator)
  • 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.zig
    • src/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; pyzig often installs an init that raises.
  • Debug handle: generated wrappers include __zig_address__() to help debug pointer identity.

Development Workflow

  1. Edit Zig:
    • binding helpers: src/faebryk/core/zig/src/pyzig/*
    • module wrappers: src/faebryk/core/zig/src/python/**
  2. Rebuild native modules:
    • ato dev compile (imports faebryk.core.zig; editable installs compile-on-import)
    • set ZIG_RELEASEMODE=ReleaseFast|ReleaseSafe|Debug as needed
  3. If you changed stubs/output:
    • ensure src/faebryk/core/zig/gen/** gets updated (this is driven by src/faebryk/core/zig/__init__.py)

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() vs tp_dealloc calling .deinit()).
    • if you duplicate input buffers (sexp does), expose a free(...) path and document it.
  • Don’t rely on Python GC for Zig arenas unless you intentionally installed a tp_dealloc that calls deinit.
  • Stub hygiene matters: keep the .pyi surface 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.so and pyzig_sexp.so from src/faebryk/core/zig/zig-out/lib/
  • copying + formatting generated .pyi files into src/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