Build and publish Zig-powered Python extensions with native cross-compilation
Project description
zig-maturin
Build and publish Zig-powered Python extensions — pip install, no compiler on the user's side.
zig-maturin is Maturin + PyO3 for Zig: a high-level Zig library (pyo3zig) for writing Python extensions, plus a CLI that compiles them into ready-to-install wheels.
import my_extension
my_extension.add(2, 3) # -> 5
my_extension.greet("world") # -> "Hello, world!"
The extension author needs Zig. The end user just pip installs a wheel — no Zig, no compiler, nothing to build.
Why Zig
| Rust + Maturin | Zig + zig-maturin | |
|---|---|---|
| Cross-compilation | needs Docker / extra toolchains | built-in: --target aarch64-macos works out of the box |
| Toolchain size | gigabytes | a few megabytes |
| Honest manylinux | via auditwheel |
glibc pinned at build (gnu.2.28) → the tag is true |
| C-API access | unsafe extern |
native C interop |
Performance
Summing 0..1_000_000 in a tight loop, Zig (releasing the GIL) vs pure Python
(examples/bench.py, CPython 3.13, x86_64):
| Implementation | Time per call | Speedup |
|---|---|---|
Zig (pz.allowThreads) |
3.3 ms | — |
Pure Python sum(range(n)) |
15.6 ms | 4.8× |
Pure Python for loop |
45.9 ms | 14× |
Numbers vary by machine; run python examples/bench.py after building the demo.
Install
pip install zig-maturin # the build tool (pure Python)
No system toolchain is required: if zig is not on PATH, the build pulls in the
ziglang wheel (a pinned Zig binary)
automatically and compiles through it. If you already have
Zig 0.16 on PATH, that is used instead (no
download).
Quick start
zig-maturin scaffold my_extension
cd my_extension
zig-maturin develop # build + install into the current venv
python -c "import my_extension; print(my_extension.hello())"
zig-maturin build # produce a wheel in dist/
Scaffolded projects use the PEP 517 backend
(build-backend = "zig_maturin.buildapi"), so the standard tools work too —
pip install ., pip wheel ., python -m build. If no zig is on PATH the
backend adds ziglang as a build dependency automatically, so this works with
no system toolchain:
pip install . # builds + installs the extension
pip install -e . # editable (PEP 660); re-run to recompile
python -m build # wheel + sdist in dist/
What scaffold generates
zig-maturin scaffold my_extension writes a complete, buildable project and
wires up the Zig dependency for you (it runs zig fetch --save if zig is on
PATH):
my_extension/
├── pyproject.toml # [tool.zig-maturin] config + PEP 517 backend
├── build.zig # the Zig build script (links pyo3zig + the C shim)
├── build.zig.zon # dependency manifest (zig-maturin pinned with a hash)
└── src/main.zig # your extension — edit this
You normally only touch src/main.zig. The generated build.zig already wires
in the two modules and the C shim — you rarely need to change it:
const zm_dep = b.dependency("zig-maturin", .{ .target = target, .optimize = optimize });
const mod = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "zig-maturin", .module = zm_dep.module("zig-maturin") }, // low-level C-API
.{ .name = "pyo3zig", .module = zm_dep.module("pyo3zig") }, // high-level layer
},
});
const lib = b.addLibrary(.{ .name = "my_extension", .linkage = .dynamic, .root_module = mod });
lib.root_module.link_libc = true;
lib.root_module.addCSourceFile(.{ .file = zm_dep.path("pyo3zig_capi.c"), .flags = &.{} });
// CPython symbols resolve against the interpreter at import time:
lib.linker_allow_shlib_undefined = true;
b.installArtifact(lib);
Adding pyo3zig to an existing Zig project (instead of scaffolding): run
zig fetch --save=zig-maturin git+https://github.com/rroblf01/zig-maturin, then
add the two .imports and the addCSourceFile/link_libc/
linker_allow_shlib_undefined lines above to your build.zig.
For a feature-by-feature tour of a real, compiled extension, see the
examples walkthrough (the source is
pyo3zig_example.zig, built by zig build and exercised
by the test suite).
Mixed Python/Zig packages
Ship pure-Python code alongside the native module: put a package at
<python-source>/<module-name>/ (e.g. src/my_extension/__init__.py). The wheel
then bundles those .py files and nests the compiled extension inside the
package as my_extension/my_extension.<so>, which __init__.py re-exports:
# src/my_extension/__init__.py
from .my_extension import * # the Zig extension
from .helpers import wrapper # pure-Python companion code
Without such a package, the extension is installed as a top-level module.so.
One wheel for all CPython versions (abi3)
Set abi3 in [tool.zig-maturin] to build against the stable ABI and ship a
single cp312-abi3-<platform> wheel that works on that CPython and every later
version:
[tool.zig-maturin]
abi3 = "3.12"
Or, without touching pyproject.toml, pass it on the command line:
zig-maturin build --abi3 3.12
The framework already uses out-of-line refcounting and accessor functions (not
inline macros), so abi3 adds ~no runtime overhead here. Managed __dict__ and
weakref need the GC pre-header and are unavailable under abi3 (cyclic GC of
?*pz.PyObject fields still works).
Free-threading (no-GIL, PEP 703) — not yet
Free-threaded interpreters (python3.13t / python3.14t) are not supported
yet. The C shim already opts in (Py_MOD_GIL_NOT_USED) and is refcount-clean,
but a free-threaded build has a wider PyObject header, so the Zig-side module
and instance layouts need a free-threaded-specific definition before the
extension can load there. Regular (with-GIL) CPython is fully supported.
Sub-interpreters
Modules use multi-phase init (PEP 489) and declare support for sub-interpreters,
so the extension can be imported into more than one interpreter (which
single-phase modules cannot). Type objects and the cached datetime / awaitable
/ exception objects are keyed per interpreter — each interpreter gets its own, so
nothing is shared across the interpreter boundary. Classes, operators,
inheritance, container conversions, datetime and custom exceptions all work in
a sub-interpreter (see tests/test_subinterp.py).
Shared-GIL ("legacy") sub-interpreters are fully supported. Per-interpreter-GIL (truly parallel interpreters) is not yet declared — the per-interpreter caches rely on the shared GIL for serialization.
Stability
1.0.0 is the first stable release. The public Zig API (pyo3zig, imported as
pz) and the zig_maturin build tooling follow semantic versioning: breaking
changes wait for a major bump.
Writing an extension
A module is declared with pyModule and exported with exportModule:
const std = @import("std");
const pz = @import("pyo3zig");
// Turn a Zig panic into a Python exception instead of crashing the interpreter.
pub const panic = pz.panic;
fn add(a: i64, b: i64) i64 {
return a + b;
}
fn greet(name: []const u8) !pz.PyString {
var buf: [256]u8 = undefined;
return pz.PyString.init(try std.fmt.bufPrint(&buf, "Hello, {s}!", .{name}));
}
const Mod = pz.pyModule("my_extension", .{
.doc = "An extension written in Zig.",
.functions = &.{
pz.pyFnNamed("add", add),
pz.pyFnNamed("greet", greet),
},
});
comptime {
pz.exportModule(Mod);
}
Plain Zig functions are wrapped automatically: argument count, type conversion,
and error handling are all derived from the signature. Returning !T makes a
Zig error surface as a Python exception.
Type conversions
| Zig | Python (argument) | Python (return) |
|---|---|---|
i8..i64, u8..u64 |
int |
int |
f32, f64 |
float |
float |
std.math.Complex(f64/f32) |
complex (int/float coerced) |
complex |
bool |
bool |
bool |
enum |
int (validated; bad value → ValueError) |
int |
pz.DateTime |
datetime.datetime |
datetime.datetime |
[]const u8 |
str / bytes / bytearray / os.PathLike (borrowed) |
str |
?T |
T or None |
T or None |
[]T, [N]T |
list / tuple |
list |
std.StringHashMap(V), std.AutoHashMap(K,V) |
dict |
dict |
pz.PySet, pz.PyFrozenSet |
— | set / frozenset |
| tuple struct | list / tuple |
tuple |
| plain struct | dict (by field name; field defaults honored) |
dict (by field name) |
*MyClass |
an instance of MyClass (borrowed) |
— |
?*pz.PyObject |
any object | any object (passthrough) |
Conversion is bidirectional: a list/tuple becomes a []T argument, a dict
becomes a struct argument, and an instance of one of your classes can be passed
to a function as a *MyClass pointer. A type mismatch raises a precise
TypeError (expected int, got str).
Keyword arguments and defaults
Zig reflection doesn't expose parameter names, so declare them explicitly:
fn power(base: i64, exp: i64) i64 { ... }
pz.pyFnKw("power", power, .{
.args = &.{ "base", "exp" },
.defaults = .{ .exp = @as(i64, 2) }, // optional, by name
});
power(3) # 9 (exp defaults to 2)
power(2, 10) # 1024
power(base=5, exp=3) # 125
Classes
A Zig extern struct becomes a Python class. Fields are exposed as attributes;
declare optional dunder methods directly on the struct:
const Greeter = extern struct {
val: i64,
pub fn init(v: i64) Greeter {
return .{ .val = v };
}
pub fn __str__(self: *Greeter) !pz.PyString { ... }
pub fn __hash__(self: *Greeter) i64 { return self.val; }
pub fn __eq__(self: *Greeter, other: *Greeter) bool { return self.val == other.val; }
pub fn __deinit__(self: *Greeter) void { ... } // called on GC
};
fn greet_method(self: *Greeter) !pz.PyString { ... }
const GreeterClass = pz.PyClass(Greeter, .{
.methods = &.{ pz.wrapMethodNamed(Greeter, "greet", greet_method) },
.readonly = &.{"val"}, // expose `val` read-only
});
Register the class in the module's .classes field.
Hooks (declared on the struct):
- Lifecycle / repr:
init,__deinit__(called on GC),__str__,__repr__,__hash__,__format__(format()/ f-string specs),__call__(callable instances),__await__(awaitable;await objresolves to its return value) or__await_delegate__(self) ?*PyObject(delegate to a real awaitable and suspend to the event loop),__aiter__/__anext__(async for x in obj;__anext__(self) -> ?T),__enter__/__exit__(context manager,with obj:),__reduce__(pickle),__getstate__/__setstate__,__bytes__(bytes(obj)),__floor__/__ceil__/__trunc__(math.*),__round__,__copy__/__deepcopy__(copy.copy/copy.deepcopy, returning a fresh instance),__fspath__(os.PathLike),__length_hint__. - Comparisons:
__eq__(and__ne__derived from it), plus__lt__,__le__,__gt__,__ge__— define any subset (just__lt__enablessorted()). Defining__eq__without__hash__makes instances unhashable, as in Python. - Container / iterator protocols:
__len__,__getitem__,__setitem__,__delitem__,__contains__,__iter__,__next__,__reversed__(a type with__next__is its own iterator;__getitem__normalizes negative indices when__len__is present). - Operators:
__add__,__sub__,__mul__,__truediv__,__floordiv__,__mod__,__pow__,__matmul__,__divmod__, bitwise__and__/__or__/__xor__/__lshift__/__rshift__, unary__neg__/__pos__/__abs__/__invert__/__bool__, the matching in-place forms (__iadd__,__imul__,__iand__,__ilshift__, … — every operator has one), and conversions__int__/__float__/__index__. A binary op's second parameter can be the same type (*Self) or a scalar (e.g.i64forvec * 2); define__radd__/__rsub__/__rmul__for the reflected form (2 * vec). Operands that don't match yieldNotImplemented. A result of typeSelfis wrapped into a new instance. - Attributes:
__getattr__(self, name)(consulted only when normal lookup fails) and__setattr__(self, name, value)(intercepts every assignment). - Descriptors:
__get__(self, obj, objtype),__set__(self, obj, value),__delete__(self, obj)— use an instance as a managed attribute on a class. - Subclass hook:
__init_subclass__(cls)— fires when a Python subclass is created (clsis the new subclass). - Buffer:
__buffer__(self) []const u8(read-only) or__buffer_mut__(self) []u8(writable) exposes a zero-copy view tomemoryview/bytes/numpy.
Every class is also subscriptable for type hints — MyClass[int] yields a
types.GenericAlias (auto __class_getitem__).
Cyclic GC: a class storing a ?*pz.PyObject field automatically gets
Py_TPFLAGS_HAVE_GC with tp_traverse/tp_clear, so reference cycles are
collectable. The framework owns one reference per field (incref on set, decref
on clear/dealloc); don't manually decref those fields in __deinit__. These GC
classes also support weakref.ref(obj) and a managed __dict__ (arbitrary
Python attributes, GC-traced) — both cleared on dealloc. Value classes (no
PyObject field) have neither, keeping instances minimal.
Subclassing from Python: every class is Py_TPFLAGS_BASETYPE, so Python code
can class Sub(MyClass): ... and inherit fields, methods, and operators —
including classes with __deinit__ or cyclic GC. Teardown runs in tp_finalize,
so CPython's subtype_dealloc correctly tears down a subclass's __dict__,
weakrefs, and GC; __deinit__ fires for subclass instances and cycles through
them stay collectable. Wider integers i65..i128/u65..u128 are supported as
arguments and return values (round-tripped through CPython's bigint).
PyClass config:
const Vec2Class = pz.PyClass(Vec2, .{
.doc = "A 2D vector.", // class docstring (help(Vec2))
// keyword __init__ with defaults
.init_args = &.{ "x", "y" },
.init_defaults = .{ .y = @as(i64, 0) },
// keyword __call__ with defaults (when the class defines __call__)
.call_args = &.{ "k" },
.call_defaults = .{ .k = @as(i64, 1) },
// computed (read-only) properties
.properties = &.{ .{ .name = "length_sq", .get = vec2_length_sq } },
.methods = &.{
pz.wrapMethodNamed(Vec2, "dot", vec2_dot), // positional method
pz.wrapMethodKw(Vec2, "scale", scale, .{ .args = &.{"k"} }), // kwargs method
pz.staticMethod("dims", vec2_dims), // @staticmethod
pz.classMethod(Vec2, "from_pair", vec2_from_pair), // @classmethod / alt constructor
},
});
A classMethod whose Zig function returns T (or !T) is treated as an
alternative constructor: the returned struct is wrapped into a fresh
instance, so Vec2.from_pair(...) returns a Vec2.
Enums: expose a Zig enum as a real Python enum.IntEnum with
pz.enumClass(MyEnum, "Name"), then list it in .classes alongside your
PyClass types — each variant becomes a member (Color.RED == 0). (A plain
enum used as a function argument or return value converts as an int.)
Releasing the GIL
Wrap a long pure-Zig computation in pz.allowThreads to release the GIL while
it runs, so other Python threads make progress:
fn heavy_sum(n: i64) i64 {
return pz.allowThreads(compute_sum, .{n});
}
Typed exceptions
Raise a specific built-in (or custom) Python exception, then return any Zig error — the framework preserves the one you set instead of remapping it:
fn parse_positive(x: i64) !i64 {
if (x <= 0) {
pz.setError(pz.PyExc_ValueError(), "value must be positive");
return error.NotPositive;
}
return x;
}
For a reusable custom exception, declare a pz.exceptionClass, register it in
.classes, and raise it from Zig:
const MyError = pz.exceptionClass("mymod.MyError", pz.PyExc_ValueError);
// .classes = &.{ ..., MyError }
fn check(n: i64) !i64 {
if (n < 0) { MyError.raise("must be non-negative"); return error.Bad; }
return n;
}
MyError becomes mymod.MyError (a ValueError subclass) on the Python side.
Pass null as the base for a plain Exception. (pz.newException(...) remains
for one-off types not registered as a class.)
Inheritance
A Zig class can inherit from another Zig class with .base. The derived struct
must embed the base's struct as its first field; the base's methods and
field accessors are then inherited and isinstance/issubclass work:
const Animal = extern struct {
legs: i64,
pub fn init(legs: i64) Animal { return .{ .legs = legs }; }
};
const AnimalClass = pz.PyClass(Animal, .{ .init_args = &.{"legs"}, ... });
const Dog = extern struct {
base: Animal, // the base, embedded first
good: bool,
pub fn init(legs: i64, good: bool) Dog {
return .{ .base = .{ .legs = legs }, .good = good };
}
};
const DogClass = pz.PyClass(Dog, .{ .base = AnimalClass, .init_args = &.{ "legs", "good" }, ... });
d = Dog(4, True)
isinstance(d, Animal) # True; d.legs and inherited methods work
class Puppy(Dog): ... # Python can subclass the derived class too
Register the base in .classes before the derived class.
Computed properties
Expose getter/setter attributes (not backed by a struct field) with
.properties:
const Vec2Class = pz.PyClass(Vec2, .{
.properties = &.{
.{ .name = "length_sq", .get = vec2_length_sq }, // read-only
.{ .name = "x", .get = vec2_get_x, .set = vec2_set_x }, // read-write
},
});
Variadic functions (*args / **kwargs)
pz.pyFnRaw registers a function that receives the raw argument tuple and
keyword dict:
fn sum_all(args: ?*pz.PyObject, kwargs: ?*pz.PyObject) i64 {
var total: i64 = 0;
var i: isize = 0;
while (i < pz.PyTuple_Size(args)) : (i += 1)
total += pz.PyLong_AsLongLong(pz.PyTuple_GetItem(args, i));
return total;
}
// pz.pyFnRaw("sum_all", sum_all)
Module constants
const Mod = pz.pyModule("my_extension", .{
.constants = .{ .VERSION = "1.0", .MAX_ITEMS = @as(i64, 100) },
.functions = &.{ ... },
.classes = &.{ GreeterClass },
});
Submodules
Nest modules with .submodules. Each child is set as an attribute of the parent
and registered in sys.modules under its dotted name, so both attribute access
and import parent.child work:
const MathxMod = pz.pyModule("mathx", .{
.constants = .{ .E = @as(f64, 2.71828) },
.functions = &.{ pz.pyFnNamed("triple", triple) },
});
const Mod = pz.pyModule("my_extension", .{
.submodules = .{ MathxMod },
.functions = &.{ ... },
});
import my_extension
my_extension.mathx.triple(4) # attribute access
from my_extension.mathx import triple # dotted import
Error handling and panics
- A Zig error (
!T) becomes a Python exception (mapped by kind:error.Overflow→OverflowError, etc.). - A Zig panic (out-of-bounds,
@panic, integer overflow in safe builds) would normally abort the whole interpreter. Opt into the safety net withpub const panic = pz.panic;and it becomes aRuntimeErrorinstead — the interpreter stays alive. (Caveat: the recovery skipsdefers between the panic site and the call boundary, so that window leaks.)
Type stubs (.pyi)
Type hints are generated at compile time from your Zig signatures:
const STUB = pz.moduleStub(.{
.{ .name = "add", .func = add, .args = &.{ "a", "b" } },
.{ .name = "greet", .func = greet, .args = &.{"name"} },
}) ++ "\n" ++ pz.classStub(.{
.name = "Greeter", .type = Greeter, .init = &.{"v"},
.methods = .{ .{ .name = "greet", .func = greet_method } },
});
fn __pyi__() []const u8 { return STUB; }
// register pz.pyFnNamed("__pyi__", __pyi__)
classStub emits a class block (struct fields as attributes, __init__,
methods, and .properties as @property accessors). For variadic functions set
.raw = true on the moduleStub entry (rendered as *args, **kwargs), and for
custom exceptions use pz.exceptionStub("MyError", "ValueError"). complex,
datetime, optionals and containers map to their Python spellings automatically.
zig-maturin build calls __pyi__() on native builds and ships the resulting
my_extension.pyi inside the wheel, so type checkers see your signatures.
CLI
| Command | Description |
|---|---|
zig-maturin scaffold <name> |
Create a new project (pyproject, build.zig, src/main.zig). |
zig-maturin develop |
Build and install into the current environment. |
zig-maturin build |
Build a wheel in dist/. |
zig-maturin sdist |
Build a source distribution. |
zig-maturin generate-ci |
Write a GitHub Actions workflow that builds + publishes wheels. |
build / develop options: --target <triple> (repeatable), --release,
--out <dir>, --abi3 <X.Y> (build a stable-ABI wheel), and for
cross-compilation --python-include / --python-libdir / --python-lib.
Cross-compilation
Zig cross-compiles out of the box. Linux glibc targets are pinned so the manylinux tag is honest:
zig-maturin build --target x86_64-linux-gnu --target aarch64-macos
# -> manylinux_2_28_x86_64, macosx_11_0_arm64
| Zig target | Wheel tag |
|---|---|
x86_64-linux-gnu (→ gnu.2.28) |
manylinux_2_28_x86_64 |
aarch64-linux-gnu |
manylinux_2_28_aarch64 |
x86_64-linux-musl |
musllinux_1_2_x86_64 |
aarch64-macos / x86_64-macos |
macosx_11_0_arm64 / _x86_64 |
x86_64-windows / aarch64-windows |
win_amd64 / win_arm64 |
Cross-compiling needs the target Python's headers (and, on Windows, its
pythonXY.lib); supply them via --python-include / --python-libdir /
--python-lib or [tool.zig-maturin]. Native builds detect them via
sysconfig automatically.
Configuration
[tool.zig-maturin]
module-name = "my_extension" # default: project name
zig-source = "src/main.zig"
# cross-compilation overrides (optional):
# python-include = "..."
# python-libdir = "..."
# python-lib = "python312"
Troubleshooting
| Symptom | Cause / fix |
|---|---|
error: expected ... found on zig build, or unknown builtins |
Wrong Zig version. zig-maturin targets Zig 0.16; check zig version, or just don't install Zig and let the build pull in the pinned ziglang wheel. |
zig fetch failed during scaffold |
No zig on PATH at scaffold time. Run the printed command later: zig fetch --save=zig-maturin git+https://github.com/rroblf01/zig-maturin. The build itself still works toolchain-free. |
python3-config not found / Python.h missing |
Install the Python dev headers (python3-dev / python3-devel), or use the official python.org build. For cross-compilation the host can't detect the target's headers — pass --python-include (and on Windows --python-libdir/--python-lib). |
ImportError: undefined symbol: Py... at import |
Almost always an ABI mismatch: the wheel was built for a different Python. Rebuild against the interpreter you're importing into, or build an abi3 = "3.12" wheel for forward compatibility. |
ModuleNotFoundError after zig build |
zig build drops the .so in zig-out/lib/lib<name>.so; Python needs it as <name>.so on sys.path. Use zig-maturin develop (installs it correctly) instead of importing from zig-out. |
| Interpreter aborts on a Zig panic | Add pub const panic = pz.panic; to turn panics into RuntimeError (see Error handling and panics). |
Requirements
- Python 3.12+ (free-threaded
3.13t/3.14tnot supported yet) - Zig 0.16 — on PATH, or pulled in automatically as the
ziglangwheel - Linux, macOS, or Windows
License
MIT — Ricardo Robles Fernández
Related
- Maturin, PyO3 — the Rust originals
- zig-python — Python C-API bindings for Zig
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zig_maturin-1.0.1.tar.gz.
File metadata
- Download URL: zig_maturin-1.0.1.tar.gz
- Upload date:
- Size: 24.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ebd7887f0c8592db8a0b4e2bd19281f9bbfb3b3d03a992faa0c8945840e133b
|
|
| MD5 |
370e7ede7f3468d08e739db8e28a05b1
|
|
| BLAKE2b-256 |
a259989849f44426e8d2d255d77b32547469e2a2df9a7ddf57593a58520ee0bf
|
Provenance
The following attestation bundles were made for zig_maturin-1.0.1.tar.gz:
Publisher:
release.yml on rroblf01/zig-maturin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zig_maturin-1.0.1.tar.gz -
Subject digest:
9ebd7887f0c8592db8a0b4e2bd19281f9bbfb3b3d03a992faa0c8945840e133b - Sigstore transparency entry: 1799558498
- Sigstore integration time:
-
Permalink:
rroblf01/zig-maturin@8e6071fe5982497a9c9c05fc26584c60eda5b919 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/rroblf01
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8e6071fe5982497a9c9c05fc26584c60eda5b919 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zig_maturin-1.0.1-py3-none-any.whl.
File metadata
- Download URL: zig_maturin-1.0.1-py3-none-any.whl
- Upload date:
- Size: 28.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e030869a177444591f689fc74ef388a0f40f9224fa28d43df1615f8c4ff43fef
|
|
| MD5 |
4d38b5ec165ca2c005c03878e7360ad0
|
|
| BLAKE2b-256 |
0dc6001097cf0c0633d84bf9cc0a056a200f67161d1d0c0d1849f68c1bbe2324
|
Provenance
The following attestation bundles were made for zig_maturin-1.0.1-py3-none-any.whl:
Publisher:
release.yml on rroblf01/zig-maturin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zig_maturin-1.0.1-py3-none-any.whl -
Subject digest:
e030869a177444591f689fc74ef388a0f40f9224fa28d43df1615f8c4ff43fef - Sigstore transparency entry: 1799558711
- Sigstore integration time:
-
Permalink:
rroblf01/zig-maturin@8e6071fe5982497a9c9c05fc26584c60eda5b919 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/rroblf01
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8e6071fe5982497a9c9c05fc26584c60eda5b919 -
Trigger Event:
push
-
Statement type: