packaging icon indicating copy to clipboard operation
packaging copied to clipboard

Specifier `<` that contains prerelease version does not match on valid prerelease versions

Open notatallshaw opened this issue 11 months ago • 3 comments

This relates to this part of the spec:

The exclusive ordered comparison <V MUST NOT allow a pre-release of the specified version unless the specified version is itself a pre-release. Allowing pre-releases that are earlier than, but not equal to a specific pre-release may be accomplished by using <V.rc1 or similar.

So, for example, <3.0.0a8 is a prerelease specifier, and therefore falls under "unless the specified version is itself a pre-release", I would expect it to then "Allowing pre-releases that are earlier than, but not equal to a specific pre-release", for example 3.0.0a7.

However that does not appear to be the case:

>>> from packaging.specifiers import Specifier
>>> Specifier('<3.0.0a8').contains("3.0.0a7")
False
>>> Specifier('<=3.0.0a8').contains("3.0.0a7")
True

This came up in https://github.com/astral-sh/uv/issues/2646 and I'm aware of the comment https://github.com/pypa/packaging/issues/776#issuecomment-1900515985, but the spec seems very explicit and even gives an examples that < allows prereleases when the version itself is a prerelease.

notatallshaw avatar Mar 26 '24 22:03 notatallshaw

Interestingly the spec talks about < in relation to prerelease, and > in releation to post releases, and I see > does follow the spec here:

>>> Specifier('>3.0.0post1').contains("3.0.0post2")
True

However, unless I am missing it, the spec fails to mention how to handle < with post releases, or > with prereleases, e.g. should >3.0.0a8 match 3.0.0a9 or <3.0.0post2 match 3.0.0post1?

notatallshaw avatar Mar 26 '24 22:03 notatallshaw

Personally I don’t see how those combinations should be treated differently from those explicitly mentioned in the spec.

uranusjr avatar Mar 26 '24 22:03 uranusjr

Personally I don’t see how those combinations should be treated differently from those explicitly mentioned in the spec.

In that case the common theme is that packaging is violating the spec for prereleases, but not for postreleases:

>>> Specifier('>3.0.0a8').contains("3.0.0a9")
False
>>> Specifier('<3.0.0post2').contains("3.0.0post1")
True

notatallshaw avatar Mar 26 '24 23:03 notatallshaw

Reading over the spec, it seems the change is a reasonable one.

@pradyunsg are you good w/ making this work? #794 looks to do the right thing and I think we should merge it, but I want to give you a chance to concur/disagree.

brettcannon avatar Sep 16 '24 16:09 brettcannon

Let's do this.

pradyunsg avatar Sep 16 '24 20:09 pradyunsg