Repository navigation
fix(spec): let ~=X.Y match later minor versions - #159
Open
darrenhuai wants to merge 2 commits into
Open
darrenhuai wants to merge 2 commits into
darrenhuai wants to merge 2 commits into
Conversation
SimpleVersion pads every release to three parts, and the compatible
release check built its upper bound by dropping the last part of that
padded tuple. That is only right when the specifier itself has three
parts. For ~=3.10 it gave >=3.10,<3.11 instead of the >=3.10,==3.* that
PEP 440 defines, so get_interpreter("~=3.10") returned None on a machine
whose Pythons are all 3.11 or newer, and the uv store filter and
PythonSpec.satisfies with a full X.Y.Z spec skipped the same
interpreters. virtualenv -p "~=3.10" fails the same way.
The check now compares every release part the specifier wrote except
the last, the same prefix match the wildcard check uses. Comparing the
prefix also stops ~=3.12.0 from accepting 3.13.0a1, which sorted below
the old 3.13 bound. A bare ~=3 is not valid PEP 440, but it was
accepted before, so it keeps working and now means ==3.* in both
SimpleSpecifier.contains and PythonInfo.satisfies, which used to
disagree on it.
Anyone who used ~=3.12 to mean 3.12 only now gets later 3.x releases
too; PEP 440 spells that ~=3.12.0 or ==3.12.*.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
With 3.9, 3.13, 3.14 and 3.15 installed, a two-part compatible release finds nothing on main:
PEP 440 defines
~=3.10as>=3.10, ==3.*, and the docs list~=as supported and link that spec, so the first lookup should find 3.14.3 like the explicit range does.virtualenv -p "~=3.10"goes through the same call and fails the same way.SimpleVersion.from_stringpads every release to three parts, so~=3.10is stored as(3, 10, 0)._check_compatible_releasebuilt its upper bound by dropping the last part of that padded tuple and bumping the one before it, which gives 3.11. That's only right for a three-part spec like~=3.12.0, the only form the tests checked against the upper bound.PythonInfo.satisfies(used byget_interpreteranditer_interpreters), the uv store filter andPythonSpec.satisfieswith a fullX.Y.Zspec all go through this method, so they skipped 3.11 and newer for~=3.10, and 3.13 and newer for~=3.12.The fix compares all but the last release part the spec actually wrote. It takes that count from
version_str.count("."), as #48 did in_satisfies_version_specifier, and checks the prefix the way_check_wildcardalready does for==X.*. Checking the prefix rather than building a bound also stops~=3.12.0from accepting 3.13.0a1, which used to slip in because that pre-release sorts below 3.13.0. The deadlen(release) >= 2branch and its two pragmas go away.This does change what some specs pick. On this machine
get_interpreter("~=3.13")returned 3.13.7 on main and now returns 3.14.3. Anyone who used~=3.12to mean 3.12 only now gets later 3.x too; PEP 440 spells that~=3.12.0or==3.12.*.A bare
~=3isn't valid PEP 440, but it was accepted before, andSimpleSpecifierSetsilently drops entries that fail to parse, so rejecting it would turn~=3into a set that matches everything. I kept it lenient and it now means==3.*, which is whatPythonInfo.satisfiesalready did with it. Before this changeSimpleSpecifier.containssaid 3.13.7 was outside~=3whilesatisfiessaid a 3.13.7 interpreter matched.The new
test_specifier_containsrows check~=3.10against 3.9.25, 3.13.7, 4.0.0 and 4.0.0a1,~=3.13rc1against 3.14.0, and~=3.12.0against 3.13.0a1, and the~=3test,test_satisfies_version_specifier_prereleaseand the uv probe-order test get matching cases. Seven of them fail on main. The 4.0.0 rows pin the==3.*ceiling and the 3.9.25 row keeps the lower bound covered. Taking the part count from the padded release, dropping themax(..., 1)floor, going back to a computed upper bound, or removing thecandidate < self.versioncheck each breaks at least one of them.Windows and Linux (under coverage) show the same failures as main,
_specifier.pyis fully covered, and ruff and ty (3.9 and 3.15) report nothing new.