Call python code from c via cython

后端 未结 3 1580
有刺的猬
有刺的猬 2020-12-05 03:07

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 togethe

相关标签:
3条回答
  • 2020-12-05 03:39

    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.

    Edit

    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.

    0 讨论(0)
  • 2020-12-05 04:00

    Maybe this is not what you want but I got it working by the following changes:

    in quacker.pyx I added

    cdef public int i
    

    To force Cython to generate the .h file.

    An then in the main:

    #include <Python.h>
    #include "caller.h"
    #include "quacker.h"
    
    int main() {
      Py_Initialize();
      initquacker();
      initcaller();
      call_quack();
      Py_Finalize();
      return 0;
    }
    
    0 讨论(0)
  • 2020-12-05 04:03

    In case there's anyone wondering how would it work in Python 3, here's my solution after struggling a bit as a Cython newbie.

    main.c

    #include <Python.h>
    #include "caller.h"
    
    int
    main() 
    {
        PyImport_AppendInittab("caller", PyInit_caller);
        Py_Initialize();
        PyImport_ImportModule("caller");
        call_quack();
        Py_Finalize();
        return 0;
    }
    

    caller.pyx

    # cython: language_level=3
    import sys
    sys.path.insert(0, '')
    
    from quacker import quack
    
    cdef public void call_quack():
        quack()
    

    quacker.py

    def quack():
        print("Quack!")
    

    Finally, here's the Makefile that compiles everything:

    target=main
    cybridge=caller
    
    CC=gcc
    CFLAGS= `python3-config --cflags`
    LDFLAGS=`python3-config --ldflags`
    
    all:
            cython $(cybridge).pyx
            $(CC) $(CFLAGS) -c *.c
            $(CC) $(LDFLAGS) *.o -o $(target)
    
    clean:
            rm -f $(cybridge).{c,h,o} $(target).o $(target)
            rm -rf __pycache__
    
    0 讨论(0)
提交回复
热议问题