4

I'm working on wrapping a set of C functions using Cython into a module. I would like to be able to cimport this first module into subsequent Cython-based projects, but I am running into an 'undefined symbol' issue when importing the derivative modules in a python script.

Consider the following minimal working example of two modules developed in separate directories:

# exModA wraps the functions provided in exModAFxn.c
exModA/
    __init__.pxd
    __init__.py
    exModAFxn.c
    exModA.pyx
    setup.py
# exModB attempts to use the cdef functions from exModA
exModB/
   __init__.py
   exModB.pyx
   setup.py
# The test script attempts to make use of exModB
test.py

exModA/__init__.pxd:

cdef extern void myCMessage()

exModA/__init__.py:

from exModA import *

exModA/exModAFxn.c:

#include <stdio.h>
void myCMessage() { printf( "This is a test!\n" ); }

exModA/exModA.pyx:

cdef extern from "exModAFxn.c" :
    void myCMessage()
# Use myCMessage in a python function
def foo() :
    myCMessage()

exModA/setup.py:

from distutils.core import setup, Extension
from Cython.Build import cythonize
setup( 
    name = 'exModA',
    ext_modules = cythonize( ['exModA/exModA.pyx'] ),
)

exModB/__init__.py:

from exModB import *

exModB/exModB.pyx:

cimport exModA
# Use a cdef function from exModA in a python function
def bar() :
    exModA.myCMessage()

exModB/setup.py:

from distutils.core import setup, Extension
from Cython.Build import cythonize
setup( 
    name = 'exModB',
    ext_modules = cythonize( ['exModB/exModB.pyx'] ),
)

The test.py script is called after compiling the two cython modules.

python extModA/setup.py build_ext --inplace
python extModB/setup.py build_ext --inplace

test.py:

import exModA
exModA.foo()    # successfully prints message
import exModB   # raises ImportError: undefined symbol: myCMessage
exModB.bar()    # we never get to this line :(

The successful printing of the message from the exModA.foo function suggests to me that myCMessage is in fact available, but it isn't found when exModB is imported. I know this problem goes away if I merged exModA and exModB into a single project, but I'd like to avoid that approach if possible -- I'm trying to expose a common set of wrappers for a C library for use in several different applications.

res_edit
  • 183
  • 1
  • 8

1 Answers1

3

I was able to get this to work by doing the following:

First, specify the exModA library for linking in your exModB/setup.py file as follows:

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

ext = Extension('exModB/exModB',
                sources=['exModB/exModB.pyx'],
                libraries=['exModA'],
                library_dirs=['/home/MyFolder/exModA'],
                runtime_library_dirs=['/home/MyFolder/exModA']
                )
setup(name='exModB', ext_modules = cythonize(ext))

(Instead of specifying a runtime library directory, I recommend moving exModA.so to a folder on your library search path.)

I also had to rename exModA.so to libexModA.so so that gcc could find it. Do this after building exModA.so, but before building exModB.so:

python exModA/setup.py build_ext --inplace
cp exModA/exModA.so exModA/libexModA.so
python exModB/setup.py build_ext --inplace

You will probably want to tweak this, but it should get you started.

Carrie D.
  • 188
  • 2
  • 12
  • Thanks, Carrie! This worked as advertised for the example I provided. – res_edit Jan 27 '16 at 15:15
  • 1
    Works like a charm. Just a note: I actually followed the instructions from "Python Cookbook" from O'Reilly. I was getting undefined symbol for every single function I wrote and exported in my library (which was then converted to a cython module). When using `nm -C libsample.so` I was able to see that all required symbols were exported correctly however when doing `nm -C sample.cython-34m.so` I was getting `U` (undefined symbol) for all my stuff. Calling `cythonize` solved the problem. – rbaleksandar Dec 21 '16 at 19:17