Skip to content

Submodules

PyOZ supports creating nested module structures, allowing you to organize your API hierarchically (e.g., mymodule.math.sqrt()).

Creating Submodules

Use .module_init to add submodules during module initialization:

fn setupSubmodules(module: *pyoz.PyObject) callconv(.c) c_int {
    const mod = pyoz.Module{ .ptr = module };
    _ = mod.createSubmodule("math", "Math utilities", &math_methods) catch return -1;
    _ = mod.createSubmodule("io", "I/O utilities", &io_methods) catch return -1;
    return 0;
}

pub const MyModule = pyoz.module(.{
    .name = "mymodule",
    .module_init = &setupSubmodules,
    // ...
});

Defining Submodule Methods

Create a method array with pyoz.methodDef() and terminate with pyoz.methodDefSentinel():

fn math_sqrt(x: f64) !f64 {
    if (x < 0) return error.NegativeValue;
    return @sqrt(x);
}

fn math_pow(base: f64, exp: f64) f64 {
    return std.math.pow(f64, base, exp);
}

var math_methods = [_]pyoz.PyMethodDef{
    pyoz.methodDef("sqrt", &pyoz.wrapFunc(math_sqrt), "Square root"),
    pyoz.methodDef("pow", &pyoz.wrapFunc(math_pow), "Power"),
    pyoz.methodDefSentinel(),  // Required terminator
};

API Reference

Function Description
pyoz.methodDef(name, fn, doc) Create method definition
pyoz.methodDefSentinel() Required null terminator
pyoz.wrapFunc(fn) Wrap Zig function for submodule use
mod.createSubmodule(name, doc, methods) Create and attach submodule

Usage in Python

import mymodule
from mymodule import math

# Via parent module
mymodule.math.sqrt(16)  # 4.0

# Direct import
math.pow(2, 10)         # 1024.0

Complete Example

const std = @import("std");
const pyoz = @import("PyOZ");

// Main module
fn version() []const u8 { return "1.0.0"; }

// Math submodule functions
fn math_sqrt(x: f64) !f64 {
    if (x < 0) return error.NegativeValue;
    return @sqrt(x);
}

var math_methods = [_]pyoz.PyMethodDef{
    pyoz.methodDef("sqrt", &pyoz.wrapFunc(math_sqrt), "Square root"),
    pyoz.methodDefSentinel(),
};

fn setupSubmodules(module: *pyoz.PyObject) callconv(.c) c_int {
    const mod = pyoz.Module{ .ptr = module };
    _ = mod.createSubmodule("math", "Math functions", &math_methods) catch return -1;
    return 0;
}

pub const MyLib = pyoz.module(.{
    .name = "mylib",
    .module_init = &setupSubmodules,
    .funcs = &.{ pyoz.func("version", version, "Get version") },
});

Auto-Scan Alternative

If you're using the .from API, you can create submodules declaratively with pyoz.sub() instead of writing manual init code:

const string_utils = @import("string_utils.zig");
const io_utils = @import("io_utils.zig");

pub const Example = pyoz.module(.{
    .name = "example",
    .from = &.{
        math,
        pyoz.sub("strings", string_utils),
        pyoz.sub("io", io_utils),
    },
});

All public functions, classes, enums, and constants in the namespace are auto-registered into the submodule. Submodule docstrings come from a pub const __doc__ in the namespace. You can also combine pyoz.sub() with pyoz.source() for filtering.

See Auto-Scan (.from) for the full guide.

Next Steps