[Distutils] PEP345 - Distutils2 - Cannot pin to exact version

Erik Bray erik.m.bray at gmail.com
Tue Sep 11 21:25:02 CEST 2012


On Tue, Sep 11, 2012 at 3:19 PM, Daniel Holth <dholth at gmail.com> wrote:
> On Tue, Sep 11, 2012 at 3:11 PM, Erik Bray <erik.m.bray at gmail.com> wrote:
>> On Tue, Sep 11, 2012 at 2:35 PM, Donald Stufft <donald.stufft at gmail.com> wrote:
>>> I was digging through PEP386 & PEP345 today, and I noticed something odd
>>> about the wording of PEP345.
>>>
>>> It states:
>>>
>>>     When a version is provided, it always includes all versions that starts
>>> with the same value. For
>>>     example the "2.5" version of Python will include versions like "2.5.2"
>>> or "2.5.3". Pre and post
>>>     releases in that case are excluded. So in our example, versions like
>>> "2.5a1" are not included
>>>     when "2.5" is used. If the first version of the range is required, it
>>> has to be explicitly given. In
>>>     our example, it will be "2.5.0".
>>>
>>> It also states:
>>>
>>>     In that case, "2.5.0" will have to be explicitly used to avoid any
>>> confusion between the "2.5"
>>>     notation that represents the full range. It is a recommended practice to
>>> use schemes of the
>>>     same length for a series to completely avoid this problem.
>>>
>>> This effectively translates to an inability to pin to an exact version. Even
>>> in the case of specifying
>>> == it checks that the version "starts with" the value you selected. So if
>>> you pin to "2.5", and the
>>> author then releases "2.5.1", that will count as ==2.5. If you try to then
>>> pin to "2.5.0", and the
>>> author releases "2.5.0.1", then that will count as ==2.5.0.
>>>
>>> Essentially this translates to:
>>>
>>>     ==2.5       -> >=2.5<2.6
>>>     ==2.5.0    -> >=2.5.0<2.5.1
>>>     ==2.5.0.0 -> >=2.5.0.0<2.5.0.1
>>>
>>> Which means that version specifiers are _always_ ranges and are never exact
>>> versions. The PEP
>>> as written relies on authors to decide beforehand how many digits they are
>>> going to use in their
>>> versions, and for them to never increase or decrease that number.
>>>
>>> I also checked to see if Distutils2/packaging implemented VersionPredicates
>>> that way or if they
>>> allowed specifying an exact version. It turned out that it implements the
>>> PEP as written:
>>>
>>>>>> from distutils2 import version
>>>>>> predicate = version.VersionPredicate("foo (==2.5)")
>>>>>> print predicate
>>> foo (==2.5)
>>>>>> predicate.match("2.5")
>>> True
>>>>>> predicate.match("2.5.0")
>>> True
>>>>>> predicate.match("2.5.0.0")
>>> True
>>>>>> predicate.mach("2.5.0.5")
>>> True
>>
>> That's kind of annoying.  Does anyone know if this is by design?
>>
>> FWIW there is a workaround. For example if you want to pin to exactly 2.5.1:
>>
>>>>> predicate = version.VersionPredicate("foo (==2.5.1,<2.5.1.1)")
>>>>> predicate.match('2.5.1')
>> True
>>>>> predicate.match('2.5.2')
>> False
>>>>> predicate.match('2.5.1.0')
>> True
>>>>> predicate.match('2.5.1.1')
>
> But you could still release 2.5.1.0.0? I suppose we limit the number
> of version parts these days.
>
> Why don't we update the spec so that (2.0) means (2.0) the range, and
> (==2.0) means 2.0 (exactly).
>
> Daniel

The PEP is ambiguous on this, but you could get away with reading it
as "When a version is provided (without a conditional operator) it
always includes all versions that start with the same value".
Although it's unwritten in the PEP exactly how the operators are meant
to be interpreted, I would say they should be interpreted strictly.

Erik


More information about the Distutils-SIG mailing list