2. The module#
The root file (test/setu_class.zig is the model):
const setu = @import("setu"); const py = setu.py; pub const module: setu.Module = .{ .name = "liburing", // the import name: `liburing/__init__.so` (or `liburing.so`) .version = build_options.version, // `__version__` .from = &.{ .{ .ns = @import("ring.zig") }, // classes only: no `source` needed .{ .ns = @import("prep.zig"), .source = @embedFile("prep.zig") }, // functions need the source .{ .ns = @import("sqe.zig"), .functions = false }, // lowercase Zig helpers there .{ .ns = kernel, .prefixes = &.{ "IOSQE_", "IORING_OP_" } }, // constants by prefix .{ .ns = @import("enums.zig"), .enums = .int_enum }, // `enum.IntEnum` classes }, .init = init, // after Setu's part of `Py_mod_exec` }; /// The project's own setup: classes exist, constants are set. fn init(mod: *setu.c.Object) py.Error!void { _ = mod; } comptime { py.exportModule(module); }
setu.Module fields: name, doc (default: the //! doc of the first from entry with a source), version, from, init, stub_extra (lines the stub gets for what init adds by hand), methods (a hand-written c.MethodDef table; a name there replaces the scanned function of that name), exec (runs after init), gil (true: the free-threaded build enables the GIL on import), multiple_interpreters (default false: Setu keeps state in globals).
What a from entry (setu.From) takes from its ns: lowercase pub fns (functions; need .source), setu.Class declarations (Python name: the struct's), setu.Exception declarations (Python name: the pub const's), pub const E = enum (its UPPER_CASE fields as int constants, or the IntEnum E with .enums = .int_enum), UPPER_CASE pub const ints / floats / bools / strings. Skipped: _ names, camelCase functions, plain structs, lowercase values, pub vars. Filters take Python names: .only, .exclude (not together; each name must exist), .prefixes, .functions = false. Classes are created bases first; a Python name found twice is a compile error naming both places.
Pin a tag in the project's build.zig.zon: zig fetch --save=setu "git+https://github.com/YoSTEALTH/Setu#v<version>", the ref after a #; it prints resolved ref "v<version>" to commit ... and saves .url = "...?ref=v<version>#<commit>" with its .hash. Zig 0.17.0 ignores a ?ref= in what it is given: it fetches the default branch and saves that commit under the tag's name. Remove an old .path entry first (--save writes the URL into it, with no .hash). The saved commit should be the tag's: git ls-remote https://github.com/YoSTEALTH/Setu v<version>.
build.zig of the project (setu in its build.zig.zon):
const setu_dep = b.dependency("setu", .{ .target = target, .optimize = optimize }); ext.root_module.addImport("setu", setu_dep.module("setu")); const pyi = @import("setu").addStubs(b, setu_dep, .{ .root_source_file = b.path("src/liburing/root.zig"), .imports = &.{.{ .name = "build_options", .module = options.createModule() }}, // what the root imports }); b.getInstallStep().dependOn(&b.addInstallFileWithDir(pyi, .lib, "__init__.pyi").step); // next to __init__.so @import("setu").addSetuBuildSteps(b, setu_dep); // tool/setu_build.py: setu-build-sync / -check @import("setu").addSetuDocSteps(b, setu_dep); // tool/setu_doc.py: setu-doc-sync / -check, doc-web @import("setu").addSetuGuideSteps(b, setu_dep); // doc/setu-guide.md: setu-guide-sync / -check @import("setu").addCoverageSteps(b, setu_dep, ext, .{ // zig build coverage: kcov over pytest (see 11.) .module_name = "liburing", // the name the tests import the extension by .package = true, // it is `liburing/__init__.so` .include = &.{ b.path("src/liburing"), setu_dep.path("src") }, // the sources to report .stub = pyi, // for tests that read the .pyi next to the extension .python = b.option([]const u8, "python", "Python for `coverage`") orelse "python3", }); @import("setu").addProfileSteps(b, setu_dep, ext, .{ // zig build profile: callgrind over the cases (see 11.) .module_name = "liburing", .package = true, .cases = b.path("tool/bench_cases.py"), // `CASES` + `namespace(module)`, as Setu's tool/bench.py }); @import("setu").addSymbolCheck(b, setu_dep, ext, .{ // zig build check-symbol -Dpython=... (see 11.) .python = b.option([]const u8, "python", "Python for `check-symbol`") orelse "python3", });
The extension is the package itself, <name>/__init__.so next to __init__.pyi and py.typed (section 7), or a plain <name>.so (@import("setu").ext_suffix); the wheel cp314.cp315-abi3.abi3t.cp314t.cp315t (tool/setu_build.py, a PEP 517 backend each project keeps a byte-identical copy of).