问题
这个特定的模块 (python-apt) 是 only available on PyPi with version 0.7.8。然而,这个版本似乎是一个错误!
python-apthas stated the following 的开发人员和 Debian 软件包维护者之一:
啊,又不是这整个 PyPI 的事情了。从来没有人正式
上传python-apt 那里。它与 APT 紧密耦合,并且
不应该以除通过以外的任何方式分发
Debian 软件包。
没有,也从来没有对 PyPi 的任何支持。我可以
说我对在那里复制工作完全没有兴趣。
Source: Debian "Deity" Mailing List 2016-11-22 msg#00094
您可以从 apt 安装 python-apt,我们不会在 pip 上提供 python-apt。我最近控制了 pypi 条目,需要对其进行处理。不过,我并不热衷于在发行版之外提供python-apt(python-apt 和 apt 版本 x.y 需要匹配),所以我宁愿摆脱它,所以人们会停止对过时版本的问题。
Source: python-apt#1883451
因此,至少对于这种依赖关系,在通过pip + PyPi 以 python-native 方式解决依赖关系时,我们似乎不走运。幸运的是,上游项目托管在salsa.debian.org GitLab 实例上,pip 现在支持git+ SCM url,以及其他选项。
解决方案:
通常,有很多解决方案可以解决这种依赖关系。您要解决的问题是:
- 这个包是从哪里提供的?
- 系统操作系统包管理器
- 任意上游发布 URL
- 开发 SCM 回购
- 带有错误修复的分叉 Git 存储库
- 等等……
- 您将安装哪个版本?
- 兼容性问题:
- 开发:
- 只需使用最新最好的(最前沿)
- 为我的平台或系统使用特定版本(例如:系统操作系统包、我在开发目录中的本地分叉版本)
- 质量保证/测试:
- 针对特定版本进行测试
- 针对最新版本进行测试(例如:夜间版本)
- 针对系统操作系统版本提供的版本进行测试
- 应该如何解决依赖关系?
- “抽象”依赖于
pip-module-name + version constraint
- 允许稍后使用 where 收集(URL / PyPi / Artifactory)和 what允许版本满足约束。
- 如果需要,用户可以随时通过为
pip 指定参数、使用特定的virtualenv 进行安装等来覆盖这些...
- “具体”对特定 URL + 包名 + 版本的依赖
- 极端情况:锁定到特定 URL + 版本 +
sha1 / sha256 / sha** 并通过校验和验证确保准确的位置和文件完整性。
- 不太灵活,但最有把握锁定到一个准确和精确的版本和来源。
- 您是在开发“应用程序”还是“库”/Python 模块?
- 是否需要通过
pip 使用setup.py install_requires = [...] 样式解析来安装依赖项? (图书馆)
- 依赖项是否需要由应用程序安装程序通过
pip install -r requirements.txt 安装? (应用)
- 您的项目将如何发布?
- 谁将安装软件包,他们的系统将如何解决依赖关系?
- 这是作为库在 PyPi 上发布,还是在其他地方作为应用程序发布? (遵循一些经验法则)
- 一般来说:
- 库往往希望有广泛开放的版本说明符
- 应用程序需要非常具体的依赖项以确保稳定性(大量依赖项意味着大量未经测试的版本排列!)
- 使用
setup.py 指定 PyPi 的库依赖项
- 使用
requirements.txt 指定应用程序的依赖项
- 这是否会被打包为原生操作系统包? (例如:
.deb、.rpm、.apk 等...)
- 本机包管理器也有依赖解析...也许使用它来确保本机兼容性!
- 您的软件包还支持哪些其他操作系统平台以及这些平台将如何解决依赖关系?
因此,我们通常可以看到,存在各种与所需的特殊性相关的问题,即这些依赖项在何处以及如何解决和安装。这里没有“一刀切”的解决方案......有利有弊,只有许多不同的解决方案属于以下范围:
more specific <---------------------> less specific
reliable compatibility reliable installability
less testing permutations more (possibly un-vetted) testing permutations
limited platform support more platform support (when more permutations are well tested)
dependable known configurations less dependable known configurations
less platform tolerant more tolerant and agnostic of platforms
more OS native less OS native
来自 GitLab 上游回购的最新和最伟大的
python-apt 包问题的一个解决方案是在requirements.txt 中使用此git+ URL 功能。这非常适合针对来自 GitLab 的 python-apt 的上游版本进行开发。为了进一步将安装与系统操作系统提供的python-apt 版本隔离开来,可能需要virtualenv 或pip install --user。例如:
requirements.txt:
--index-url https://pypi.python.org/simple/
-e git+https://salsa.debian.org/apt-team/python-apt.git#egg=python-apt
-e .
这可以被带有setup.py 的示例项目使用,其中包含:
[...SNIP...] # Boilerplate stuff here
setup(
#[...SNIP...] # Other setup() args here
platforms=['linux'],
# Reference:
# - https://github.com/pypa/interoperability-peps/pull/30/files#r184839487
# sudo apt install python3-apt apt-rdepends apt
# os_requires=[
# ['python3-apt', type='packagename', target='run', os='ubuntu'],
# ['apt-rdepends', type='packagename', target='run', os='ubuntu'],
# ['apt', type='packagename', target='run', os='ubuntu']
# ['libapt-pkg-dev', type='packagename', target='build', os='ubuntu']
# ]
# Build-deps for apt-python via git SCM: sudo apt install libapt-pkg-dev
python_requires='>=3.5',
install_requires=[
'python-apt (>= 2.0)',
# rest of your dependencies here
#[... SNIP ...]
],
package_dir={'': 'lib'},
scripts=_glob('bin/*'),
#[...SNIP...]
)
注意:os_requires isn't actually supported yet, but is proposed for a PEP。这可能有助于将来对包的外部依赖。在这种情况下,python 模块不是通过 PyPi / pip 分发,而是仅通过操作系统上的 apt / .deb 包提供的情况下会有所帮助。
如果您愿意,请设置您的 virtualenv 或使用 pip3 install --user,然后继续。
在运行pip3 install -r requirements.txt 时,使用git+ 要求功能会导致以下结果:
$ pip3 install -r requirements.txt
Looking in indexes: https://pypi.python.org/simple/
Obtaining file:///../example-project (from -r requirements.txt (line 4))
Obtaining python-apt from git+https://salsa.debian.org/apt-team/python-apt.git#egg=python-apt (from -r requirements.txt (line 3))
Updating ./example-project-venv/src/python-apt clone
Running command git fetch -q --tags
Running command git reset --hard -q c97d4159beae2f9cd42d55d3dff9c37f5c69aa44
ERROR: example-project 0.0.1 has requirement python-apt>=2.0, but you'll have python-apt 0.0.0 which is incompatible.
Installing collected packages: python-apt, example-project
Running setup.py develop for python-apt
Running setup.py develop for example-project
Successfully installed example-project python-apt
注意:您可能需要首先为python-apt 安装运行时和构建/setup.py 依赖项:
# Runtime deps (e.g.: Ubuntu 20.04 needs python3-apt, <20.04 needs python-apt):
sudo apt install python3-apt apt
# python-apt pip install deps (also for setup.py / development)
sudo apt install libapt-pkg-dev
替代方案:dependency_links(注意:可能已弃用)
如果您正在开发库类型模块,并且还希望使用 GitLab 作为 python-apt 的源代码,您可能需要考虑在 setup.py 中提供 using dependency_links 以提供 git+ 或http(s) tarball 发布 URL,而不是 requirements.txt。这有助于区分“应用程序”python 项目和“库”python 模块项目。这一切都取决于您的项目的安装过程是什么样的。 (例如:您想要pip install -r requirements.txt,还是只是pip install example-module,或python[3] setup.py {sdist,bdist,bdist_rpm, etc...}。为python-apt 的分叉版本指定自定义URL 也可能会有所帮助。但是,此方法可能很快就会被弃用(如果不是部分已经出现在 pip 的新版本中)。您可能需要考虑其他选项来确保您的依赖规范,例如 PEP 508 或pip install --find-links ... 代替。
此外,"application" vs "library" distinction 在这里也很重要,“抽象”与“具体”依赖关系的概念也是如此。一个简短的总结可能是:
抽象和具体之间的这种划分是一个重要的划分。它是
什么允许 PyPI 镜像基础设施工作。这是什么
允许公司托管自己的私有包索引。甚至是
是什么使您能够分叉库以修复错误或添加功能以及
使用你自己的叉子。因为抽象依赖是一个名字和一个
可选版本说明符,您可以从 PyPI 或从
Crate.io,或来自您自己的文件系统。你可以分叉一个库,改变
代码,只要它有正确的名称和版本说明符
该库会很乐意继续使用它。
Setuptools 具有类似于 Go 示例的功能。它被称为
依赖链接,它看起来像这样:
setup(
# ...
dependency_links = [
"http://packages.example.com/snapshots/",
"http://example2.com/p/bar-1.0.tar.gz",
], ) ```
This “feature” of setuptools removes the abstractness of its
dependencies and hardcodes an exact url from which you can fetch the
dependency from. Now very similarly to Go if we want to modify
packages, or simply fetch them from a different server we’ll need to
go in and edit each package in the dependency chain in order to update
the dependency_links.
Source: caremad.io Blog Post: setup.py vs requirements.txt
对于这个python-apt 示例,我们可能会使用这样的东西来锁定v2.0.0 上的“具体依赖项”:
setup(
# [...SNIP...]
dependency_links = [
"https://salsa.debian.org/apt-team/python-apt/-/archive/2.0.0/python-apt-2.0.0.tar.gz#egg=python-apt"
],
# [...SNIP...]
) `
注意:这个“错误功能”是 briefly removed,然后是 brought back,在指定私有包依赖 URL 时给出了 some usefulness。但是,目前 pip --process-dependency-links 标志已被弃用,因此它的用处可能仅限于旧版本的 Python 2 + pip。
Newer versions of pip now have URL support for PEP 508 syntax。这可能是使用复杂语法指定具体和抽象依赖关系的最面向未来的方法(有关详细信息,请参阅PEP 508)。现在可以通过多种方式指定包,包括自定义 URL。
例如,使用可选的sha256 校验和将python-apt 锁定到v2.0.0:
setup(
# [...SNIP...]
install_requires=[
'python-apt@https://salsa.debian.org/apt-team/python-apt/-/archive/2.0.0/python-apt-2.0.0.tar.gz#sha256=1ddbd3eb7cbc1ded7e0e8a2dd75219f0c59c7e062c6e6bfd5c8ff6f656c59a4e',
# [...SNIP...]
],
# [...SNIP...]
)
requirements.txt:
--index-url https://pypi.python.org/simple/
-e .
那么,pip install -r requirements.txt 仍然可以在没有任何额外标志的情况下工作:
$ ./example-project-venv/bin/python3 ./example-project-venv/bin/pip3 install -r requirements.txt
Looking in indexes: https://pypi.python.org/simple/
Obtaining file://./src/pub/example-project (from -r requirements.txt (line 4))
Requirement already satisfied: graph-tools>=1.5 in ./example-project-venv/lib/python3.8/site-packages (from example-project==0.0.1->-r requirements.txt (line 4)) (1.5)
Collecting python-apt@ https://salsa.debian.org/apt-team/python-apt/-/archive/2.0.0/python-apt-2.0.0.tar.gz#sha256=1ddbd3eb7cbc1ded7e0e8a2dd75219f0c59c7e062c6e6bfd5c8ff6f656c59a4e
Using cached https://salsa.debian.org/apt-team/python-apt/-/archive/2.0.0/python-apt-2.0.0.tar.gz (458 kB)
Building wheels for collected packages: python-apt
Building wheel for python-apt (setup.py) ... done
Created wheel for python-apt: filename=python_apt-0.0.0-cp38-cp38-linux_x86_64.whl size=2040980 sha256=79eeb0d1bb9e3c9785acb68f164a3f72a5777539137d180e9ded7558d2547a49
Stored in directory: ~/.cache/pip/wheels/c4/09/b5/36fc8c9a1ebe8786620db922f1495da200dce187ee7c618993
Successfully built python-apt
Installing collected packages: python-apt, example-project
Attempting uninstall: example-project
Found existing installation: example-project 0.0.1
Uninstalling example-project-0.0.1:
Successfully uninstalled example-project-0.0.1
Running setup.py develop for example-project
Successfully installed example-project python-apt-0.0.0
替代方案:pip install --find-links ...
另一种安装锁定到特定版本的“具体依赖项”的替代方法是将--find-links 传递给pip install,并使用已发布的tarball 文件。在给定发布 URL 的情况下,此方法可能有助于显式安装特定版本。比如使用python-aptv2.0.0:
$ ./example-project-venv/bin/python3 ./example-project-venv/bin/pip3 install --find-links 'https://salsa.debian.org/apt-team/python-apt/-/archive/2.0.0/python-apt-2.0.0.tar.gz' -r requirements.txt
Looking in indexes: https://pypi.python.org/simple/
Looking in links: https://salsa.debian.org/apt-team/python-apt/-/archive/2.0.0/python-apt-2.0.0.tar.gz
Obtaining file://./example-project (from -r requirements.txt (line 4))
Requirement already satisfied: graph-tools>=1.5 in ./example-project-venv/lib/python3.8/site-packages (from example-project==0.0.1->-r requirements.txt (line 4)) (1.5)
Collecting python-apt>=2.0
Downloading https://salsa.debian.org/apt-team/python-apt/-/archive/2.0.0/python-apt-2.0.0.tar.gz (458 kB)
|████████████████████████████████| 458 kB 614 kB/s
WARNING: Requested python-apt>=2.0 from https://salsa.debian.org/apt-team/python-apt/-/archive/2.0.0/python-apt-2.0.0.tar.gz (from example-project==0.0.1->-r requirements.txt (line 4)), but installing version 0.0.0
Building wheels for collected packages: python-apt
Building wheel for python-apt (setup.py) ... done
Created wheel for python-apt: filename=python_apt-0.0.0-cp38-cp38-linux_x86_64.whl size=2040783 sha256=d0a8f88c04f202e948b9855837140517d9b2bd3cef72e626221614552a476780
Stored in directory: ~/.cache/pip/wheels/8a/07/e9/b3c3328bac08c030a5b1e754e01e327b62fd26f9baedf07c15
Successfully built python-apt
ERROR: example-project 0.0.1 has requirement python-apt>=2.0, but you'll have python-apt 0.0.0 which is incompatible.
Installing collected packages: python-apt, example-project
Attempting uninstall: python-apt
Found existing installation: python-apt 0.0.0
Uninstalling python-apt-0.0.0:
Successfully uninstalled python-apt-0.0.0
Attempting uninstall: example-project
Found existing installation: example-project 0.0.1
Uninstalling example-project-0.0.1:
Successfully uninstalled example-project-0.0.1
Running setup.py develop for example-project
Successfully installed example-project python-apt-0.0.0
基本系统 Debian 软件包
在 Debian 和 Ubuntu 上,您会在各种发行版本上看到两个 .deb 软件包:python3-apt 和 python-apt(适用于 Python2)。
These packages are managed 由 APT 包管理器安装,因此安装在系统位置:/usr/lib/python3/dist-packages 或 /usr/lib/python2.7/dist-packages 分别用于 Python3 和 Python2.7。
这个dist-packages 路径和其他Python 打包约定是explained well in this post:
系统已经在每个Python版本的全局dist-packages目录下安装了Python包并创建了符号链接:
/usr/lib/python2.7/dist-packages/numpy
/usr/lib/python3/dist-packages/numpy
ls -ls /usr/include/numpy
#-> ../lib/python2.7/dist-packages/numpy/core/include/numpy
ls -l /usr/include/python2.7/numpy
#->../../lib/python2.7/dist-packages/numpy/core/include/numpy
ls -l /usr/include/python3.5/numpy
#-> ../../lib/python3/dist-packages/numpy/core/include/numpy
注意dist-packages 的使用好,而不是site-packages,应该为系统Python 保留。
因此,如果您希望使用python3-apt 的基本操作系统系统级别版本,那么您需要确保此路径位于您的sys.path 或PYTHONPATH 上,这样import apt 才能工作.然而,如果您想使用site-packages 位置或virtualenv 位置...这些位置必须存在于sys.path / PYTHONPATH 上。
不幸的是,as mentioned before,目前还没有官方的方式来说明对提供特定版本 python 模块的 OS 包的依赖。但是,只要您管理 python 运行时环境的 import 路径,您就应该能够使用位于 dist-packages 目录中的操作系统包中的正确版本。