如果在软件包根目录中,则无法导入C ++扩展名

布拉德·所罗门(Brad Solomon):

注意:由于提出了这个问题,后来我发现python -m pip install -e .它将安装扩展名cmod.venv/lib/python3.8/site-packages/hello-c-extension.egg-link指向当前目录中的项目。src在以后的提交中,我也切换到了布局,并发现https://pythonwheels.com/是分发轮子的高质量软件包的很好参考。但是,我仍然很想知道setup.py子命令的行为


作为对manylinux的一些研究的一部分,我正在使用一个玩具项目为C ++扩展模块构建不同的平台轮。

似乎在本地构建和安装时,如果当前目录为项目根目录则无法导入C ++扩展模块。这使我无法运行单元测试等。我相信这样做的原因是.成为的第一个组件sys.path,因此可以选择纯Python版本,而不能使用已编译的扩展。

我怎样才能解决这个问题?我是否正确运行本地构建/安装?

包结构如下所示:

$ tree hello-c-extension/
hello-c-extension/
├── LICENSE
├── Makefile
├── README.md
├── cmod
│   ├── __init__.py
│   ├── _cmodule.cc
│   └── pymod.py
├── setup.py
└── tests
    ├── __init__.py
    └── test_cext.py

我在GitHub上也有这个项目;29fef5b开始,我问了这个问题

要构建/安装,我使用:

cd hello-c-extension
python -m venv .venv
source ./.venv/bin/activate
python -m pip install -U pip wheel setuptools
python setup.py build install

现在,从当前目录中,我可以导入Python模块,但不能导入相应的扩展模块。Python模块将作为当前目录中的模块,而不是在site-packages

$ python -c 'from cmod import pymod; print(pymod)'
<module 'cmod.pymod' from '/Users/brad/Scripts/python/projects/bsolomon1124/hello-c-extension/cmod/pymod.py'>
$ python -c 'from cmod import _cmod; print(_cmod)'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: cannot import name '_cmod' from 'cmod' (/Users/brad/Scripts/python/projects/bsolomon1124/hello-c-extension/cmod/__init__.py)

恶意删除了PWD元素的sys.path修复程序:

>>> import sys
>>> sys.path
['', '/Users/brad/.pyenv/versions/3.8.1/lib/python38.zip', '/Users/brad/.pyenv/versions/3.8.1/lib/python3.8', '/Users/brad/.pyenv/versions/3.8.1/lib/python3.8/lib-dynload', '/Users/brad/Scripts/python/projects/bsolomon1124/hello-c-extension/.venv/lib/python3.8/site-packages', '/Users/brad/Scripts/python/projects/bsolomon1124/hello-c-extension/.venv/lib/python3.8/site-packages/hello_c_extension-0.4-py3.8-macosx-10.15-x86_64.egg']
>>> del sys.path[0]
>>> from cmod import _cmod; print(_cmod)
<module 'cmod._cmod' from '/Users/brad/Scripts/python/projects/bsolomon1124/hello-c-extension/.venv/lib/python3.8/site-packages/hello_c_extension-0.4-py3.8-macosx-10.15-x86_64.egg/cmod/_cmod.cpython-38-darwin.so'>

最后,更改目录也会使问题消失:

$ cd ..
$ python -c 'from cmod import _cmod; print(_cmod)'
<module 'cmod._cmod' from '/Users/brad/Scripts/python/projects/bsolomon1124/hello-c-extension/.venv/lib/python3.8/site-packages/hello_c_extension-0.4-py3.8-macosx-10.15-x86_64.egg/cmod/_cmod.cpython-38-darwin.so'>

这是真的吗?它应该如何工作?在这种情况下,为扩展模块运行单元测试的正确方法是什么?

系统信息:

$ python -V
Python 3.8.1
$ uname -mrsv
Darwin 19.4.0 Darwin Kernel Version 19.4.0: Wed Mar  4 22:28:40 PST 2020; root:xnu-6153.101.6~15/RELEASE_X86_64 x86_64
读者:

问题的原因

这是真的吗?它应该如何工作?在这种情况下,为扩展模块运行单元测试的正确方法是什么?

从导入Python sys.path这意味着它将在每个目录中搜索导入,从索引0到中的最后一项sys.path

请注意,采用修正的方法是在导入之前添加以下内容:sys.path.append(sys.path.pop(0))这仍然允许本地导入,但是意味着site-packages首先搜索和标准库。

我怎样才能解决这个问题?我是否正确运行本地构建/安装?

我将在下面回答第一个问题。

是的,您的构建/安装很好,但是要解决导入问题,您需要对其进行一些微调。

解决问题

切换到src/cmod布局。一个伟大的链接-这是指出了自己在问题的意见-是在这里

评论中讨论了这种方法,并且由于提出了问题,您实际上已经实现了此方法。60093f1)。

我现在引用其中的一些文章;它比我更好地解释了这一点。

src目录是一种更好的方法,因为:

  • 您将获得进口平价。当前目录隐式包含在sys.path中;但是在从站点程序包安装和导入时不是这样。用户将永远不会拥有与您相同的当前工作目录。

此约束在测试和包装中均具有有益的含义:

  • 您将被迫测试已安装的代码(例如:通过在virtualenv中安装)。这将确保已部署的代码正常工作(已正确打包)-否则测试将失败。早。在发布损坏的发行版之前。

  • 您将被迫安装发行版。如果您曾经在PyPI上上传了缺少模块或依赖项损坏的发行版,那是因为您没有测试安装。仅仅能够成功构建sdist并不能保证它将实际安装!

而且pip install -e .不再乱七八糟的东西了

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章