Complete Module Example¶
This example demonstrates a complete PyOZ module with functions, classes, enums, exceptions, and constants.
Source Code¶
// src/lib.zig
const std = @import("std");
const pyoz = @import("PyOZ");
// ============================================================================
// Functions
// ============================================================================
/// Add two integers
fn add(a: i64, b: i64) i64 {
return a + b;
}
/// Divide with error handling
fn divide(a: f64, b: f64) !f64 {
if (b == 0.0) return error.DivisionByZero;
return a / b;
}
/// Function with optional parameters
fn greet(name: []const u8, greeting: ?[]const u8, times: ?i64) []const u8 {
_ = name;
_ = greeting orelse "Hello";
_ = times orelse 1;
return "Hello!";
}
/// CPU-intensive work that releases the GIL
fn compute(n: i64) i64 {
const gil = pyoz.releaseGIL();
defer gil.acquire();
var sum: i64 = 0;
var i: i64 = 0;
while (i < n) : (i += 1) {
sum +%= @mod(i *% i, 1000000007);
}
return sum;
}
// ============================================================================
// Classes
// ============================================================================
const Point = struct {
pub const __doc__: [*:0]const u8 = "A 2D point with x and y coordinates.";
x: f64,
y: f64,
pub fn __new__(x: f64, y: f64) Point {
return .{ .x = x, .y = y };
}
pub fn magnitude(self: *const Point) f64 {
return @sqrt(self.x * self.x + self.y * self.y);
}
pub fn scale(self: *Point, factor: f64) void {
self.x *= factor;
self.y *= factor;
}
pub fn __add__(self: *const Point, other: *const Point) Point {
return .{ .x = self.x + other.x, .y = self.y + other.y };
}
pub fn __repr__(self: *const Point) []const u8 {
_ = self;
return "Point(...)";
}
pub fn origin() Point {
return .{ .x = 0.0, .y = 0.0 };
}
};
const Counter = struct {
count: i64,
step: i64,
pub fn __new__(initial: i64, step: ?i64) Counter {
return .{
.count = initial,
.step = step orelse 1,
};
}
pub fn increment(self: *Counter) void {
self.count += self.step;
}
pub fn decrement(self: *Counter) void {
self.count -= self.step;
}
pub fn get(self: *const Counter) i64 {
return self.count;
}
pub fn reset(self: *Counter) void {
self.count = 0;
}
};
// ============================================================================
// Enums
// ============================================================================
const Priority = enum(i32) {
Low = 1,
Medium = 2,
High = 3,
Critical = 4,
};
const Status = enum {
pending,
active,
completed,
cancelled,
};
// ============================================================================
// Module Definition
// ============================================================================
const MyModule = pyoz.module(.{
.name = "mymodule",
.doc = "A complete example PyOZ module",
.funcs = &.{
pyoz.func("add", add, "Add two integers"),
pyoz.func("divide", divide, "Divide two numbers"),
pyoz.kwfunc("greet", greet, "Greet someone"),
pyoz.func("compute", compute, "CPU-intensive computation (releases GIL)"),
},
.classes = &.{
pyoz.class("Point", Point),
pyoz.class("Counter", Counter),
},
.enums = &.{
pyoz.enumDef("Priority", Priority),
pyoz.enumDef("Status", Status),
},
.consts = &.{
pyoz.constant("VERSION", "1.0.0"),
pyoz.constant("PI", 3.14159265358979),
pyoz.constant("MAX_VALUE", @as(i64, 1000000)),
},
.exceptions = &.{
pyoz.exception("ValidationError", .{ .doc = "Validation failed", .base = .ValueError }),
},
.error_mappings = &.{
pyoz.mapError("DivisionByZero", .ValueError),
},
});
pub export fn PyInit_mymodule() ?*pyoz.PyObject {
return MyModule.init();
}
Usage in Python¶
import mymodule
from mymodule import Point, Counter, Priority, Status
# Constants
print(mymodule.VERSION) # "1.0.0"
print(mymodule.PI) # 3.14159265358979
print(mymodule.MAX_VALUE) # 1000000
# Functions
print(mymodule.add(2, 3)) # 5
print(mymodule.divide(10, 2)) # 5.0
print(mymodule.greet("World")) # "Hello!"
print(mymodule.greet("World", "Hi", 3))
# Error handling
try:
mymodule.divide(1, 0)
except ValueError as e:
print(f"Error: {e}") # Error: DivisionByZero
# Classes
p1 = Point(3.0, 4.0)
print(p1.x, p1.y) # 3.0 4.0
print(p1.magnitude()) # 5.0
p2 = Point(1.0, 1.0)
p3 = p1 + p2 # Uses __add__
print(p3.x, p3.y) # 4.0 5.0
p1.scale(2.0)
print(p1.x, p1.y) # 6.0 8.0
origin = Point.origin() # Static method
print(origin.x, origin.y) # 0.0 0.0
# Counter
c = Counter(0)
c.increment()
c.increment()
print(c.get()) # 2
c = Counter(10, 5) # Start at 10, step by 5
c.increment()
print(c.get()) # 15
# Enums
print(Priority.High) # Priority.High
print(Priority.High.value) # 3
print(Status.active) # Status.active
print(Status.active.value) # "active"
for p in Priority:
print(f"{p.name} = {p.value}")
# GIL release for threading
import threading
import time
def python_work():
return sum(range(1000000))
# compute() releases GIL, allowing other threads to run
start = time.time()
t = threading.Thread(target=python_work)
t.start()
result = mymodule.compute(10000000)
t.join()
print(f"Completed in {time.time() - start:.2f}s")
Build and Test¶
# Initialize (if starting fresh)
pyoz init mymodule
# Build for development
pyoz develop
# Test
python -c "import mymodule; print(mymodule.add(1, 2))"
# Build release wheel
pyoz build --release
# Publish
export PYPI_TOKEN="pypi-..."
pyoz publish