我似乎无法解决使用子目录结构时导入C ++扩展模块不再起作用的问题。
下面的两个案例代表一个简单的工作案例,以及一个稍作改动的案例,我一生无法工作。
项目树:
demo_cext/ # Current working directory for all of this
├── _cmodule.cc
└── setup.py
setup.py
的内容:
import setuptools
module = setuptools.Extension("_cmod", sources=["_cmodule.cc"], language="c++")
if __name__ == "__main__":
setuptools.setup(name="cmod", ext_modules=[module])
_cmodule.cc
的内容,基本上是C扩展的世界,它创建不带参数且返回5的函数foo()
。
#define PY_SSIZE_T_CLEAN
#include <Python.h>
static PyObject *
foo(PyObject *self, PyObject *args) {
/* noargs() */
if (!PyArg_ParseTuple(args, "")) {
return NULL;
}
return PyLong_FromLong(5);
}
static PyMethodDef FooMethods[] = {
{"foo", foo, METH_VARARGS, "Do the foo"},
{NULL, NULL, 0, NULL}
};
PyDoc_STRVAR(module_doc, "This is the module docstring.");
static struct PyModuleDef cmodule = {
PyModuleDef_HEAD_INIT,
"cmod",
module_doc,
-1,
FooMethods,
NULL,
NULL,
NULL,
NULL
};
PyMODINIT_FUNC
PyInit__cmod(void) {
PyObject* m = PyModule_Create(&cmodule);
if (m == NULL) {
return NULL;
}
return m;
}
整个过程就像一个咒语:
$ python3 -V
Python 3.7.4
$ python3 setup.py build install
>>> import _cmod
>>> _cmod.foo()
5
重新调整项目布局:
$ rm -rf build/ dist/ cmod.egg-info/ && \
> mkdir cmod/ && touch cmod/__init__.py && \
> mv _cmodule.cc cmod/
留给我:
demo_cext/ # Current working directory for all of this
├── cmod
│ ├── __init__.py
│ └── _cmodule.cc
└── setup.py
我稍微更改setup.py
:
import setuptools
module = setuptools.Extension("cmod._cmod", sources=["cmod/_cmodule.cc"], language="c++")
if __name__ == "__main__":
setuptools.setup(name="cmod", ext_modules=[module])
现在再次运行:
$ python3 setup.py build install
尝试导入模块使我有了:
>>> from cmod import _cmod
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: cannot import name '_cmod' from 'cmod' (.../demo_cext/cmod/__init__.py)
>>> import cmod._cmod
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'cmod._cmod'
这里我有什么问题?我确信使用命名约定很简单。
[在启动Python之前尝试更改目录(例如cd ..
)。追溯表明您在cmod
(您的源目录,而不是安装目录)中找到了demo_cext/cmod
,但是构建的扩展名不存在(在demo_cext/build
之后的build
中,并且您的系统站点软件包目录install
之后。
如果只能在Python 3上使用,另一种解决方案是摆脱cmod/__init__.py
文件;多亏了__init__.py
,在Python 3中制作一个包并不需要一个空的PEP 420文件。通过删除__init__.py
,隐式名称空间包装将接管,它应自动在cmod
中的all sys.path
包中搜索您要导入的子模块。