chmike
chmike

Reputation: 22174

Extending python with non python aware C library?

I would like to create a module that uses a non python aware C library and ctypes.

According to this documentation about creating a C extension to python, I’m required to provide a module initialization function with the signature PyObject* PyInit_modulename(void). This looks like a significant complication compared to using the wrapper using ctypes that I already have.

So far I tested building an Extension with setpuptools on unix and it worked. It was simply and handy. But it doesn’t work on windows. I use cibuildwheel and github actions to build wheels for all OS including windows. The compilation on Windows fails because the function PyInit_ext is not found.

I thus need to compile my code as a pure DLL (windows shared library). I then can load the library with ctypes. Can this be done with setuptools which is used by cibuildwheel ?

How can I do that ?

Here is my setup.cfg file:

[metadata]
name = hello_chmike
version = 0.0.0
url = https://github.com/chmike/hello
maintainer = Christophe Meessen
maintainer_email = [email protected]
description = Simple python module with external C library
long_description = file: README.md
long_description_content_type = text/markdown
keywords = Example
license = BSD 3-Clause License
license_file = LICENSE.txt
classifiers =
    License :: OSI Approved :: BSD License
    Programming Language :: Python :: 3
    Operating System :: OS Independent
    Topic :: Utilities

[options]
packages = hello
python_requires = >=3.6

and here is the setup.py file I currently have and which is not good because it uses Extension:

from setuptools import setup, Extension

setup(
  ext_modules=[Extension('hello.ext',
                       ['src/hello.c'],
                       depends=['src/hello.h'],
                       include_dirs=['src'],
              )],
)

The content of the file hello.c is the following:

#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS 1
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// hello return a heap allocated string containing the name appended 
// to "hello " and followed by "!".
char *hello(char *name) {
    char *buf = malloc(7+strlen(name));
    sprintf(buf, "hello %s!", name);
    return buf;
}

Upvotes: 1

Views: 133

Answers (1)

chmike
chmike

Reputation: 22174

The only viable solution to get portable code across different OS and using the python building tools and cibuildwheel to get precompiled modules is to create a real python extension.

This means creating a python extension C code that wraps the python agnostic C library. Wrapping means that we create C functions as required by the Python C extension standard that call the python agnostic C functions from my library.

Using ctypes to wrap the python agnostic C library will work when used for a specific OS like linux. But the execution context is so different between OS that trying to make a ctypes wrapper that works on the every OS requires complex hacking and platform switch in the python wrapper.

By using real python extension C code, we avoid this complexity. I wish I knew this when I started.

Upvotes: 1

Related Questions