Search code examples
pythonctypessetuptoolspython-c-api

Packaged Python C Extension is missing PyInit_module() in so File


I have created a C library libgac and then wrote Python module implementing bindings with ctype. I call the python module gazepy.

My project file structure is as follows:

pyproject.toml
setup.py
src
  gazepy
    gac          # repo with the C library libgac
    gazepy.py    # python bindings for the C library libgac
    __init__.py  # empty file
tests

Running sudo pip install . generates and installs the so file gazepy.cpython-38-x86_64-linux-gnu.so.

So far so good, however, if I want to import the library in a python3 shell I get the following error:

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

My pyproject.toml file:

[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"

[project]
name = "gazepy"
# ...

My setup.py file:

from setuptools import setup, Extension, find_packages
import subprocess

module_name = "gazepy"
module_path = "src/" + module_name
gac_path = module_path + "/gac"
cglm_path = gac_path + "/cglm"

def main():
    setup(
            name=module_name,
            packages=find_packages(),
            ext_modules=[
                Extension(module_name, [gac_path + '/src/gac.c'],
                    include_dirs=[gac_path  + '/include', cglm_path + '/include'],
                    libraries=['m'])
            ]
    )


if __name__ == "__main__":
    main()

When looking at the so file gazepy.cpython-38-x86_64-linux-gnu.so I cannot find any definition of PyInit_gazepy():

nm /usr/local/lib/python3.8/dist-packages/gazepy.cpython-38-x86_64-linux-gnu.so | grep PyInit

What am I missing here?


Solution

  • python extension modules (.so and .pyd files) have higher priority than .py files when using the import command, so python is trying to import the .so file instead of the .py file.

    in order for import gazepy to correctly import your python file you should change the name of the extension module module_name to have an underscore _gazepy, this way python is not going to load it, then you should only load your .so using ctypes.


    you are compiling your C library as a python extension, which is wrong, as it now depends on python

    the above solution will work although your library is not a python extension module, but you should consider Compiling & installing C executable using python's setuptools/setup.py so it won't depend on python, unfortunately this has worse portability than just treating this C library as an extension module, so if you care about producing a clean library you should try compiling it as a C library instead of an extension module, but if you care about low effort portability then keep your current python extension method.