The downgrade decision and the startup check read each clause of a pip
requirement on its own, and did not read "!=". Found by CodeRabbit on
#3521, #3523 and #3524 and in review.
- Install a requirement for a protected package that allows the
installed version when another clause sets a minimum above it
("==4.*,>=4.58.0" beside 4.57.0); it was skipped
- Skip one that the installed version does not meet and that allows no
newer version ("[torch]<=4.57.0,!=4.57.0", "<4.58,!=4.57.*"); it was
installed, and pip replaced the package with an older version
- Install one that excludes the installed version with "!=" while a
newer version is allowed ("~=4.57.0,!=4.57.0"); without extras it
was skipped
- At startup, read ">" as pip does: a post-release of its version does
not meet it, and the requirement is installed when a newer version
meets every clause
- At startup, take "==0.21.*" as met by an installed 0.21.0rc1, as pip
does
- At startup, install a requirement that excludes the installed
version with "!=" and allows a newer one; pip chooses the version,
which is an older one when no newer release exists
- Ask packaging whether a newer version meets every clause, about the
versions next to the installed one and next to each clause's version;
a pre-release counts only when "==", ">=", "<=", "~=" or "===" names
one, which is when every pip takes one
- Never replace an installed local build, such as a CUDA build, on
these grounds, and keep a protected one that "!=" excludes unless
the requirement sets a minimum above it: for such a build the
changes only add requirements that are skipped
- Add tests and a CHANGELOG entry
The same change for main is #3524.