Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unreasonable comparisons #320

Closed
torsava opened this issue Jul 10, 2020 · 5 comments
Closed

Unreasonable comparisons #320

torsava opened this issue Jul 10, 2020 · 5 comments

Comments

@torsava
Copy link

torsava commented Jul 10, 2020

Hi,
we have found that packaging evaluates some comparisons unreasonably / illogically.

I'll use this format:

(version, operator, version, comparison_evaluation),

For these comparisons, packaging ignores equality and evaluates as if the operator was simply <:

('2.4.8', '<=', '2.4.8.*', False),
('2.4.8.0', '<=', '2.4.8.*', False),
('2.4.8.1', '<=', '2.4.8.*', False),
('2.4.8.post1', '<=', '2.4.8.*', False),

And in this set, also the > operator is evaluated wrong:

('2.4.8', '>', '2.4.8.*', True),
('2.4.8.0', '>', '2.4.8.*', True),
('2.4.8.1', '>', '2.4.8.*', True),
('2.4.8b5', '>', '2.4.8.*', True),
('2.4.8.post1', '>', '2.4.8.*', True),

Both these comparisons are undefined according to PEP 440. However, as someone will surely be using them, tests have been added for them in the Python-to-RPM-version project pyreq2rpm, see PR gordonmessmer/pyreq2rpm#7.

The tests currently verify that the comparisons are consistent with this illlogical upstream behaviour. But of course, an even better solution would be to fix the behaviour.

@hroncok
Copy link
Contributor

hroncok commented Jul 10, 2020

A practical example:

$ pip install -U 'pip<=20.1.*'
...
Successfully installed pip-20.0.2

As much as I wrap head my around this, i would expect both 20.1.1 and 20.1 to satisfy the requirement.

@di
Copy link
Member

di commented Jul 10, 2020

It's not very clear from the PEP, but prefix matching (using .*) is only supported for strict equality operators (== and !=). This is why prefix matching is only mentioned in the "Version matching" and "Version exclusion" sections.

See also these portions of the source:

# All other operators only allow a sub set of what the
# (non)equality operators do. Specifically they do not allow
# local versions to be specified nor do they allow the prefix
# matching wild cards.

# Prefix matching on operators which don't support them
"~=1.0.*",
">=1.0.*",
"<=1.0.*",
">1.0.*",
"<1.0.*",

@gordonmessmer
Copy link

I think the problem is that prefix matching on ordered comparisons aren't defined, but they are supported as best I can tell. Miro posted an example of their use, above.

If I create a mostly-empty module whose setup.py includes this line:

requirements = ["pip>=20,<=20.1.*"]

then:

$ python3 -m venv utest_env
$ . utest_env/bin/activate
$ pip3 install ./utest
Processing ./utest
Collecting pip<=20.1.*,>=20
  Downloading https://files.pythonhosted.org/packages/54/0c/d01aa759fdc501a58f431eb594a17495f15b88da142ce14b5845662c13f3/pip-20.0.2-py2.py3-none-any.whl (1.4MB)
Installing collected packages: pip, utest
  Found existing installation: pip 19.3.1
    Uninstalling pip-19.3.1:
      Successfully uninstalled pip-19.3.1
    Running setup.py install for utest ... done
Successfully installed pip-20.0.2 utest-0.1.0

@di
Copy link
Member

di commented Jul 10, 2020

It's "supported" but only by parsing it as a LegacySpecifier, which is probably not what you're expecting:

>>> from packaging.specifiers import SpecifierSet
>>> SpecifierSet('>=20')._specs
frozenset({<Specifier('>=20')>})
>>> SpecifierSet('<=20.1.*')._specs
frozenset({<LegacySpecifier('<=20.1.*')>})

@di
Copy link
Member

di commented Oct 20, 2020

Closing this. Legacy specifiers will be deprecated in the next release and this will emit a warning.

@di di closed this as completed Oct 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants