Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cython: undefined symbol

Tags:

python

cython

I wrote a minimal C function (with accompanying header file) with the goal of creating a Cython wrapper that would enable the C code's use from a Python file somewhere else.

My files are as such:

C file:

/* engine.c */

#include <stdio.h>
#include "customlib.h"

int engine(int value, int iterations) {

    /* declare iteration index */
    int idx;

    for (idx = 0; idx < iterations; idx = idx + 1) {
        value = value * 3;
    }

    /* return value that should be accessible by the Python module */
    return value;
}

C header file:

/* customlib.h */

#ifndef CUSTOM_HEADER_H
#define CUSTOM_HEADER_H

/* engine function */
int engine(int value, int iterations);

#endif

Cython module that wraps the C code:

# wrapper.pyx

cdef extern from "customlib.h":
    int engine(int value, int iterations)

def call_c_code(value, iterations):
    output_value = engine(value, iterations)
    print(output_value)

Python module that calls the C code via the Cython wrapper:

# caller.py

import wrapper

wrapper.call_c_code(1, 2) /* values not important; just an example */

Setup code for generating the *.so from the *.pyx file:

# setup.py

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

# definitions
sourcefiles = ['engine.c']
extensions = [Extension('wrapper', sourcefiles)]

# setup function
setup(name='wrapper', ext_modules=cythonize('wrapper.pyx'))

THE PROBLEM: The shared object (*.so) seems to compile without any issues. However, even just importing the wrapper.py throws the following error:

Traceback (most recent call last):
  File "caller.py", line 10, in <module>
    import wrapper
ImportError: /home/alex/Documents/Projects/Cython/wrapper.cpython-36m-x86_64-linux-gnu.so: undefined symbol: engine
like image 752
questionable_code Avatar asked Jul 10 '18 18:07

questionable_code


1 Answers

In your setup.py you don't actually do anything with:

extensions = [Extension('wrapper', sourcefiles)]

this is just dead code, essentially. It assigns to a variable and then never uses it. Regardless, you don't want to make an Extension from your engine.c. Extension is for defining Python modules. The only Python module here is the one compiled from wrapper.pyx.

Instead, try something like:

extensions = [Extension('wrapper', ['wrapper.pyx', 'engine.c'])]
setup(
    ...
    ext_modules=cythonize(extensions)
    ...
 )

This will also compile engine.c and link its resulting object file into your extension module.

The cythonize helper function is smart enough to distinguish plain .c sources from Cython sources that it needs to pass through the Cython compiler. It will then pass all the resulting C sources through the C compiler and link them.

like image 69
Iguananaut Avatar answered Oct 17 '22 22:10

Iguananaut