Discrepancy between PEP-440 and behavior of `packaging`, regarding prefix matching?


according to PEP-440:

For purposes of prefix matching, the pre-release segment is considered to have an implied preceding ., so given the version 1.1a1, the following clauses would match or not as shown:

== 1.1.*      # Same prefix, so 1.1a1 matches clause

However, when I test this example with the relevant tools from packaging, the outcome is different than the above:

$ pip install --upgrade packaging
Successfully installed packaging-23.1
$ python
Python 3.9.9 (main, Dec 22 2021, 01:10:49)
>>> from packaging.specifiers import SpecifierSet
>>> from packaging.version import Version
>>> s = SpecifierSet('== 1.1.*')
>>> v = Version('1.1a1')
>>> s.contains(v)

Is PEP-440 or packaging right? Or, maybe, what am I missing? :slight_smile:


You’re not supplying the prereleases argument, so the specifier won’t match prereleases by default.

1 Like

Thanks for the reply, Paul!

I felt I must be missing something… :slight_smile:

Am I correct, that the prereleases=True behavior is used – in practice, by such tools as pip and setuptools – when there is no available non-prerelease version or the user explicitly demands on using prereleases?


I don’t recall the details of what pip does offhand (feel free to go and check the code yourself if you want :wink:) but how tools should handle prereleases is covered in PEP 440.

1 Like


Hm… But then it seems that this fragment of PEP 440 is not correct:

== 3.1.*: any version that starts with 3.1. Equivalent to the ~=3.1.0 compatible release clause.’

(PEP 440 – Version Identification and Dependency Specification | peps.python.org)

– considering:

>>> s1 = SpecifierSet('== 3.1.*')
>>> s2 = SpecifierSet('~= 3.1.0')
>>> s1.contains('3.1a1', prereleases=True)
>>> s2.contains('3.1a1', prereleases=True)

(or am I missing something again? :open_mouth:)

I think that example is not quite right. PEP 440 – Version Identification and Dependency Specification | peps.python.org says:

For a given release identifier V.N, the compatible release clause is approximately equivalent to the pair of comparison clauses:

>= V.N, == V.*

So the equivalent set would actually be:

>>> s3 = SpecifierSet('>=3.1.0, ==3.1.*')
>>> s3.contains('3.1a1', prereleases=True)


>>> Version("3.1a1") < Version("3.1.0")
1 Like

I think that example is not quite right.

I agree it is not, and please note that this example is from the PEP itself.

What I am trying to say is that the fragment of the PEP quoted by me (i.e., the following bullet point in the Examples section: == 3.1.*: any version that starts with 3.1. Equivalent to the ~=3.1.0 compatible release clause.) – seems to contradict the specification in earlier parts of the document.

Yes, I’m saying that I think the example in the PEP is wrong, we’re saying the same thing.

1 Like