klamann
klamann

Reputation: 1817

Python version number comparison with != (not equal)

I'm trying to write a function which replicates the behaviour of python_requires from setup.py, because I failed to find the implementation in the source code of setuptools and pip. Comparing version numbers in Python should be easy enough, I'm using packaging.version which does all the work for me, except...

Let's look at a version string like ">=2.6, !=3.0.*, !=3.1.*". I split at every comma, then look at each comparison individually:

from packaging import version
version.parse("3.0.5") >= version.parse("2.6")

Version 3.0.5 is greater than 2.6, as expected. Next:

version.parse("3.0.5") != version.parse("3.0.*")

This returns True (i.e. 3.0.5 is not the same as 3.0.*), but version 3.0.5 should actually match 3.0.*. Is there a standard way in Python to check if a version number with wildcards matches another version number?

edit: also related, how would I implement a compatible version matcher, like ~= 3.0.5, which should match any version >= 3.0.5 but < 3.1.0?

It looks like I'm trying to implement the version specifiers of PEP 440 here...

Upvotes: 2

Views: 4113

Answers (2)

Kirk M
Kirk M

Reputation: 151

If your end goal is just to compare version compatibility against a PEP 440 version specifier, it looks like packaging.specifiers already supports this:

>>> from packaging.specifiers import SpecifierSet
>>> vspec = SpecifierSet(">=11.3,!=11.3.2")
>>> "11.3.1" in vspec
True
>>> "11.3.2" in vspec
False
>>> 

The specifiers.py source may be helpful if you are really going for your own implementation. Looks like the general approach is to regex-match each operator, mapping them to appropriate Specifier methods where the comparison is deferred to the Version operators after any special handling (e.g., _compare_equal() handles the ".*" case you asked about).

Upvotes: 1

Kemp
Kemp

Reputation: 3649

As per the documentation packaging.version.parse is only intended to parse PEP 440 compatible version numbers, of which "3.0.*" is not one. The use of wildcards is called out in the PEP as being valid for use when matching but they are not actually part of the version scheme.

When packaging.version.parse is given an incompatible version number to parse it is returned as a packaging.version.LegacyVersion instead of a packaging.version.Version. You can confirm that this is what is being returned in your case. Presumably the operators work differently on these (possibly not implemented at all) and they have implemented some additional logic in the setup tools to handle LegacyVersion objects.

Upvotes: 2

Related Questions