Ответ 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()
, обнаружит, что он уже загружен и вызов будет успешным.