Python 子目录结构破坏了C++;扩展构建 我似乎无法解决一个问题:导入一个C++扩展模块在使用子目录结构时不再工作。

Python 子目录结构破坏了C++;扩展构建 我似乎无法解决一个问题:导入一个C++扩展模块在使用子目录结构时不再工作。,python,c++,python-3.x,python-c-api,Python,C++,Python 3.x,Python C Api,下面的两个案例展示了一个简单的工作案例和一个稍微改变的案例,我一生都无法工作 情景1(工作) 项目树: demo_cext/#所有这些的当前工作目录 ├── _cmodule.cc └── setup.py setup.py的内容: 导入设置工具 module=setuptools.Extension(“\u cmod”,sources=[”\u cmodule.cc“],language=“c++”) 如果名称=“\uuuuu main\uuuuuuuu”: setuptools.setup

下面的两个案例展示了一个简单的工作案例和一个稍微改变的案例,我一生都无法工作

情景1(工作) 项目树:

demo_cext/#所有这些的当前工作目录
├── _cmodule.cc
└── setup.py
setup.py的内容

导入设置工具
module=setuptools.Extension(“\u cmod”,sources=[”\u cmodule.cc“],language=“c++”)
如果名称=“\uuuuu main\uuuuuuuu”:
setuptools.setup(name=“cmod”,ext\u modules=[module])
\u cmodule.cc
的内容,基本上是C扩展的hello world,它创建了一个不带参数并返回5的函数
foo()

#定义PY\u-SSIZE\u-T\u-CLEAN
#包括
静态PyObject*
foo(PyObject*self,PyObject*args){
/*noargs()*/
if(!PyArg_ParseTuple(args,“”){
返回NULL;
}
从长(5)返回PyLong_;
}
静态PyMethodDef FooMethods[]={
{“foo”,foo,METH_VARARGS,“做foo”},
{NULL,NULL,0,NULL}
};
PyDoc_STRVAR(module_doc,“这是module docstring”);
静态结构PyModuleDef cmodule={
PyModuleDef_HEAD_INIT,
“cmod”,
模块(doc),
-1,
食物方法,
无效的
无效的
无效的
无效的
};
PyMODINIT_FUNC
PyInit_u_cmod(无效){
PyObject*m=PyModule_Create(&cmodule);
如果(m==NULL){
返回NULL;
}
返回m;
}
整个过程就像一个符咒:

$python3-V
Python 3.7.4
$python3 setup.py构建安装
导入 >>>_cmod.foo() 5. 情景2(中断) 将项目重新定向到中专门介绍的布局

$rm-rf构建/dist/cmod.egg-info/&\
>mkdir cmod/&&touch cmod/_u init__;uuuu.py&&\
>mv_cmodule.cc cmod/
留给我的是:

demo_cext/#所有这些的当前工作目录
├── cmod
│   ├── __初始值
│   └── _cmodule.cc
└── setup.py
我稍微更改了
setup.py

导入设置工具
module=setuptools.Extension(“cmod.\u cmod”,sources=[“cmod/\u cmodule.cc]”,language=“c++”)
如果名称=“\uuuuu main\uuuuuuuu”:
setuptools.setup(name=“cmod”,ext\u modules=[module])
再次运行后,现在:

$python3 setup.py构建安装
尝试导入模块会给我留下:

>>来自cmod导入\u cmod
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
ImportError:无法从“cmod”导入名称“\u cmod”(…/demo\u cext/cmod/\uuu init\uuuuu.py)
>>>导入cmod.\u cmod
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
ModuleNotFoundError:没有名为“cmod.\u cmod”的模块

我这里出了什么问题?我相信命名约定很简单。这似乎是直接面对的。

在启动Python之前尝试更改目录(例如
cd..
)。您的回溯表明您正在
demo_cext/cmod
中找到
cmod
(您的源目录,而不是安装目录),但构建的扩展将不在那里(它将位于
demo_cext/build
之后的
build
中的某个位置,以及
install
之后的系统站点软件包目录)


如果只在Python3上使用,另一种解决方案是去掉
cmod/\uuuu init\uuuu.py
文件;在Python 3中制作包不需要空的
\uuuuu init\uuuuuuuuuuuupy.py文件,这要归功于。通过删除
\uuuu init\uuuuuuuupy
,隐式命名空间包应该接管,并且它应该在
sys.path
中自动搜索所有
cmod
包以查找您试图导入的子模块。

@BradSolomon:那么隐式命名空间包将无法工作。但是对于包的实际用户,他们不会从具有
cmod/\uuuu init\uuuu.py
工作目录的运行,因此第一个解决方案(
cd
从您的开发目录中取出以测试安装的模块)将准确地再现您的用户体验。另一种解决方案主要是为了方便您,即开发人员,让Python搜索源目录和安装的包以查找
\u cmod
,而无需通过更改工作目录来绕过搜索源目录。@BradSolomon:问题不在于shell的
路径
,它是Python中的
sys.path
(Python在其中查找模块;hacky
PYTHONPATH
环境变量可以更改它,而不是
path
sys.path
隐式地以当前工作目录作为前缀,因此从
../demo\u cext
启动时,它将在查看已安装的软件包之前找到
../demo\u cext/cmod
。如果您想在
\uuu init\uuuuuuy.py
中进行自定义操作的同时不更改目录,那么您可以。@BradSolomon:不,Python文件很好,因为它们在源代码
cmod
和全局安装的
cmod
中是相同的。只有扩展有问题,因为它们只是源
cmod
中的原始C源文件,而实际的扩展模块只存在于全局安装的
cmod
中。创建一个显式名称空间模块应该仍然可以正常工作;我链接的答案会扩展它将查找
cmod
的子包/模块的位置(因此它仍然会首先检查源
cmod
,然后检查已安装的
cmod
,如果找不到的话)。通过创建一个简单的PyPI包,我能够完全验证您在这里指出的几乎所有内容:。它可以顺利安装pip,并允许访问Python和C模块。因此,正如你所指出的,困难出现了