I'm trying to call cython (cdef) function in C program. When the cdef function contains python statements, e.g. print(0.5), or python (def) functions, calling the (cdef) function raises a segmentation fault.
The .pyx file:
# cython: language_level=3
cdef public double PI = 3.1415926
cdef public double get_e():
print("calling get_e()")
return 2.718281828
The .c file:
#include "Python.h"
#include "transcendentals.h"
#include <math.h>
#include <stdio.h>
int main(int argc, char **argv) {
Py_Initialize();
PyInit_transcendentals();
printf("pi**e: %f\n", pow(PI, get_e()));
Py_Finalize();
return 0;
}
The compiling commands:
cython transcendentals.pyx
gcc -I. -I/usr/include/python3.5m -I/usr/include/python3.5m \
-Wno-unused-result -Wsign-compare \
-g -fstack-protector-strong -Wformat \
-Werror=format-security -DNDEBUG -g \
-fwrapv -O3 -Wall -Wstrict-prototypes \
-L/usr/lib/python3.5/config-3.5m-x86_64-linux-gnu \
-L/usr/lib transcendentals.c main.c \
-lpython3.5m -lpthread -ldl -lutil -lm -Xlinker \
-export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions
When I remove the print statement of get_e function, no segmentation fault would be raised. But the value of PI will be 0.
I guess you are using Cython 0.29. Since 0.29, PEP-489 multi-phase module initialisation has been enabled for Python versions >=3.5. This means, using PyInit_XXX
is no longer sufficient, as you are experiencing.
Cython's documentation suggest to use inittab mechanism, i.e. your main
-function should look something like:
#include "Python.h"
#include "transcendentals.h"
#include <math.h>
#include <stdio.h>
int main(int argc, char **argv) {
int status=PyImport_AppendInittab("transcendentals", PyInit_transcendentals);
if(status==-1){
return -1;//error
}
Py_Initialize();
PyObject *module = PyImport_ImportModule("transcendentals");
if(module==NULL){
Py_Finalize();
return -1;//error
}
printf("pi**e: %f\n", pow(PI, get_e()));
Py_Finalize();
return 0;
}
Another possibility to restore the old behavior would be to define macro CYTHON_PEP489_MULTI_PHASE_INIT=0
and thus overriding the default by e.g. passing -DCYTHON_PEP489_MULTI_PHASE_INIT=0
to gcc on the command line.
This seems like a bug (or at least an issue with Python3.7).
I tested your example on my Arch Linux with Python3.7.
First thing which made me curious was how long the compilation took on this step:
gcc -I. -I/usr/include/python3.7m -I/usr/include/python3.7m -Wno-unused-result \
-Wsign-compare -g -fstack-protector-strong -Wformat -Werror=format-security -g \
-fwrapv -O0 -Wall -Wstrict-prototypes -L/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu \
-L/usr/lib transcendentals.c main.c -lpython3.7m -lpthread -ldl -lutil -lm
I have a not so bad computer but it took it a couple of minutes to get this compilation done. Strange.
And upon running ./a.out
, I also got a segmentation error, like you.
So, then I decided to test (with one minor modification: change PyInit_transcendentals
to inittranscendentals
in main
) with Python2.7, as shown below:
gcc -I. -I/usr/include/python2.7 -I/usr/include/python2.7 -Wno-unused-result \
-Wsign-compare -g -fstack-protector-strong -Wformat -Werror=format-security \
-g -fwrapv -O0 -Wall -Wstrict-prototypes -L/usr/lib/python2.7/config-2.7-x86_64-linux-gnu \
-L/usr/lib transcendentals.c main.c -lpython2.7 -lpthread -ldl -lutil -lm
The compilation was instant.
I ran ./a.out
and the output was:
called get_e():2.718282calling get_e()
pi**e: 22.459157
Then just to be sure, that this had nothing to do with any flags that you might be using, nor that the math library nor something else would be having an effect here, I repeated the test with a very simple "hello world" example as shown below.
#include <Python.h>
#include "hello.h"
int main() {
Py_Initialize();
inithello();
hello();
Py_Finalize();
return 0;
}
# cython: language_level=2
cdef public hello():
print "hello!"
Then,
cython hello.pyx
cc -c *.c -I /usr/include/python2.7/
cc -L /usr/lib/python2.7/ -lpython2.7 -ldl *.o -o main
./main
The output was,
hello!
On the other hand, recompling with Python3.7 (after changing inithello
to PyInit_hello
) gave the following output:
cc -c *.c -I /usr/include/python3.7m/
cc -L /usr/lib/python3.7/ -lpython3.7m -ldl *.o -o main
./main
Segmentation fault (core dumped)