So I'd like to call some python code from c via cython. I've managed to call cython code from c. And I can also call python code from cython. But when I add it all together, some things are missing.
Here is my python code (quacker.pyx
):
def quack():
print "Quack!"
Here is my cython "bridge" (caller.pyx
):
from quacker import quack
cdef public void call_quack():
quack()
And here is the c code (main.c
):
#include <Python.h>
#include "caller.h"
int main() {
Py_Initialize();
initcaller();
call_quack();
Py_Finalize();
return 0;
}
When I run this I get this exception:
Exception NameError: "name 'quack' is not defined" in 'caller.call_quack' ignored
The missing pieces I'm suspecting:
initquacker()
quacker.h
quacker.h
- only quacker.c
caller.c
doesn't import quacker.h
or call initquacker()
I'm not really sure that it's even possible to do what I'm trying to do, but it seems to me that it ought to be. I'd love to hear any input you might have.
Edit:
This is how I cythonize / compile / link / run:
$ cython *.pyx
$ cc -c *.c -I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7
$ cc -L/System/Library/Frameworks/Python.framework/Versions/2.7/lib -L/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config -lpython2.7 -ldl *.o -o main
$ ./main
If you rename the quacker.pyx
to quacker.py
, everything is actually correct. The only problem is that your program won't search for python modules in the current directory, resulting in the output:
Exception NameError: "name 'quack' is not defined" in 'caller.call_quack' ignored
If you add the current directory to the PYTHONPATH environment variable however, the output becomes the one you'd expect:
$ PYTHONPATH=".:$PYTHONPATH" ./main
Quack!
When running the python shell, according to the documentation the current directory (or the directory containing the script) is added to the sys.path
variable automatically, but when creating a simple program using Py_Initialize
and Py_Finalize
this does not seem to happen. Since the PYTHONPATH variable is also used to populate the sys.path
python variable, the workaround above produces the correct result.
Alternatively, below the Py_Intialize
line, you could add an empty string to sys.path
as follows by just executing some python code, specified as a string:
PyRun_SimpleString("import sys\nsys.path.insert(0,'')");
After recompiling, just running ./main
should then work.
It's actually interesting to see what's going on if you run the code as specified in the question, so without renaming the quacker.pyx
file. In that case, the initcaller()
function tries to import the quacker
module, but since no quacker.py
or quacker.pyc
exists, the module cannot be found, and the initcaller()
function produces an error.
Now, this error is reported the python way, by raising an exception. But the code in the main.c
file doesn't check for this. I'm no expert in this, but in my tests adding the following code below initcaller()
seemed to work:
if (PyErr_Occurred())
{
PyErr_Print();
return -1;
}
The output of the program then becomes the following:
Traceback (most recent call last):
File "caller.pyx", line 1, in init caller (caller.c:836)
from quacker import quack
ImportError: No module named quacker
By calling the initquacker()
function before initcaller()
, the module name quacker
already gets registered so the import call that's done inside initcaller()
will detect that it's already loaded and the call will succeed.