Hi there!

I’m experimenting with different tools to write low level extensions to Python. I’ve written a bunch of extensions with PyBind11, but I really wanted to mess with Zig. I’ve been looking for an excuse for ages to try it.

Anyway, if you’re in C code, you register an extension module by using this PyMODINIT_FUNC thing like this:


PyMODINIT_FUNC PyInit_simple(void) {
    return PyModule_Create(&module);


pub export fn PyInit_simple() PyMODINIT_FUNC {
    return PyModule_Create(&module);

When compiling, I get an error:

pub const PyMODINIT_FUNC = @compileError("unable to translate C expr: unexpected token 'Eof'"); 

I’m so new to Zig that I don’t even know where to start. Did I bite off more than I could chew? Should I just go back to my previous Zigless (boring) life with C++ / PyBind11?

Also first time posting, really hope it supports markdown triple backtick.

Cheers everyone <3


Hi @glitch, welcome to ziggit :slight_smile:

cImport generates a @compileError when it cannot parse the definition.
Since PyMODINIT_FUNC is actually a pointer to a PyObject, the following code must work:

const py = @cImport({
    @cDefine("PY_SSIZE_T_CLEAN", 1);

const PyObject = py.PyObject;

pub export fn PyInit_simple() *PyObject {
    return PyModule_Create(&module);

OK will check it out thank you <3

@dimdin Thank you so much, that helped me compile the program.

Weirdly I’m getting an error now on the Python side of things, which I recognize isn’t necessarily relevant subject matter for this forum.

Here is my whole program at this point:

    @cDefine("PY_SSIZE_T_CLEAN", {});

const std = @import("std");
const print = std.debug.print;

const PyObject = py.PyObject;
const PyMethodDef = py.PyMethodDef;
const PyModuleDef = py.PyModuleDef;
const Py_BuildValue = py.Py_BuildValue;
const PyModule_Create = py.PyModule_Create;
const PyArg_ParseTuple = py.PyArg_ParseTuple;

fn do_stuff(self: [*c]PyObject, args: [*c]PyObject) callconv(.C) [*]PyObject {
    _ = self;
    var input: [*:0]u8 = undefined;
    if (PyArg_ParseTuple(args, "s", &input) == 0) return Py_BuildValue("");
    print("you entered: {s}\n", .{input});
    return Py_BuildValue("s", "Take off every Zig");

var methods = [_]PyMethodDef{
        .ml_name = "do_stuff",
        .ml_meth = do_stuff,
        .ml_flags = @as(c_int, 1),
        .ml_doc = "For Great Justice!",

var module = PyModuleDef{
    .m_name = "simple",
    .m_methods = &methods,

pub export fn PyInit_simple() *PyObject {
    return PyModule_Create(&module);

Trying to import the module in Python gives me a very odd looking error:

ImportError: dynamic module does not define module export function (PyInit_simple)

It must mean that there is something off about the type signature of the PyInit_simple fn?

Curious if anyone has seen this.

Thanks again <3

Some ideas:

  • PyInit_simple must be in a module called simple (not sure if this is or something else)
  • Where python is trying to find this synamic library ( or whatever must be called).
1 Like

Yeah, when I used to use pybind the issue is that the names were off. Here’s a stack overflow that may help. Looks like you may need to rename something: python - Unable to solve "ImportError: dynamic module does not define module export function" - Stack Overflow

Also, welcome @glitch, good to see some python developers making their way onto the forum!

I don’t have direct answers, but here is a meetup talk and the related project:

1 Like

Thank you so much for the warm welcome!

I’ll keep poking away and seeing if I can get my Python ext module boilerplate to do stuff. Folks clearly have linked me working examples so it’s probably just a PEBKAC error…

It would be super nice to be able to use Zig. I love the syntax, it’s so much cleaner and more modern feeling than C…

Also, even if I fail in my task and I’m stuck in C-land I absolutely intend to utilize Zig’s cross-platform compiler tooling. That right there is downright amazing.