pyzig

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.

3,147 stars

Best use case

pyzig is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

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.

Teams using pyzig should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/pyzig/SKILL.md --create-dirs "https://raw.githubusercontent.com/atopile/atopile/main/.claude/skills/pyzig/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/pyzig/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How pyzig Compares

Feature / AgentpyzigStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

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.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# Pyzig Module

`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

```bash
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 `PyTypeObject`s 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)

Related Skills

lsp

3147
from atopile/atopile

How the atopile Language Server works (pygls), how it builds per-document graphs for completion/hover/defs, and the invariants for keeping it fast and crash-proof.

solver

3147
from atopile/atopile

How the Faebryk parameter solver works (Sets/Literals, Parameters, Expressions), the core invariants enforced during mutation, and practical workflows for debugging and extending the solver. Use when implementing or modifying constraint solving, parameter bounds, or debugging expression simplification.

SEXP Benchmark Strategy

3147
from atopile/atopile

## Goal

sexp

3147
from atopile/atopile

How the Zig S-expression engine and typed KiCad models work, how they are exposed to Python (pyzig_sexp), and the invariants around parsing, formatting, and freeing. Use when working with KiCad file parsing, S-expression generation, or layout sync.

planning

3147
from atopile/atopile

Spec-driven planning for complex design tasks: when to plan, how to write specs as .ato files, and how to verify against requirements.

Package Agent

3147
from atopile/atopile

You are a package specialist.

library

3147
from atopile/atopile

How the Faebryk component library is structured, how `_F.py` is generated, and the conventions/invariants for adding new library modules. Use when adding or modifying library components, traits, or module definitions.

graph

3147
from atopile/atopile

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.

frontend

3147
from atopile/atopile

Frontend standards for atopile extension webviews: architecture, contracts, design system, and testing workflow.

faebryk

3147
from atopile/atopile

How Faebryk's TypeGraph works (GraphView + Zig edges), how to traverse/resolve references, and how FabLL types/traits map onto edge types. Use when working with TypeGraph traversal, edge types, or building type-aware queries.

fabll

3147
from atopile/atopile

How FabLL (faebryk.core.node) maps Python node/trait declarations into the TypeGraph + instance graph, including field/trait invariants and instantiation patterns. Use when defining new components or traits, working with the Node API, or understanding type registration.

domain-layer

3147
from atopile/atopile

Instructions for electronics-specific logic and build processes: netlists, PCBs, build steps, and exporters. Use when implementing or modifying build steps, exporters, PCB generation, or BOM/netlist output.