Simple Cython file causes redefinition errors when using cpdef

Viewed 152

I've got a simple cython *.pyx file which causes a bunch of redefinition errors, and I don't understand why. If I change 'cpdef' to 'cdef' it compiles fine, but then doesn't export those functions, and I'd like them to be exported. Can somebody explain why this fails, or what I'm doing wrong here?

core.pyx:

cdef extern from "module.h":
    cpdef double radians(double degrees)
    cpdef double degrees(double radians)

module.h:

#ifndef MODULE_H
#define MODULE_H
double radians(double degrees);
double degrees(double radians);
#endif

module.c:

#include "module.h"

double radians(double degrees)
{
    return degrees * (M_PI / 180.0);
}

double degrees(double radians)
{
    return radians * (180.0 / M_PI);
}

errors:

$ python3 setup.py build
running build
running build_ext
building 'core' extension
x86_64-linux-gnu-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -Iinclude -I/usr/include/python3.8 -c src/core.c -o build/temp.linux-x86_64-3.8/src/core.o
src/core.c:1294:13: error: redefinition of ‘__pyx_doc_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap’
 1294 | static char __pyx_doc_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap[] = "wrap(radians: float) -> float";
      |             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1146:13: note: previous definition of ‘__pyx_doc_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap’ was here
 1146 | static char __pyx_doc_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap[] = "wrap(degrees: float) -> float";
      |             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1295:20: error: redefinition of ‘__pyx_mdef_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap’
 1295 | static PyMethodDef __pyx_mdef_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap = {"wrap", (PyCFunction)__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap, METH_O, __pyx_doc_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap};
      |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1147:20: note: previous definition of ‘__pyx_mdef_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap’ was here
 1147 | static PyMethodDef __pyx_mdef_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap = {"wrap", (PyCFunction)__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap, METH_O, __pyx_doc_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap};
      |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1296:18: error: redefinition of ‘__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap’
 1296 | static PyObject *__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap(PyObject *__pyx_self, PyObject *__pyx_arg_radians) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1148:18: note: previous definition of ‘__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap’ was here
 1148 | static PyObject *__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap(PyObject *__pyx_self, PyObject *__pyx_arg_degrees) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1317:18: error: redefinition of ‘__pyx_pf_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap’
 1317 | static PyObject *__pyx_pf_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap(PyObject *__pyx_self, double __pyx_v_radians) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1169:18: note: previous definition of ‘__pyx_pf_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap’ was here
 1169 | static PyObject *__pyx_pf_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap(PyObject *__pyx_self, double __pyx_v_degrees) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1370:18: error: redefinition of ‘__Pyx_CFunc_double____double___to_py’
 1370 | static PyObject *__Pyx_CFunc_double____double___to_py(double (*__pyx_v_f)(double)) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1222:18: note: previous definition of ‘__Pyx_CFunc_double____double___to_py’ was here
 1222 | static PyObject *__Pyx_CFunc_double____double___to_py(double (*__pyx_v_f)(double)) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1317:18: warning: ‘__pyx_pf_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap’ defined but not used [-Wunused-function]
 1317 | static PyObject *__pyx_pf_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap(PyObject *__pyx_self, double __pyx_v_radians) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1296:18: warning: ‘__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap’ defined but not used [-Wunused-function]
 1296 | static PyObject *__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap(PyObject *__pyx_self, PyObject *__pyx_arg_radians) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1222:18: warning: ‘__Pyx_CFunc_double____double___to_py’ defined but not used [-Wunused-function]
 1222 | static PyObject *__Pyx_CFunc_double____double___to_py(double (*__pyx_v_f)(double)) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: command 'x86_64-linux-gnu-gcc' failed with exit status 1

1 Answers

As DavidW has pointed out, this is probably a bug. It looks like, the variable names used in the resulting C-code only take the variables types but not the name of the function, thus __pyx_doc_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap gets defined twice: once for radians and once degrees.

So for the time being you should use workarounds. One would be to give double-type different names, for example:

%%cython -a  --verbose
cdef extern from *:
    """
    //some dummy implementations
    double radians(double degrees)
    {
        return degrees * (1.0 / 180.0);
    }

    int degrees(double radians)
    {
        return radians * (180.0 / 1.0);
    }
    """
    ctypedef double double1 "double"
    cpdef double degrees(double radians)
    cpdef double1 radians(double degrees)

here we use the cname-trick and Cython will replace "double1" through "double".

But this doesn't look like a sane solution on a bigger scale/long term. I would switch to a more verbose, but less puzzling approach (given that using cpdef in extern (here documentation) is not very common anyway):

%%cython -a

cdef extern from *:
    """
    // code as above
    ...
    """
    double c_degrees "degrees"(double radians)
    double c_radians "radians"(double degrees)
    
cpdef double degrees(double radians):
    return c_degrees(radians)
cpdef double radians(double degrees):
    return c_radians(degrees)

Once again, cname-trick is used to differentiate between cpdef and wrappend cname of the function in Cython code.

Related