Вызовите код python c c через cython

Итак, я хотел бы назвать некоторый код python с c через cython. Мне удалось вызвать код cython с c. И я также могу вызвать код python из cython. Но когда я добавляю все это вместе, некоторые вещи отсутствуют.

Вот мой код python (quacker.pyx):

def quack():
    print "Quack!"

Вот мой "мост" cython (caller.pyx):

from quacker import quack

cdef public void call_quack():
    quack()

И вот код c (main.c):

#include <Python.h>
#include "caller.h"

int main() {
  Py_Initialize();
  initcaller();
  call_quack();
  Py_Finalize();
  return 0;
}

Когда я запускаю это, я получаю это исключение:

Exception NameError: "name 'quack' is not defined" in 'caller.call_quack' ignored

Отсутствующие части, которые я подозреваю:

  • Я не звонил initquacker()
  • Я не включил quacker.h
  • Cython не производил никаких quacker.h - only quacker.c
  • caller.c не импортирует quacker.h или вызывает initquacker()

Я не уверен, что даже можно делать то, что я пытаюсь сделать, но мне кажется, что это должно быть. Я бы хотел услышать любые ваши данные.

Edit:

Вот как я 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

Ответы

Ответ 1

Если вы переименуете quacker.pyx в quacker.py, все будет правильно. Единственная проблема заключается в том, что ваша программа не будет искать модули python в текущем каталоге, что приведет к выводу:

Exception NameError: "name 'quack' is not defined" in 'caller.call_quack' ignored

Если вы добавите текущий каталог в переменную среды PYTHONPATH, то результат будет таким, который вы ожидаете:

$ PYTHONPATH=".:$PYTHONPATH" ./main 
Quack!

При запуске оболочки python в соответствии с документацией текущий каталог (или каталог, содержащий script) автоматически добавляется к переменной sys.path но при создании простой программы с использованием Py_Initialize и Py_Finalize это, похоже, не происходит. Поскольку переменная PYTHONPATH также используется для заполнения переменной python sys.path, обходной путь выше дает правильный результат.

В качестве альтернативы, ниже строки Py_Intialize вы можете добавить пустую строку в sys.path следующим образом, просто выполнив некоторый код python, указанный в виде строки:

PyRun_SimpleString("import sys\nsys.path.insert(0,'')");

После перекомпиляции следует запустить только ./main.

Изменить

На самом деле интересно посмотреть, что происходит, если вы запускаете код, как указано в вопросе, поэтому без переименования файла quacker.pyx. В этом случае функция initcaller() пытается импортировать модуль quacker, но поскольку существует не quacker.py или quacker.pyc, модуль не может быть найден, а функция initcaller() создает ошибку.

Теперь эта ошибка сообщается о способе python, создавая исключение. Но код в файле main.c не проверяет это. Я не эксперт в этом, но в моих тестах добавлен следующий код ниже initcaller(), казалось, работал:

if (PyErr_Occurred())
{
    PyErr_Print();
    return -1;
}

Затем вывод программы будет следующим:

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

Вызывая функцию initquacker() до initcaller(), имя модуля quacker уже регистрируется, поэтому вызов импорта, который был выполнен внутри initcaller(), обнаружит, что он уже загружен и вызов будет успешным.

Ответ 2

Возможно, это не то, что вы хотите, но я получил его, выполнив следующие изменения:

в quacker.pyx Я добавил

cdef public int i

Чтобы заставить Cython генерировать файл .h.

Затем в главном:

#include <Python.h>
#include "caller.h"
#include "quacker.h"

int main() {
  Py_Initialize();
  initquacker();
  initcaller();
  call_quack();
  Py_Finalize();
  return 0;
}