Skip to content

馃悰 fix(seed): guard every embed update log read, not just one - #3383

Merged
gaborbernat merged 4 commits into
pypa:mainfrom
darrenhuai:fix/embed-update-log-all-readers
Oct 8, 2026
Merged

gaborbernat merged 4 commits into
pypa:mainfrom
darrenhuai:fix/embed-update-log-all-readers

Conversation

@darrenhuai

Copy link
Copy Markdown
Contributor

#3376 made UpdateLog.from_app_data drop an embed update log that holds JSON of the wrong shape. Three other readers in periodic_update.py still parse the file with the raw UpdateLog.from_dict(embed_update_log.read()), and one of them runs first on the default path, so the guard only helps runs with --no-periodic-update.

With periodic update on (the default), handle_auto_update hits the bad log before from_app_data ever sees it. The app-data seeder logs the TypeError as fail with a traceback, then retries with download=True and pulls pip from PyPI even though nobody asked for a download. Offline it fails outright:

$ echo '{"completed": null, "periodic": null, "started": null, "versions": {}}' > <app-data>/wheel/3.14/embed/3/pip.json
$ PIP_NO_INDEX=1 virtualenv venv
RuntimeError: seed failed due to failing to download wheels pip

#3376's own test passed do_periodic_update=False, which is why it never reached this.

add_wheel_to_update_log and the background _run_do_update have the same unguarded read. All three now go through from_app_data, so a malformed log gets dropped with one warning wherever it's first read, and the reset log schedules a fresh periodic update the way a never-run log does.

The malformed-log cases are shared across the four entry points as _MALFORMED_LOGS. Each new test fails when its own call site is put back to from_dict. The full suite, ruff, and ty (3.9 and 3.14) pass.

Something I noticed but left alone: test_periodic_update_skip and test_periodic_update_trigger pass os.environ, True positionally into (do_periodic_update, env), so the arguments are swapped. They only pass because os.environ is truthy. Happy to send that as a separate fix if you want it.

darrenhuai and others added 4 commits October 7, 2026 19:19
pypa#3376 made UpdateLog.from_app_data drop an update log that holds JSON of
the wrong shape, but periodic_update() only reaches that guard after
handle_auto_update() has already parsed the same file with the raw
UpdateLog.from_dict(). Periodic update is on by default, so the guard
only helped runs with --no-periodic-update. Everyone else still hit the
TypeError: the app-data seeder logged it as "fail" with a traceback,
then retried with download=True and fetched pip from PyPI even though
the user never asked for a download. With no network (PIP_NO_INDEX=1
reproduces it) environment creation failed with "seed failed due to
failing to download wheels pip".

add_wheel_to_update_log() and the background do_update() had the same
unguarded read. All three now go through from_app_data, so a malformed
log is dropped with one warning wherever it is first read, and the reset
log schedules a fresh periodic update as a never-run log would.

The malformed-log cases are shared across the four entry points; each
new test fails when its call site is put back to from_dict.
Write each malformed log through one parametrized fixture instead of
repeating the setup in four tests, inline the update log stores that the
guarded readers now touch once, and state the changelog fix in user terms.

@gaborbernat gaborbernat left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gaborbernat
gaborbernat enabled auto-merge (squash) October 8, 2026 05:53
@gaborbernat
gaborbernat disabled auto-merge October 8, 2026 14:47
@gaborbernat
gaborbernat merged commit 464c34b into pypa:main Oct 8, 2026
76 checks passed
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

Successfully merging this pull request may close these issues.

2 participants