有没有办法以编程方式确认 python 包版本是否满足需求说明符?



我正在尝试查找是否有一种方法可以获取已安装的软件包和版本并检查它是否满足需求规范。

例如,如果我有包pip==20.0.2,我希望程序执行以下操作:

CheckReqSpec("pip==20.0.2", "pip>=19.0.0")  -> True
CheckReqSpec("pip==20.0.2", "pip<=20.1")    -> True
CheckReqSpec("pip==20.0.2", "pip~=20.0.0")  -> True
CheckReqSpec("pip==20.0.2", "pip>20.0.2")   -> False

我发现 pkg_resources.extern.packaging 有 version.parse,这对于比较大于或小于的不同版本很有用,但需求规范可能非常复杂,并且像 ~= 这样的运算符不是标准的数学运算符。

安装工具文档有这个例子:

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

是否有现有的方法来执行此检查,或者有简单的方法来制作自己的检查?

编辑: ~= 尤其困难,特别是当规范作为变量输入时。* 在版本要求也很难弄清楚,因为

version.parse("20.0.*") == version.parse("20.0.1") # False
version.parse("20.0.*") < version.parse("20.0.0")  # True 
version.parse("20.0.*") < version.parse("20.1.1")  # True 
version.parse("20.0.*") >= version.parse("20.0.0") # False

现在已弃用pkg_resources(从setuptools(作为API,并将在导入时引起警告:

$ python3 -W always -c 'from pkg_resources import Requirement'
<string>:1: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html

相反,我们可以使用packaging解析需求(无论如何pkg_resources内部都在使用(。检查要求名称与相等比较匹配,并且检查要求版本在使用in的说明符集中:

>>> from packaging.requirements import Requirement
>>> req = Requirement("pip~=20.0.0")
>>> pin = "pip==20.0.2"
>>> name, version = pin.split("==")
>>> name == req.name and version in req.specifier
True

发布后工作。必须明确选择加入预发布。

>>> "20.0.0post1" in req.specifier
True
>>> req.specifier.contains("20.0.1b3")
False
>>> req.specifier.contains("20.0.1b3", prereleases=True)
True

注意:顶级packaging安装的版本可能与 pip 供应商和内部使用的打包版本不同。如果您需要保证打包 API 与 pip 的行为完全匹配,您可以直接从 pip 的供应商子包中导入Requirement类型:

from pip._vendor.packaging.requirements import Requirement

或者,如果从私有子模块导入让您感到害怕,则将顶层打包安装到与您的 pip 版本当前供应商完全相同的版本。检查您的 pip 版本(带pip --version(,然后检查 pip 供应商的相应打包版本。例如,如果您的 pip 版本是 23.2.1,您可以签入:

https://github.com/pypa/pip/blob/23.2.1/src/pip/_vendor/vendor.txt

在这里,您将看到 pip==23.2.1 供应商在 package==21.3 的旧版本。

也许packaging

from packaging import version
version.parse("20.0.2") > version.parse("19.0.0")   # True
version.parse("20.0.2") <= version.parse("20.1")    # True
version.parse("20.0.2") >= version.parse("20.0.0")  # True
version.parse("20.0.2") > version.parse("20.0.2")   # False

我会推荐packaging,它可以像下面这样使用:

>>> import packaging.requirements
>>> import packaging.version
>>> packaging.version.parse('20.0.2') in packaging.requirements.Requirement('pip>=19.0.0').specifier
True
>>> packaging.version.parse('20.0.2') in packaging.requirements.Requirement('pip~=20.0').specifier
True
>>> packaging.requirements.Requirement('pip==20.0.*').specifier.contains('20.0.2')
True
>>> packaging.requirements.Requirement('pip==20.0.*').specifier.contains('21')
False
>>> packaging.requirements.Requirement('PickyThing<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1').specifier
<SpecifierSet('!=1.9.6,<1.6,<2.0a0,==2.4c1,>1.9')>

相关内容

最新更新