Skip to main content

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-dev or python3-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 .zig file.
  • 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.Debug
  • Optimize.ReleaseSafe
  • Optimize.ReleaseFast
  • Optimize.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.PyObject after importing c or py.
  • Zig errors are forwarded to Python exceptions. Returning error.SomeName becomes a Python exception, and py_utils.zig also 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.

Metadata

Release files for import-zig 0.16.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for import-zig 0.16.0
File Size Uploaded
import_zig-0.16.0.tar.gz 15.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for import-zig 0.16.0
File Interpreter ABI Platform
import_zig-0.16.0-py3-none-any.whl Python 3 none any Details

Total release size: 30.7 kB

Release files / import_zig-0.16.0.tar.gz

Download URL import_zig-0.16.0.tar.gz
Size 15.9 kB
Tags Source
SHA-256 checksum
How to use checksums
ea2fd8437d8f8f362ef53ea46dfdf67ca1eecf15b312087dd192bd7e85b86f9f
BLAKE2b-256 checksum
How to use checksums
daee7e93d199a66daab70d53e0d0475bd04e880bcb18e88fc5d2369ed7ad7835
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.3

Release files / import_zig-0.16.0-py3-none-any.whl

Download URL import_zig-0.16.0-py3-none-any.whl
Size 14.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7faf10b58944e6c25e294f0a56ecf1acd6e82e550982a200dbacf6fe9620eff5
BLAKE2b-256 checksum
How to use checksums
c63b84b639b8121517cc888bfa17129038bd1c39a1103e410e49873be11bffb0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.3

Release history Release notifications | RSS feed

This release

0.16.0 This release

2 release files

0.15.3

2 release files

0.15.2

2 release files

0.14.0

2 release files

0.13.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page