python - 如果在包根目录中,则无法导入 C++ 扩展
问题描述
注意:自从提出这个问题后,我后来发现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
.
我怎样才能解决这个问题?我是否正确运行本地构建/安装?
包结构如下所示:
$ 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
.
请注意,修复的一个hacky方法是在导入之前添加以下内容:sys.path.append(sys.path.pop(0))
. 这仍然允许本地导入,但意味着site-packages
首先搜索标准库。
我怎样才能解决这个问题?我是否正确运行本地构建/安装?
下面我回答第一个问题。
是的,您的构建/安装很好,但要解决导入问题,您需要稍微调整一下。
解决问题
切换到src/cmod
布局。一个很好的链接——你自己在问题评论中指出——在这里。
评论中讨论了这种方法,并且自从提出问题以来,您实际上已经实现了这一点。( 60093f1 )。
我现在将引用那篇文章的一些内容;它比我能更好地解释这一点。
src 目录是更好的方法,因为:
- 你得到进口平价。当前目录隐式包含在 sys.path 中;但从站点包安装和导入时并非如此。用户永远不会拥有与您相同的当前工作目录。
这种约束对测试和包装都有有益的影响:
您将被迫测试已安装的代码(例如:通过安装在 virtualenv 中)。这将确保部署的代码正常工作(正确打包) - 否则您的测试将失败。早期的。在您发布损坏的发行版之前。
您将被迫安装发行版。如果您曾经在 PyPI 上上传了缺少模块或依赖项损坏的发行版,那是因为您没有测试安装。仅仅能够成功构建 sdist 并不能保证它会实际安装!
并且pip install -e .
不会再乱七八糟了。
推荐阅读
- bash - Makefile:将前缀添加到任何内容
- d3.js - d3v4 散点图 - 修复域添加动画
- php - 如何将通知从 php 服务器端发送到 Visual Basic 客户端,如 android
- python - 如何在迭代三个列表以匹配单词后创建 Python 字典
- swift - 如何处理 swift 模型类的额外参数
- c# - Unity 错误:“Animator”不包含“Setbool”的定义
- regex - 识别单个表达式的正则表达式
- r - 'group by' 中的错误,说明它是一个用于循环汇总 R 的字符
- django - 从我的模型中获取特定数据 - Django
- java - 如何将动作事件添加到 JavaFX 文本字段以获得焦点?