Skip to content

erik-dunteman/zigpy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

zigpy

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

Basic Example

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

About

A python bindings generator for your Zig library

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published