A high-performance framework for writing Python extension modules in Zig with automatic memory management, hot-reload, and NumPy integration.
π Inspired by ziggy-pydust
Documentation: https://github.com/amiyamandal-dev/pyz3
Source Code: https://github.com/amiyamandal-dev/pyz3
pyz3 is a complete framework for building high-performance Python extension modules in Zig. It provides:
- π Seamless Python-Zig Interop - Automatic argument marshalling and type conversion
- π NumPy Integration - Call NumPy functions from Zig via Python object interface
- π§ Complete CLI Toolkit - Maturin-style commands for project lifecycle management
- π¦ Cross-Platform Builds - Build wheels for Linux, macOS, and Windows
- π C/C++ Integration - Automatic binding generation for C/C++ libraries
- π§ͺ Testing Integration - Pytest plugin to discover and run Zig tests
- β‘ Hot Reload - Watch mode with automatic rebuilding
- π‘οΈ Memory Safe - Leverages Zig's safety features with Python's GC
const py = @import("pyz3");
pub fn fibonacci(args: struct { n: u64 }) u64 {
if (args.n < 2) return args.n;
var sum: u64 = 0;
var last: u64 = 0;
var curr: u64 = 1;
for (1..args.n) |_| {
sum = last + curr;
last = curr;
curr = sum;
}
return sum;
}
comptime {
py.rootmodule(@This());
}import mymodule
print(mymodule.fibonacci(10)) # Output: 55NumPy can be used from Zig code via the Python object interface:
const py = @import("pyz3");
const root = @This();
/// Create and manipulate NumPy arrays from Zig
pub fn array_stats() !py.PyObject {
const np = try py.numpy.getModule(@This());
defer np.decref();
// Create arange(1, 11)
const arange_method = try np.getAttribute("arange");
defer arange_method.decref();
const arr = try py.call(root, py.PyObject, arange_method, .{ 1, 11 }, .{});
defer arr.decref();
// Get mean
const mean_method = try arr.getAttribute("mean");
defer mean_method.decref();
return try py.call(root, py.PyObject, mean_method, .{}, .{});
}
comptime {
py.rootmodule(root);
}import mymodule
result = mymodule.array_stats()
print(result) # Output: 5.5Note: Direct zero-copy array access via
PyArrayis not yet implemented. Arrays are currently accessed through Python method calls.
- Zig: 0.15.x (tested with 0.15.1)
- Python: 3.11, 3.12, 3.13 (CPython)
- Platforms: Linux (x86_64, aarch64), macOS (x86_64, arm64), Windows (x64)
- pyz3 Version: 0.9.3
pip install pyz3Or with distribution extras for building wheels:
pip install pyz3[dist]# Create project using cookiecutter template
pyz3 init -n myproject --description "My awesome extension" --email "you@example.com" --no-interactive
cd myproject# Development build
zig build
# Release build
zig build -Doptimize=ReleaseFast
# Watch mode (hot reload)
pyz3 watch# Run pytest
pytest
# Run specific test
pytest test/test_myproject.py -v# Build wheel for current platform
python -m build --wheel
# Build for all platforms (uses cross-compilation)
pyz3 build-wheel --all-platforms
# Publish to PyPI
pyz3 deploy --repository testpypi # Test first!
pyz3 deploy --repository pypi # Productionpyz3 provides a complete CLI for managing your extension projects:
pyz3 init [OPTIONS] # Initialize new project
pyz3 build [OPTIONS] # Build extension module
pyz3 watch # Watch mode with hot reload
pyz3 test [OPTIONS] # Run tests
pyz3 clean # Clean build artifacts
pyz3 build-wheel [OPTIONS] # Build distribution packages
pyz3 deploy [OPTIONS] # Publish to PyPIAutomatic conversion between Python and Zig types:
| Zig Type | Python Type |
|---|---|
void |
None |
bool |
bool |
i32, i64 |
int |
f32, f64 |
float |
[]const u8 |
str |
struct {...} |
dict |
py.PyObject |
numpy.ndarray (via py.numpy.getModule) |
pub const Point = py.class(struct {
pub const __doc__ = "A 2D point";
const Self = @This();
x: f64,
y: f64,
pub fn __init__(self: *Self, args: struct { x: f64, y: f64 }) !void {
self.* = .{ .x = args.x, .y = args.y };
}
pub fn distance(self: *const Self) f64 {
return @sqrt(self.x * self.x + self.y * self.y);
}
});pub fn divide(args: struct { a: i64, b: i64 }) !i64 {
if (args.b == 0) {
return py.ZeroDivisionError(root).raise("division by zero");
}
return @divTrunc(args.a, args.b);
}Build wheels for multiple platforms:
# Using environment variables
ZIG_TARGET=x86_64-linux-gnu PYZ3_OPTIMIZE=ReleaseFast python -m build --wheel
ZIG_TARGET=aarch64-linux-gnu PYZ3_OPTIMIZE=ReleaseFast python -m build --wheel
ZIG_TARGET=x86_64-macos PYZ3_OPTIMIZE=ReleaseFast python -m build --wheel
ZIG_TARGET=aarch64-macos PYZ3_OPTIMIZE=ReleaseFast python -m build --wheel
ZIG_TARGET=x86_64-windows-gnu PYZ3_OPTIMIZE=ReleaseFast python -m build --wheelThe build system automatically:
- Detects target platform
- Cross-compiles for different architectures
- Creates manylinux-compatible wheels
- Handles platform-specific optimizations
pyz3 leverages Zig's performance advantages:
- Zero-cost abstractions - No runtime overhead
- Compile-time optimizations - Zig's comptime for metaprogramming
- SIMD support - Automatic vectorization where possible
- Small binaries - Smaller than equivalent Rust extensions
- Fast compilation - Faster than Rust, comparable to C
This project is a hard fork of ziggy-pydust by Fulcrum.
Major differences in pyz3:
- β NumPy integration via Python object interface (zero-copy PyArray planned)
- β Enhanced cross-compilation support
- β Updated CLI commands and workflows
- β NumPy examples and tests
- β Improved documentation for data science use cases
Special thanks to the original ziggy-pydust contributors for creating an excellent foundation!
Apache License 2.0
Contributions are welcome! Please feel free to submit a Pull Request.
- Original Project: ziggy-pydust
- Zig Language: ziglang.org
- NumPy: numpy.org