Вызов библиотеки cython с несколькими файлами pyx через c ++ - PullRequest
0 голосов
/ 04 октября 2018

У меня есть проект на python, который я хочу вызвать из приложения c ++.Я хотел бы объединить все исходники Python в одну общую библиотеку и связать приложение c ++ с этой библиотекой.Прямо сейчас мой cython setup.py создает один *.so для каждого источника Python, что очень неудобно.

Вот файл setup.py:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

sourcefiles = ['project_interface.pyx', 'impl_file1.pyx']

setup(
    ext_modules = cythonize(sourcefiles)
)

project_interface.pyx:

# distutils: language = c++

import impl_file1

cdef public void HelloWorld():
    print "Calling Hello World"
    impl_file1.helloworld_func()

impl_file1.pyx:

def helloworld_func():
    print 'Hello World'

Я пытался изменить файл setup.py для объединения всего кода Python в одну библиотеку, например:

setup(
      ext_modules = cythonize([Extension("my_project", sourcefiles, language='c++')])
)

К сожалению,при выполнении void HelloWorld() приложение не может больше сохранять файл impl_file1.Я получаю:

Calling Hello World
NameError: name 'impl_file1' is not defined
Exception NameError: "name 'impl_file1' is not defined" in 'project_interface.HelloWorld' ignored

Программа на C ++, управляющая этим:

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

int main(int argc, const char** argv){
    Py_Initialize();
    initproject_interface();
    HelloWorld();
    Py_Finalize();


    return 0;
}

Это приложение работает правильно при компиляции с несколькими *.so файлами.

Компиляция оченьв обоих случаях это просто:

python setup.py build_ext --inplace
mv my_project.so libmy_project.so
g++ main.cpp -o main `python2-config --cflags --ldflags` -L. -lmy_project

Есть ли способ заставить работать решение с единой общей библиотекой?

Ответы [ 2 ]

0 голосов
/ 08 октября 2018

Для меня (в немного ином сценарии, но я не ожидал разницы) решение @DavidW нуждается в доработке.Вот мои настройки:

foo.pyx :

cdef extern from "Python.h":
    int PyImport_AppendInittab(const char *name, object (*initfunc)())


cdef extern from *:
    """
    PyObject *PyInit_bar(void);
    """
    object PyInit_bar()

PyImport_AppendInittab("bar", PyInit_bar)

import bar  # HERE The error happens

bar.pyx :

print("bar imported")

setup.py :

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

sourcefiles = ['foo.pyx', 'bar.pyx']


setup(
    ext_modules = cythonize([Extension("foo", sourcefiles)])
)

Теперь, после построения с python setup.py build_ext -i, выдается ошибка:

import foo
ImportError: 'bar' is not a built-in module

из здесь .Чтобы обойти это, я должен добавить имя "bar" в sys.builtin_module_names:

...
import sys
sys.builtin_module_names = list(sys.builtin_module_names)+["bar"]
import bar
0 голосов
/ 05 октября 2018

Существует несколько похожих вопросов о объединении нескольких модулей Cython (например, 1 , 2 ), которые на самом деле не жизнеспособны, поскольку Python использует пути к файлам для обработки модулей.Однако этот вопрос не совсем тот же, потому что вы вызываете его из C ++, что дает вам дополнительную опцию.

Вам необходимо использовать функцию C API PyImport_AppendInittab, чтобыPython для обработки impl_file1 как встроенного модуля, поэтому он не ищет путь к файлу для импорта.Начните с предоставления объявления функции импорта (поскольку вы не получите это из заголовочного файла):

extern "C" {
// PyObject* PyInit_impl_file1(); // Python 3
void initimpl_file1(); // Python 2
}

Затем в main перед Py_Initialize добавьте:

PyImport_AppendInittab("impl_file1", initimpl_file1); // change the name for Python 3
...