【发布时间】:2021-06-19 07:34:27
【问题描述】:
我有一个用 C++ 编写的大型程序,我希望通过 Python 使其可用。我编写了一个 python 扩展来公开一个接口,python 代码可以通过该接口调用 C++ 函数。我遇到的问题是安装似乎很重要。
我能找到的所有文档似乎都表明我应该创建一个setup.py,它会创建一个distutils.core.Extension。在我发现的每个示例中,正在创建的 Extension 对象都被提供了一个源文件列表,它会编译这些文件。如果我的代码是一两个文件,那就没问题了。不幸的是,它有几十个文件,并且我使用了一些相对复杂的 Visual Studio 构建设置。因此,至少可以说,通过列出 .c 文件进行构建似乎具有挑战性。
我目前已将我的 Python 扩展配置为构建为 .dll 并链接到 python39.lib。我尝试将扩展名更改为 .pyd 并将文件包含在 manifest.in 中。在我创建 setup.py 并运行它之后,它创建了一个 .egg 文件,我验证该文件确实包含我创建的 .pyd。但是,安装后,当我将模块导入python时,模块完全是空的(我验证了 PyInit_[module] 函数没有被调用)。 Python dll Extension Import 说如果我将扩展名更改为 .pyd 并将文件放在 python 安装的 Dlls 目录中,我可以导入 dll。我遇到了两个问题。
首先,在我看来,它不是像这样可分发的。我想把它打包成一个 python 轮子,我不确定轮子是如何做到这一点的。第二个问题更大——它并不完全有效。它调用了我的扩展的初始化函数,我已经在 WinDbg 中验证了它正在返回一个 python 模块。然而,这是我总是从控制台得到的。
>>> import bluespawn
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
SystemError: initialization of bluespawn did not return an extension module
Python 文档中有一个关于发布二进制扩展的部分,但在过去的四年里,它一直被用作占位符。链接到here 的 github 问题也不是很有帮助;它归结为使用 distutils 构建或使用 enscons 构建。但是由于我的构建是一个相当复杂的过程,至少可以说,完全重写它以使用 enscons 是不可取的。
在我看来,将文件放在 DLLs 目录中是错误的做法。鉴于我有一个 DLL 并且让 setuptools 编译一切本身似乎是不可行的,我应该如何安装我的扩展程序?
作为参考,这是我的初始化函数,以防不正确。
PyModuleDef bsModule{ PyModuleDef_HEAD_INIT, "bluespawn", "Bluespawn python bindings", -1, methods };
PyMODINIT_FUNC PyInit_bluespawn() {
PyObject* m;
Py_Initialize();
PyEval_InitThreads();
PyGILState_STATE state = PyGILState_Ensure(); // Crashes without this. Call to PyEval_InitThreads() required for this.
m = PyModule_Create(&bsModule);
PyGILState_Release(state);
Py_Finalize();
return m;
}
python 接口在这里可用:https://github.com/ION28/BLUESPAWN/blob/client-add-pylib/BLUESPAWN-win-client/src/user/python/PythonInterface.cpp
编辑:我有一个可行的解决方案,我确定不是最佳实践。我创建了一个非常小的 C 文件,它只是将它接收到的所有调用传递到我已经创建的大型 DLL 上。 C 文件负责初始化模块,但其他一切都在 DLL 中处理。它有效,但它似乎是一种非常糟糕的做事方式。我正在寻找的是一种更好的方法。
【问题讨论】:
-
那么 .pyd 在导入时是否有效?然后在外部构建它并仅从 setup.py 引用它是一个好主意(将其包含在 .whl 中 - 它应该将其安装在 站点包 目录)。但是您分享的文件很奇怪:它不是核心模块,然后是那些空函数定义,然后是所有
extern "C" __declspec(dllexport),并且PyInit函数也不存在。我不确定您打算从中导出什么。 -
也许stackoverflow.com/questions/49493537/… 或stackoverflow.com/questions/61692747/… 可能会有所帮助。我可以帮助你继续前进,但是这周我有点被抓住了。
-
我可以建议您查看
pybind11的绑定吗?这是一个很棒的库,它可能比自己编写绑定代码更容易。 -
在与 PythonInterface.cpp 相同的文件夹中有一个名为 bs_shims.c 的文件,我目前正在使用它。它与生成的 dll 一起放入 build 文件夹中,并由 setup.py 构建。它负责调用 PyInit,其中的模块定义将引用传递给 Dll 中的导出函数。