使用PEP440中定义的版本说明符进行pip/setuptools/打包,以定义多个可能的范围

2022-05-27 00:00:00 python pip setuptools python-packaging

问题描述

根据Exclude certain dependency version ranges in setuptools/pip中的问题,我想知道是否有办法在版本说明符(在PEP440中定义)中使用多个范围来定义受支持的版本。

使用以下用例:

我有一个依赖项,我支持其版本1.x,如果x是<;3;则支持版本2.x;如果x大于1且小于或等于4,则支持版本2.x。v1.x系列仍然有效,并且将继续发布新版本。

提供测试用例:

import packaging.specifiers as s


def test_specifier_multiple_ranges():
    spec = s.SpecifierSet('THE ANSWER GOES HERE')

    # A dependency is supported for:
    #       versions 1.x, provided x is <3
    #       OR
    #       versions 2.x provided x is greater than 1 and less than or equal to 3
    # The v1.x series is still active, and new versions will continue to
    # be released.
    possible_versions = [
        '1.1', '1.2', '1.3', '1.4', '2.0', '2.1', '2.2', '2.3', '2.4', '3.0'
    ]
    expected =  ['1.1', '1.2', '2.2', '2.3']

    assert list(spec.filter(possible_versions)) == expected

请注意,我不是要排除特定的版本,必须通过上限范围排除它们-v1.x系列仍然有效&排除了这一点,因为v1.5(在上面的测试用例中)随时可能出现,这是已知不受支持的。

请注意,在阅读https://github.com/pypa/pip/issues/2744处的错误报告时,以及PEP440的语法定义(据我所见,它缺乏任何OR功能),我认为这是不可能的。如果这样的回答可以明确引用此限制(例如,在PEP440中),或者它来自权威来源,我将接受此回答。


解决方案

关于缺少OR功能的说法是正确的,version specifier中的逗号表示逻辑与:

逗号(";,";)等效于逻辑AND运算符:候选版本必须与所有给定版本子句匹配,才能与说明符作为整体匹配。

因此,PEP440中可用于排除多个内部&q;范围的唯一机制是前缀匹配。 请参阅规范的version matching部分:

默认情况下,版本匹配运算符基于严格的相等比较:指定的版本必须与请求的版本完全相同。

...

可以通过在版本匹配子句中的版本标识符后附加.*来请求前缀匹配,而不是严格的比较。这意味着在确定版本标识符是否与子句匹配时,将忽略附加的尾随段。

这并不能完全解决问题,除非您有关于1.x系列和2.x系列何时停止使用的其他信息。例如,如果您知道1.x的最终次要版本是1.13,而2.x将是2.7之后的EOL,则此前缀排除有效:

specifiers  = [f"!= 1.{n}.*" for n in range(3, 14)]
specifiers += [f"!= 2.{n}.*" for n in [0, 1, *range(4, 8)]]
specifiers.append("< 3")
spec = s.SpecifierSet(", ".join(specifiers))

在实践中,您可能只需猜测一些足够高的上限,因为软件项目不会无限期地将新功能发布到旧版本中。

相关文章