Repository navigation
馃悰 fix(seed): avoid downgrading the bundled wheel - #3408
Merged
gaborbernat merged 2 commits intoOct 17, 2026
Merged
Conversation
gaborbernat
force-pushed
the
fix/seed-update-version-floor
branch
from
October 16, 2026 14:22
f0a2e2b to
c13125e
Compare
Reported by Anthropic's OSS Scanner: ANT-2026-HY012DAG.
gaborbernat
force-pushed
the
fix/seed-update-version-floor
branch
from
October 16, 2026 14:25
c13125e to
5a75fd5
Compare
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.
virtualenv seeds pip and setuptools from the wheels bundled in its own package. The periodic update (a background child that, once every 14 days, downloads newer releases into the app-data cache and records them in an update log per distribution and Python version) lets later environments use a newer cached wheel instead. Two comparisons in that path went wrong once the bundled wheel was newer than the cache. After an upgrade of virtualenv, the selector returned the newest cached wheel although the bundled one was newer, so new environments got a pip older than the one virtualenv ships until the next update ran. The update child could also record a download older than the bundled wheel. 馃悰 Automatic selection now keeps the bundled wheel unless a cached entry is newer, and the update child stops before it records a download below the bundled version. Explicit pins such as
--pip 25.2are unchanged.The selector recognized the bundled wheel by filename
periodic_updatewalks the update log newest first, one minor version at a time. For each entry it returned the bundled wheel when the entry's filename equalled the bundled wheel's name, and otherwise returned the entry onceNewVersion.useaccepted it: a periodic entry found more than an hour ago whose release is 28 days old, or any entry a manual upgrade recorded. The filename check dates from #1841, which introduced the periodic update, and #2273 kept it when the loop started to consider every patch release of a minor. The log holds what the child downloaded down to the bundled version at the time of that run. Upgrade virtualenv to a release that bundles a newer pip and no entry carries that filename: the loop reaches the newest cached entry,useaccepts it, and the environment is seeded with the older wheel. The next child runs 14 days after the last completed one, and only its entries end the downgrade.The loop now compares parsed versions: the first entry whose version is not above the bundled wheel's ends the search with the bundled wheel. Both sides are
packaging.version.Versionobjects fromWheel.parsed_version, so the comparison follows PEP 440 ordering and not the spelling of the filename. An entry newer than the bundled wheel is still returned onceuseaccepts it.flowchart LR B[bundled pip 25.3, log: 25.2 periodic, 25.1.1 periodic] --> F{filename equals the bundled name?} F -- no, before --> U{use accepts 25.2?} U -- yes --> D[seed cached pip 25.2] B --> V{25.2 above 25.3?} V -- no, now --> K[seed bundled pip 25.3] classDef bad fill:#ffd6d6,stroke:#c1121f,color:#5c0000; classDef ok fill:#d8f3dc,stroke:#2d6a4f,color:#1b4332; class D bad; class K ok;The update child recorded downloads below the bundled version
_run_do_updatedownloads the newest release, then the newest below it, and so on. It appended each download to the log and only then compared it with the bundled version; #1892 added that stop. The entry at or below the bundled version was therefore recorded before the loop ended. When the first download is already older than the bundled wheel, and a mirror behindPIP_INDEX_URLthat lags PyPI is enough for that, the log gained an entry below the bundled version with sourceperiodicormanual. With the filename check above, that entry was selected onceuseaccepted it.The loop now checks the bound before it records: a download below the bundled version ends the loop with no entry, next to the existing stops for no download and for reaching the most recent known entry. A download equal to the bundled version is still recorded and still ends the loop, as before.
sequenceDiagram participant U as update child participant I as index participant L as update log U->>I: pip download pip, bundled is 25.3 I-->>U: pip-25.2 wheel rect rgb(255, 214, 214) U->>L: before: append 25.2, then stop at the bound end rect rgb(216, 243, 220) Note over U: now: 25.2 is below 25.3, stop before the append U->>L: log unchanged endBehavior changes
Automatic bundle selection does not downgrade to an older cached update. Explicit version pins retain their existing behavior. A download equal to the bundled version still terminates the search as before; a lower version is discarded from the update results, so
--upgrade-embed-wheelsreports no new versions when the index has nothing as new as the bundled wheel.Reported by Anthropic's OSS Scanner: ANT-2026-HY012DAG.