-
Notifications
You must be signed in to change notification settings - Fork 765
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
uv incorrectly excludes pre-release Python versions when requires-python
is greater or equal to that version
#4714
Comments
I don't feel strongly about this, I'm fine with effectively omitting pre-releases for Python version handling. What does "accept already installed pre-releases for all version specifiers" mean? Is that like, if some does I maintain that per the spec a pre-release version should not satisfy |
Yes, that has been my interpretation anyway.
I don't follow your logic. If we were talking about remote packages versions instead of python_versions it would match though. Let's say package "foo" there was only one remote package version, and it was "foo 3.11.0a0", then it would match |
You might be right on that last point. It's sort of hard for me to reason about in a world in which (1) we can install Python versions on-demand to meet Separately, if |
Dug through the pip codebase, I think the line that ends up allowing Python prereleases is this one (at least in the "new" resolver): https://github.com/pypa/pip/blob/24.1.1/src/pip/_internal/resolution/resolvelib/requirements.py#L207 It looks like it was implemented by pypa/pip#8079, and a side effect of the issue that releases with only prereleases should be installed pypa/pip#8075. But maybe there is a deeper history to this that I'm unware of.
That's my thinking anyway, I'm not sure anyone who wrote the spec had your scenario in mind. Is anyone else doing universal resolution other than Poetry? It might be worth sanity checking against what Poetry does.
Well, technically they could do something like:
Which I've seen similiar syntax for package versions due to the limited way you can express ranges with holes in them. |
I'd be happy to make a Python packaging discussion on this to seek clarification from the broader community if you'd prefer to see if there's consensus on this. It's certainly not explicity spelled out in the spec, and I know sometimes the way I read the spec is not in line with the way the spec authors intended it. |
Na it's alright. We already have this semantics with |
Still trying to figure out how pip actually allows this given: >>> from packaging import specifiers
>>> specifiers.SpecifierSet(">=3.13").contains("3.13.0b0", prereleases=True)
False |
Ah you're right, the The reason pip includes prerelease versions of Python is when it extracts the version from Python it only takes the first 3 elements of the version object: https://github.com/pypa/pip/blob/24.1.1/src/pip/_internal/resolution/resolvelib/candidates.py#L540 >>> sys.version_info
sys.version_info(major=3, minor=13, micro=0, releaselevel='beta', serial=3)
>>> sys.version_info[:3]
(3, 13, 0)
>>> ".".join(str(c) for c in sys.version_info[:3])
'3.13.0' |
I appreciate that seems to invalidate most of my reasoning, I will need to run some more checks and think on this. |
That actually makes life a lot easier for us though, if we just want to mimic that... |
True, it does make me think though that you can't do any sort of prerelease exclusion, I will do some more careful testing and report an issue on pip side to see if maintainers have an opinion on that. Going back to the spec I find the following: https://packaging.python.org/en/latest/specifications/pyproject-toml/#requires-python Which links to: https://packaging.python.org/en/latest/specifications/core-metadata/#core-metadata-requires-python It's all rather underspecified, and unfortunately uses the word may. Oh well. |
## Summary There are a few ideas at play here: 1. pip always strips versions to the release when evaluating against a `Requires-Python`, so we now do the same. That means, e.g., using `3.13.0b0` will be accepted by a project with `Requires-Python: >= 3.13`, which does _not_ adhere to PEP 440 semantics but is somewhat intuitive. 2. Because we know we'll only be evaluating against release-only versions, we can use different semantics in PubGrub that let us collapse ranges. For example, `python_version >= '3.10' or python_version < '3.10'` can be collapsed to the truthy marker. Closes #4714. Closes #4272. Closes #4719.
Tested on:
Create a pyproject.toml like so:
Dry install with uv fails:
Dry install with pip succeeds:
IMO pip is following the spec here and uv is not. Specifically if we take
requires-python
to be a Version as defined by PEP 440, then according to: https://packaging.python.org/en/latest/specifications/version-specifiers/#handling-of-pre-releases then the installed version of Python should already be accepted based on:Moreover, intuitively, if a developer splits up their package releases between
python < 3.11
andpython >= 3.11
they should not expect there to be a hole of Python versions.But let me know if you know some more specific part of the spec that says my interpretation is wrong, I will file a bug against pip.
The text was updated successfully, but these errors were encountered: