排除 setuptools/pip 中的某些依赖版本范围

Exclude certain dependency version ranges in setuptools/pip

目前 Django 项目支持 1.4、1.7 和 1.8。在我的 setup.py 中,我想将这些版本反映为受支持。

install_requires=['Django>=1.4.2,<1.8.99,!=1.5,!=1.6']

然而,这仍然允许 1.5.x 和 1.6.x 版本。我怎样才能排除一个完整的范围?

Setuptools lists以下有效要求为例:

PickyThing<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1

但是这不适用于 pip(它至少应该匹配 1.4.x / 1.5.x):

No matching distribution found for PickyThing!=1.9.6,<1.6,<2.0a0,==2.4c1,>1.9

更新示例
例如;我只想包括 currently supported versions of Django。这将是 1.4.x、1.7.x 和 1.8.x。所以我会写;

#setup.py
install_requires=['Django>=1.4.2,<1.4.99,>=1.7,<1.8.99']

但是如果我 运行 pip install -e . 在这个项目上,它失败了;

Collecting Django<1.4.99,<1.8.99,>=1.4.2,>=1.7 (from ...)
Could not find a version that satisfies the requirement Django<1.4.99,<1.8.99,>=1.4.2,>=1.7 (from django-two-factor-auth==1.2.0) (from versions: 1.1.3, 1.1.4, 1.2, 1.2.1, 1.2.2, 1.2.3, 1.2.4, 1.2.5, 1.2.6, 1.2.7, 1.3, 1.3.1, 1.3.2, 1.3.3, 1.3.4, 1.3.5, 1.3.6, 1.3.7, 1.4, 1.4.1, 1.4.2, 1.4.3, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.4.8, 1.4.9, 1.4.10, 1.4.11, 1.4.12, 1.4.13, 1.4.14, 1.4.15, 1.4.16, 1.4.17, 1.4.18, 1.4.19, 1.4.20, 1.5, 1.5.1, 1.5.2, 1.5.3, 1.5.4, 1.5.5, 1.5.6, 1.5.7, 1.5.8, 1.5.9, 1.5.10, 1.5.11, 1.5.12, 1.6, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.6.5, 1.6.6, 1.6.7, 1.6.8, 1.6.9, 1.6.10, 1.6.11, 1.7, 1.7.1, 1.7.2, 1.7.3, 1.7.4, 1.7.5, 1.7.6, 1.7.7, 1.7.8, 1.8a1, 1.8b1, 1.8b2, 1.8rc1, 1.8, 1.8.1)
No matching distribution found for Django<1.4.99,<1.8.99,>=1.4.2,>=1.7 (from ...)

显然版本号1.4.20不能满足>=1.7,1.8.1不能满足<1.4.99。然而,来自 Setuptools 的文档(见上文)确实表明沿着这些方向的东西应该是可能的。然而,这对我来说并不明显。

我阅读了pkg_resources的一些相关代码。我认为文档 here 不准确。不仅 pip 找不到正确的包版本,实际上使用 setuptoolspython setup.py install 也失败了。

部分相关代码:

pip/_vendor/packaging/specifiers.py

# If we have any specifiers, then we want to wrap our iterable in the
# filter method for each one, this will act as a logical AND amongst
# each specifier.
if self._specs:
    for spec in self._specs:
        iterable = spec.filter(iterable, prereleases=prereleases)
    return iterable

你可以看到,在评论中,作者强调这将导致每个说明符之间出现AND,而不是OR。所以如果你这样做:

PickyThing<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1

你什么也得不到!

我尝试使用以下代码:

import pkg_resources

a = ['1.4', '1.8', '1.9.2']
d = pkg_resources.Requirement.parse('PickyThing<1.6,>1.9,!=1.9.6')
r = d.specifier.filter(a)

print(list(r)) # Nothing, just an empty list []

您可能想向 pip 提交错误,以便他们修复它。

您可以使用

Django>=1.4.2,<1.9,!=1.5.*,!=1.6.*

这是在PEP440.

里面定义的

您可以使用最新版本的 setuptools 和 pip 中提供的 packaging 模块测试此行为。

In [1]: from packaging import specifiers

In [2]: sp=specifiers.SpecifierSet(">=1.4.2,<1.9,!=1.5.*,!=1.6.*")

In [3]: sp.contains("1.4.2")
Out[3]: True

In [4]: sp.contains("1.6.4")
Out[4]: False

In [5]: sp.contains("1.8.2")
Out[5]: True