Compile and import Zig functions at runtime without building a package
Project description
import-zig
pip install import-zig
import_zig provides the simplest possible interface for compiling and importing Zig code into Python, to enable easy experimentation and prototyping.
Additionally, the compile_to function outputs a compiled Python extension module for the target interpreter, which can be integrated into a larger Python package. Here is a setup.py of a Python package using compile_to in this way.
One fun thing about creating Python extensions with Zig is that you don't need to install or set up any compiler toolchain to use or build a module. The entire Zig compiler is a simple dependency on the ziglang Python package, which is the only dependency of import-zig.
Requirements
- Python 3.10 or newer.
- The package installs and invokes
ziglang==0.15.1. - Python development headers must be available so Zig can compile against the Python C API. On Linux this usually means installing
python3-devorpython3-devel.
Quick start
from import_zig import import_zig
mod = import_zig(source_code="""
pub fn Q_rsqrt(number: f32) f32 {
const threehalfs: f32 = 1.5;
const x2 = number * 0.5;
var y = number;
var i: i32 = @bitCast(y);
i = 0x5f3759df - (i >> 1);
y = @bitCast(i);
y = y * (threehalfs - (x2 * y * y));
return y;
}
""")
print(f"1 / sqrt(1.234) = {mod.Q_rsqrt(1.234)}")
Choosing an input mode
Exactly one of source_code, file, or directory must be passed to import_zig() or compile_to().
source_code: compile an inline Zig source string.file: compile a single.zigfile.directory: compile a multi-file Zig project. This argument is a mapping with:path: directory containing your Zig sources.root_source_file: entry-point Zig file inside that directory.
Example using a directory:
from import_zig import import_zig
module = import_zig(
directory={
"path": "multiple_files",
"root_source_file": "import_fns.zig",
},
)
Development workflow with prepare
prepare() writes the Zig build scaffolding into an existing directory so you can work on a Zig project with editor support and compile it later with compile_prepared().
import import_zig
import_zig.prepare(
"/path/to/project_folder",
"module_name",
"root_source_file.zig",
)
This generates the following files alongside your own Zig source tree:
project_folder
├── root_source_file.zig
├── build.zig
├── build.zig.zon
└── zig_ext
├── c.h
├── generated.zig
├── .gitignore
├── py_utils.zig
└── zig_ext.zig
root_source_file.zig is your entry point and is never overwritten by prepare(). build.zig, build.zig.zon, and zig_ext/ are regenerated to match the current Python environment. On non-Windows platforms they are symlinked by default; on Windows, or when force_copy=True, they are copied instead.
This setup enables ZLS support for @import("c") and @import("py") inside your Zig sources.
Compiling to a reusable extension
Use compile_to() when you want a compiled extension file in a target directory instead of importing it immediately:
from import_zig import compile_to
compile_to(
target_dir=".",
module_name="my_module",
file="single_file.zig",
)
The produced binary is platform-specific and will use Python's extension suffix for the current interpreter.
Use compile_prepared(target_dir, cwd) after a prepare() workflow. cwd must point at the prepared Zig project directory.
Build options and dependencies
The optimize argument is accepted by import_zig(), compile_to(), and compile_prepared(). Supported values are:
Optimize.DebugOptimize.ReleaseSafeOptimize.ReleaseFastOptimize.ReleaseSmall
imports can be passed to prepare(), import_zig(), or compile_to() to populate build.zig.zon dependencies for Zig packages. Each dependency entry is written directly into the generated .dependencies section. If an import spec contains a path entry, it is rewritten relative to the prepared build directory.
Type mapping and Python interop
The conversion rules live in zig_ext/py_utils.zig and are applied based on the parameter and return types of exported pub fn functions. Nested conversions are handled recursively.
| Conversion from Python | Zig datatype | Conversion to Python |
|---|---|---|
int |
integer (any size / sign) | int |
float |
float (any size) | float |
bool(value) semantics |
bool |
bool |
| sequence | array | list |
| sequence | non-u8 slice |
list |
str |
[]const u8 |
str |
| dict or sequence | struct |
struct-like Python object, or tuple for Zig tuple structs |
None |
optional | null becomes None |
| not applicable | void |
None |
Additional interop behavior:
- If a function accepts
std.mem.Allocator, an arena allocator is provided for the duration of the call. This lets the Zig code allocate return data such as string slices whose contents are converted to a Python string before the arena is deinitialized. - Raw Python objects can be handled directly with
*c.PyObjectafter importingcorpy. - Zig errors are forwarded to Python exceptions. Returning
error.SomeNamebecomes a Python exception, andpy_utils.zigalso exposes helpers for raising Python exceptions explicitly.
Python C API helpers
Inside compiled Zig code you can use:
const c = @import("c");
const pyu = @import("py");
const py = pyu.py;
This gives access to the Python C API and the helper utilities bundled in py_utils.zig, including conversion helpers, exception helpers, and support used by the Arrow example in examples/arrow_example.zig.
Examples
See examples/basic_usage.ipynb for the main API surface, examples/prepare_development.ipynb for the prepared-project workflow, and examples/arrow_example.zig plus examples/arrow_example.ipynb for lower-level Python C API interop.
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 import_zig-0.15.3.tar.gz.
File metadata
- Download URL: import_zig-0.15.3.tar.gz
- Upload date:
- Size: 15.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fd10b65bbce6675e2cdd734a72ffd663ea6ed629edbf70dbd606b213bd6d588a
|
|
| MD5 |
47100dd6848a88d2f77ac0a5d2fa66b7
|
|
| BLAKE2b-256 |
332a8cfad6ee85f5b3f134202607d64e99b9acd5ab36ed1db95dc892c920735b
|
File details
Details for the file import_zig-0.15.3-py3-none-any.whl.
File metadata
- Download URL: import_zig-0.15.3-py3-none-any.whl
- Upload date:
- Size: 14.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f8c791c42b9e7a451f375755f89a99b30d5a9a81414c47bdb545f8a2cf998f7
|
|
| MD5 |
ad336701708d2225274ae7a1576f39b1
|
|
| BLAKE2b-256 |
7c162df9d744b6f437ab4efe43d9fb4291346f97a32929ab9a6e374a8c82cfee
|