Title: gh-128670: keep the public base executable when creating a venv from a venv by gaborbernat · Pull Request #150999 · python/cpython · GitHub
Open Graph Title: gh-128670: keep the public base executable when creating a venv from a venv by gaborbernat · Pull Request #150999 · python/cpython
X Title: gh-128670: keep the public base executable when creating a venv from a venv by gaborbernat · Pull Request #150999 · python/cpython
Description: Problem When a virtual environment is created from inside another virtual environment, and the base interpreter is reached through a stable public symlink, the child environment records the internal, version-stamped target in its pyvenv.cfg home instead of the stable public path. This happens with the common ways people now get Python: Homebrew — the stable …/opt/python@3.x/bin path resolves to a versioned …/Cellar/python@3.x/3.x.y/… path; relocatable / uv-managed interpreters and NFS canonical paths. When that internal path later changes — a Homebrew patch upgrade deletes the old Cellar/3.x.y tree, an NFS mount moves — the recorded home dangles and the environment breaks. The public symlink exists precisely so callers don't depend on the internal path, so baking it in defeats the purpose. Reproduction (Linux; also reproduces on Homebrew 3.12–3.14): $ mkdir -p /pub/bin && ln -s /usr/local/bin/python3.x /pub/bin/ $ /pub/bin/python3.x -m venv /v1 $ /v1/bin/python -m venv /v2 $ grep home /v1/pyvenv.cfg # home = /pub/bin (public, kept) $ grep home /v2/pyvenv.cfg # home = /usr/local/bin (internal — bled through) Solution In getpath's pyvenv.cfg home branch, when computing base_executable, prefer the executable as named in home (the public location the venv was created from) when it resolves to the same real file as the running interpreter, instead of fully resolving the symlink. Fall back to the previous fully-resolved path when home has no matching executable or it points elsewhere. The candidate is looked up by the resolved interpreter's name, since a venv's primary executable is python while home may only provide python3.x. This is the getpath-feasible form of what uv settled on (astral-sh/uv#8433) and corrects #128913, which matched on python and so missed the common python3.x-only case. Why it's safe Exactly one behaviour changes: a venv-from-venv whose base is reached via a public symlink now records the public path. Non-symlinked bases, a home with no matching executable, and a home pointing at a different file all produce the identical result as before. Guarded: the public path is kept only if it exists and resolves to the same real file as the running interpreter, so base_executable is never pointed at a missing or different binary. Stdlib resolution is untouched: base_prefix and the standard-library search run afterward as before and still resolve to the real location — the created environments run with the correct base_prefix (verified end-to-end). Validated: four new test_getpath cases (public symlink, versioned-name, mismatch-guard, no-home-exe fallback) plus test_getpath/test_venv and a broader sweep (test_embed, test_site, test_sysconfig, test_subprocess) — 654 tests, no regressions; and end-to-end on Linux across single-hop, multi-hop, and a 3-level venv chain. Issue: gh-128670
Open Graph Description: Problem When a virtual environment is created from inside another virtual environment, and the base interpreter is reached through a stable public symlink, the child environment records the interna...
X Description: Problem When a virtual environment is created from inside another virtual environment, and the base interpreter is reached through a stable public symlink, the child environment records the interna...
Opengraph URL: https://github.com/python/cpython/pull/150999
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:b315c97b-bbfa-c6ad-2f2c-0e8c1c2f7f51 |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | 8BDA:633B1:36FCD68:4BE5EB9:6A4F3615 |
| html-safe-nonce | 8ef674a490ef24be029e5da6d557fcf6dbc0f66edcd68b79dba2d3192fbc32c7 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI4QkRBOjYzM0IxOjM2RkNENjg6NEJFNUVCOTo2QTRGMzYxNSIsInZpc2l0b3JfaWQiOiIxNzc1NjE0ODExMzgxMzgwNjI5IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | a23f471dce1b71726e590b104d61e8e5963448be83f9eb76e49f48f7c6189b7a |
| hovercard-subject-tag | pull_request:3814007507 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/python/cpython/pull/150999/files |
| twitter:image | https://avatars.githubusercontent.com/u/690238?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/690238?s=400&v=4 |
| og:image:alt | Problem When a virtual environment is created from inside another virtual environment, and the base interpreter is reached through a stable public symlink, the child environment records the interna... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | b92d11c0aa4a77d54ef4af1078b6a15fb5a70a215b30c4ecf28889d5a8e656d9 |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/python/cpython git https://github.com/python/cpython.git |
| octolytics-dimension-user_id | 1525981 |
| octolytics-dimension-user_login | python |
| octolytics-dimension-repository_id | 81598961 |
| octolytics-dimension-repository_nwo | python/cpython |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 81598961 |
| octolytics-dimension-repository_network_root_nwo | python/cpython |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 2b8f23afb982271f1b22258a94aede67a6b77760 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width