A python bindings generator for your Zig library
This repo is currently a proof-of-concept for basic method return types.
It uses a combination of comptime type introspection and a code-gen step in build.zig
to wrap src/example_lib.zig
in a Python module
src/example_lib.zig
const std = @import("std");
const bind = @import("bind_gen.zig");
pub const MyStruct = extern struct {
a: i32,
b: i32,
// called as constructor
pub fn __init__() callconv(.C) *MyStruct {
const allocator = std.heap.page_allocator;
const my_struct = allocator.create(MyStruct) catch unreachable;
// give default values
my_struct.* = MyStruct{
.a = 42,
.b = 84,
};
return my_struct;
}
// called when the object is cleaned by python GC
pub fn __del__(ptr: *MyStruct) callconv(.C) void {
const allocator = std.heap.page_allocator;
allocator.destroy(ptr);
}
// some arbitrary method
pub fn print(ptr: *MyStruct) callconv(.C) void {
std.debug.print("print: {any}\n", .{ptr.*});
}
// method with string args
pub fn print_pystring(_: *MyStruct, str_ptr: [*:0]u8) callconv(.C) void {
const str: []u8 = std.mem.span(str_ptr); // strips off sentinel
std.debug.print("print_pystring: {s}\n", .{str});
}
};
// export struct's methods to shared library
// we use this instead of the "export" keyword to prevent naming collisions in the shared library
comptime {
bind.exportStruct(MyStruct);
}
Running a build
zig build
Will produce a shared library at zig-out/libzigpy.dylib
and code-gen Python bindings at MyStruct.py
MyStruct.py
# Autogenerated Python bindings for MyStruct
# Do not edit this file directly
from ctypes import CDLL, Structure, c_int, POINTER
libzigpy = CDLL("./zig-out/lib/libzigpy.dylib")
class MyStruct(Structure):
_fields_ = [("a", c_int), ("b", c_int), ]
libzigpy.codegenstruct___init__.restype = POINTER(MyStruct)
libzigpy.codegenstruct___del__.argtypes = [POINTER(MyStruct)]
class MyStruct():
def __init__(self):
self.ptr = libzigpy.codegenstruct___init__()
def __del__(self):
libzigpy.codegenstruct___del__(self.ptr)
@property
def a(self):
return self.ptr.contents.a
@a.setter
def a(self, value):
self.ptr.contents.a = value
@property
def b(self):
return self.ptr.contents.b
@b.setter
def b(self, value):
self.ptr.contents.b = value
def print(self):
return libzigpy.codegenstruct_print(self.ptr)
def print_pystring(self, arg1: str):
c_arg1 = c_char_p(arg1.encode('utf-8'))
return libzigpy.codegenstruct_print_pystring(self.ptr, c_arg1)
which can be imported and used in any python program:
from MyStruct import MyStruct
# my_struct is a pointer to a Zig-allocated object
my_struct = MyStruct()
# Get attributes
print(my_struct.a) # -> 42
# Set attributes
my_struct.a = 10
print(my_struct.a) # -> 10
# Call public methods
my_struct.print() # -> print: example_lib.MyStruct{ .a = 10, .b = 20 }
my_struct.print_pystring("hello world") # -> print_pystring: hello world