Skip to content

🐛 fix(app-data): build the default folder only when none is given - #3488

Merged
gaborbernat merged 4 commits into
pypa:mainfrom
darrenhuai:fix/read-only-app-data-explicit-path
Oct 28, 2026
Merged

gaborbernat merged 4 commits into
pypa:mainfrom
darrenhuai:fix/read-only-app-data-explicit-path

Conversation

@darrenhuai

@darrenhuai darrenhuai commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

On a machine where the default app-data folder does not exist, a read-only run pointed at a prebuilt cache failed, and the error named the default folder instead of the one that was passed in. 🐛 HOME=/nonexistent virtualenv --read-only-app-data --app-data ./cache venv raised read-only app data directory /nonexistent/.cache/virtualenv does not exist, and VIRTUALENV_APP_DATA failed the same way. A missing default is common: a container running as nobody, a fresh CI account, or an XDG_CACHE_HOME that points nowhere. After this change a run uses the folder it was given and does not touch the default at all.

virtualenv built the default app data while registering the --app-data option, before the command line, environment or virtualenv.ini were read. In read-only mode that build failed on a missing default. In writable mode it created the default folder even when another one was named, and when the default could not be written it left an unclosed temporary folder behind. Because the default was already an app-data object, values from VIRTUALENV_APP_DATA or the ini file were converted with its class instead of the option's own conversion, so a missing read-only folder in the environment silently fell back to the default. The option now has no default while parsing, and virtualenv builds the default folder afterwards, only when nothing was given. #3410 already rejected a bad command line before any app-data folder was created; this moves the default itself.

VIRTUALENV_APP_DATA and ini values now get the same checks as --app-data, so a missing folder given that way fails in read-only mode instead of falling back. --help still prints the default folder, except together with --app-data, where it shows None because the default is never built.

Thanks for contributing, make sure you address all the checklists (for details on how see development documentation)

  • ran the linter to address style issues (ruff check and ruff format; the other pre-commit hooks run in CI)

  • wrote descriptive pull request text

  • ensured there are test(s) validating the fix

  • added news fragment in docs/changelog folder

  • updated/extended the documentation (no docs describe the old behavior; the generated CLI reference already calls the default the platform specific application data folder)

  • for changes to activation scripts, pyvenv.cfg, wheel downloads, app-data or release workflows: the outside input is the app-data path from --app-data, VIRTUALENV_APP_DATA or the ini file. It is only ever used as a folder path, never run as code, and the change only decides whether the default folder gets built at all, so it removes a write rather than adding one.

  • ran tests on Python 3.9 and 3.15 (ran on 3.14 on Windows and 3.12 on Linux directly with pytest; leaving 3.9 and 3.15 to CI)

  • linked upstream issues beside any temporary interpreter overrides (none)

  • kept the PR in draft until CI passes

darrenhuai and others added 4 commits October 27, 2026 18:57
load_app_data passed make_app_data(None, ...) as the default of
--app-data, so the default folder was built while the option was being
registered, before the command line, VIRTUALENV_APP_DATA or the ini
file had been read. With --read-only-app-data that builds a
ReadOnlyAppData for the default folder, which raises when the folder
does not exist. Pointing a read-only run at a prebuilt cache with
--app-data or VIRTUALENV_APP_DATA therefore failed on any machine
without the default cache, such as a container user whose HOME does
not exist or a fresh CI account, and the error named a folder the user
never asked for. Writable runs created the unused default folder
instead, and left a temporary folder behind when the default could not
be used.

Because the default was an AppData object, values from the environment
or the ini file were also converted with the default's class rather
than the option's type. VIRTUALENV_APP_DATA was dropped with a warning
when the default had fallen back to temporary app-data, and in
read-only mode a missing folder there fell back to the default instead
of failing like --app-data does.

The option now defaults to None and the default folder is built after
parsing, only when no folder was given. The result is also stored on
the action so --help still shows it when no folder was given. Environment and ini values now go
through make_app_data the same way a command line value does.
Three tests repeated the same source parametrization and branched on it
in a helper; a parametrized fixture now hands each test a function that
gives the folder on the command line, in the environment or in the
config file. The blocked default is set up by a parameter instead of an
if, the help test annotates capsys, and each test asserts once.
@gaborbernat
gaborbernat marked this pull request as ready for review October 28, 2026 05:50
@gaborbernat
gaborbernat merged commit 7e8102f into pypa:main Oct 28, 2026
148 of 150 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants