Skip to content

Getting Started

Pydust is currently designed to be embedded within a Python Poetry project. Reach out if you'd like help integrating Pydust with other build setups.

See also the generated Zig documentation.

GitHub Template

By far the easiest way to get started is by creating a project from our GitHub template: github.com/fulcrum-so/ziggy-pydust-template/

This template includes:

  • A Python Poetry project
  • A src/ directory containing a Pydust Python module
  • Pytest setup for running both Python and Zig unit tests.
  • GitHub Actions workflows for building and publishing the package.
  • VSCode settings for recommended extensions, debugger configurations, etc.

Poetry Setup

Assuming you have an existing Poetry project, these are the changes you need to make to your pyproject.toml to setup Ziggy Pydust. But first, add Pydust as a dev dependency:

poetry add -G dev ziggy-pydust
pyproject.toml
[tool.poetry]
name = "your-package"
packages = [ { include = "your-module" } ]
+ include = [ { path = "src/", format = "sdist" }, { path = "your-module/*.so", format = "wheel" } ]

+ [tool.poetry.build]
+ script = "build.py"

[build-system]
- requires = ["poetry-core"]
+ requires = ["poetry-core", "ziggy-pydust==TODO_SET_VERSION"]
build-backend = "poetry.core.masonry.api"

As well as creating the build.py for Poetry to invoke the Pydust build.

build.py
1
2
3
from pydust.build import build

build()

My First Module

Once Poetry is configured, add a Pydust module to your pyproject.toml and start writing some Zig!

pyproject.toml
1
2
3
[[tool.pydust.ext_module]]
name = "example.hello"
root = "src/hello.zig"
src/hello.zig
1
2
3
4
5
6
7
8
9
const py = @import("pydust");

pub fn hello() !py.PyString {
    return try py.PyString.create("Hello!");
}

comptime {
    py.rootmodule(@This());
}

Running poetry install will build your modules. After this, you will be able to import your module from within poetry shell or poetry run pytest.

test/test_hello.py
1
2
3
4
5
from example import hello


def test_hello():
    assert hello.hello() == "Hello!"

Zig Language Server

Warning

Currently ZLS (at least when running in VSCode) requires a small amount of manual setup.

In the root of your project, create a zls.build.json file containing the path to your python executable. This can be obtained by running poetry env info -e.

zls.build.json
1
2
3
4
5
6
7
8
{
    "build_options": [
        {
            "name": "python-exe",
            "value": "/path/to/your/poetry/venv/bin/python",
        }
    ]
}

Self-managed Mode

Pydust makes it easy to get started building a Zig extension for Python. But when your use-case becomes sufficiently complex, you may wish to have full control of your build.zig file.

By default, Pydust will generated two files:

  • pydust.build.zig - a Zig file used for bootstrapping Pydust and configuring Python modules.
  • build.zig - a valid Zig build configuration based on the tool.pydust.ext_module entries in your pyproject.toml.

In self-managed mode, Pydust will only generate the pydust.build.zig file and your are free to manage your own build.zig. To enable this mode, set the flag in your pyproject.toml and remove any ext_module entries.

pyproject.toml
1
2
3
4
5
6
[tool.pydust]
+ self_managed = true

- [[tool.pydust.ext_module]]
- name = "example.hello"
- root = "example/hello.zig"

You can then configure Python modules from a custom build.zig file:

build.zig
const std = @import("std");
const py = @import("./pydust.build.zig");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    // Each Python module consists of a library_step and a test_step
    const module = pydust.addPythonModule(.{
        .name = "example.hello",
        .root_source_file = .{ .path = "example/hello.zig" },
        .target = target,
        .optimize = optimize,
    });
    module.library_step.addModule(..., ...);
    module.test_step.addModule(..., ...);
}