Search code examples
pythonctypes

Method not visible from C extension module


I'm trying to create a simple C extension to Python and get it to run on OS X. I'm running Python 3.6. My method is apparently not getting exported from the module. Stripped down, my code looks like this:

Example.c:

#define PY_SSIZE_T_CLEAN
#include <Python.h>

static PyObject*
myfunc(PyObject* self, PyObject* args)
{
    /* Not parsing args because I'm just returning a constant */
    return PyLong_FromLong(1);
}

/* Method table */
static PyMethodDef MyMethods[] = {
    {"myfunc", myfunc, METH_VARARGS, "Just do something"},
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

/* Module defintion */
static struct PyModuleDef mymodule = {
    PyModuleDef_HEAD_INIT,
    "example",   /* name of module */
    NULL, /* module documentation, may be NULL */
    -1,       /* size of per-interpreter state of the module,
                 or -1 if the module keeps state in global variables. */
    MyMethods
};

/* Module initialization */
PyMODINIT_FUNC
PyInit_example(void)
{
    //import_array(); // initialize numpy arrays
    return PyModule_Create(&mymodule);
}

I compile with

gcc -c -fPIC -I$PYINCLUDE example.c
gcc example.o -shared -o libexample.so -L$PYLIB -lpython3.6m

creating the .so file. Finally, I am trying to run this with:

import ctypes
m = ctypes.CDLL("libexample.so")
print(m.myfunc("asdf"))

when I get:

Traceback (most recent call last):
  File "test.py", line 3, in <module>
    print(m.myfunc("asdf"))
  File "/PATH/TO/anaconda3/lib/python3.6/ctypes/__init__.py", line 361, in __getattr__
    func = self.__getitem__(name)
  File "/PATH/TO/anaconda3/lib/python3.6/ctypes/__init__.py", line 366, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: dlsym(0x7f96f652e4e0, myfunc): symbol not found

I realize that many tutorials recommend using setuptools or distutils to package extensions, but I'm trying to learn only one new tool at a time. Is there something obvious that I'm doing wrong?


Solution

  • The ctypes module is meant for accessing dynamically loaded libraries in regular C. You are creating a python extension. Since python functions in C are declared as static, myfunc is not exposed in your .so file. You need to compile the extension in distutils or setuptools then import the module in your python code. If you would like to use ctypes like your example, you need to rewrite your code in C similar to below.

    Header file:

    #ifndef _EXAMPLE_H_
    #define _EXAMPLE_H_
    int myfunc();
    #endif
    

    C source file:

    #include "example.h"
    
    int myfunc()
    {
        /* Not parsing args because function is just returning a constant */
        return 1;
    }