PyOZ v0.12.0
What's New in v0.12.0
Added
- Automatic
PyInit_export — zero boilerplate module initialization —pyoz.module()now auto-exports thePyInit_<name>function via@exportin a comptime block. The build system generates a bridge module that forces Zig's lazy analysis, so the user only needspub const Module = pyoz.module(.{ .name = "mymod", ... });— no manualpub export fn PyInit_mymodneeded. Works with both standard and--packagelayouts. The module const must bepub. Existing projects with manualPyInit_exports should remove them to avoid duplicate symbol errors. .fromauto-scan API — New module config field.from = &.{ @import("my_funcs.zig") }that auto-discovers and registers public declarations from Zig namespaces. Eliminates repetitivepyoz.func()/pyoz.class()/pyoz.constant()boilerplate when the Python name matches the Zig identifier. Supports functions, classes, enums, constants, and exceptions. Docstrings are provided via{name}__doc__convention, or automatically extracted from///doc comments when usingpyoz.withSource(). Works withpyoz.source()for filtering (.only/.exclude) andpyoz.sub()for submodules.pyoz.source(namespace, .{ .only = &.{"a", "b"} })/.exclude— Filter which declarations from a.fromnamespace are exported. Use.onlyto whitelist or.excludeto blacklist specific names.pyoz.sub("name", namespace)submodule support — Declare submodules from.fromnamespaces. Functions, constants, classes, and enums in the submodule namespace are registered undermodule.name.pyoz.Exception(base, doc)andpyoz.ErrorMap()markers — Declare custom exceptions and error-to-exception mappings inside.fromnamespaces..fromauto-detectspyoz.Args(T)for keyword arguments — Functions usingpyoz.Args(T)in.fromnamespaces are automatically wrapped with named kwargs support, identical to explicitkwfuncregistration..fromdeduplication — Explicit config entries (.funcs,.classes, etc.) always take priority over.from-scanned declarations with the same name. Duplicate names across multiple.fromentries produce a compile error with guidance to usepyoz.source()filtering..fromstub generation —.pyistubs are automatically generated for all.from-scanned declarations, including functions, classes, enums, and constants.__text_signature__support forhelp()andinspect.signature()— All functions (module-level and class methods) now embed a CPython Argument Clinic-style signature inml_doc.help(func)shows proper parameter names instead ofadd(...). Keyword argument functions usingpyoz.Args(T)show field names and defaults (e.g.safe_sqrt(value, default=None)). Class methods correctly useself/$typeconventions. WhenwithSourceis used for a.fromnamespace, real Zig parameter names are used (e.g.count_words(s, /)instead ofcount_words(arg0, /)); without source, positional args fall back toarg0/arg1.pyoz.withSource(@import("f.zig"), @embedFile("f.zig"))— comptime source introspection — New wrapper for.fromentries that enables automatic extraction of///doc comments as Python docstrings,//!module-level doc comments asmodule.__doc__, real function parameter names for__text_signature__and.pyistubs, and///comments above structs as class__doc__. No boilerplate needed in the.fromfile — just wrap the@importat the module config site. Usesstd.zig.Tokenizerat comptime — source text is NOT embedded in the final binary. Explicit{name}__doc__and{name}__params__constants still take priority for backward compatibility. A legacy per-file__source__function/constant is also supported.
⚠️ Breaking Changes — Migration from 0.11.x
🔧 build.zig must be updated. The build system now uses a bridge module pattern to auto-export PyInit_. Projects created with pyoz init on 0.11.x need their build.zig replaced. The easiest way is to re-run pyoz init --path in your project directory (backs up existing files), or manually update build.zig to match the new template — see the generated build.zig for the current template.
🗑️ Remove manual PyInit_ exports. If your lib.zig contains pub export fn PyInit_mymod, delete it. The auto-export now handles this. Keeping it causes a exported symbol collision compile error.
🔓 Make the module const pub. Change const MyMod = pyoz.module(.{...}); to pub const MyMod = pyoz.module(.{...});. The bridge module needs to see it to trigger the auto-export.
🔄 kwfunc now requires pyoz.Args(T). The old kwfunc that accepted functions with ?T optional parameters is removed. Wrap your kwargs in a struct:
// Before (0.11.x)
fn greet(name: ?[]const u8, times: ?i32) []const u8 { ... }
pyoz.kwfunc("greet", greet, "Greet someone")
// After (0.12.0)
fn greet(args: pyoz.Args(struct { name: ?[]const u8 = null, times: ?i32 = null })) []const u8 {
const name = args.value.name orelse "World";
...
}
pyoz.kwfunc("greet", greet, "Greet someone")Changed
kwfuncrenamed fromkwfunc_named— The oldkwfunc(which generated unusablearg0/arg1kwarg names due to Zig's@typeInfonot exposing parameter names) is removed.kwfunc_namedis renamed tokwfuncand is now the only way to register keyword argument functions. All kwargs functions must usepyoz.Args(T)for real parameter names.- Removed broken
?Tkwargs auto-detection — Functions with optional?Tparameters are no longer auto-detected as keyword argument functions (in both.fromand explicit registration). The?Tdetection generated unusablearg0/arg1names. Usepyoz.Args(T)instead for proper named kwargs support.
Fixed
.fromenums with unsigned integer tags (enum(u8),enum(u16), etc.) registered as StrEnum instead of IntEnum — TheisIntEnumdetection infrom.zigused a flawed heuristic (checking signedness + exhaustiveness) that only recognized signed tags likeenum(i32). Unsigned explicit tags likeenum(u8)fell through and were incorrectly registered as StrEnum, causing.valueto return string names instead of integer values. Fixed by matching the proven logic fromenums.zig— checking against standard integer types (u8,u16,u32,u64,i8,i16,i32,i64,isize,c_int,c_long), which correctly distinguishes user-specified tags from Zig's auto-generated non-standard bit-width tags (u1,u2,u3, ...).- Build-time
PyInit_symbol validation —pyoz build/pyoz devnow validates that the compiled.so/.pydexports the expectedPyInit_<module_name>symbol after building. Catches mismatches betweenmodule-nameinpyproject.tomland the Zig export function, printing a clear warning with the exact fix needed. Prevents the confusingImportError: dynamic module does not define module export functionat runtime. - Stub
.pyifilename usesmodule-nameinstead of projectname— Whenmodule-namediffers from the project name (e.g.,module-name = "_liburing"withname = "liburing"), the stub file was incorrectly namedliburing.pyiinstead of_liburing.pyi. Now usesconfig.getModuleName()so the stub filename matches the actual.so/.pyd. py-packagesnow supports Python src-layout —py-packages = ["mypkg"]now searchessrc/mypkg/first (PEP 517 src-layout) before falling back tomypkg/(flat layout). Previously only flat layout was supported, causingWarning: Python package directory not foundfor src-layout projects and missing__init__.pyin wheels.- Stub generation crash on non-
Args(T)kwfunc parameters —@hasDeclinstubs.zigwas called on non-struct types (e.g.,?[]const u8,?bool) when generating stubs for functions with optional parameters, causing a comptime error. Now checks@typeInfo(...) == .@"struct"before calling@hasDecl. - PyPI wheel:
_pyoz.soplaced insidepyoz/package — The native extension_pyoz.sowas previously installed at the top level ofsite-packages/, polluting the namespace. Now placed insidepyoz/_pyoz.sowith a relative import (from ._pyoz import ...), keeping the package self-contained. - PyPI
pyozCLI updated topyoz.Args(T)for 0.12.0 compatibility — Thepypi/src/lib.zigCLI wrapper functions used raw?Toptional parameters withkwfunc, which is no longer supported in 0.12.0. Updated to usepyoz.Args(T)structs. Also added bridge module topypi/build.zigfor auto-export.
Installation
Download the binary for your platform and add it to your PATH:
| Platform | Binary |
|---|---|
| Linux x86_64 | pyoz-x86_64-linux |
| Linux ARM64 | pyoz-aarch64-linux |
| macOS x86_64 | pyoz-x86_64-macos |
| macOS ARM64 (Apple Silicon) | pyoz-aarch64-macos |
| Windows x86_64 | pyoz-x86_64-windows.exe |
| Windows ARM64 | pyoz-aarch64-windows.exe |
Source
Download PyOZ-0.12.0.tar.gz for the source code.
Quick Start
pyoz init mymodule
cd mymodule
pyoz build
pip install dist/*.whl