Skip to content

fix(python): force-reinstall bundled pip wheel to create Scripts/*.exe#271

Merged
CalvinAllen merged 1 commit into
mainfrom
fix/python/force-reinstall-pip
May 14, 2026
Merged

fix(python): force-reinstall bundled pip wheel to create Scripts/*.exe#271
CalvinAllen merged 1 commit into
mainfrom
fix/python/force-reinstall-pip

Conversation

@CalvinAllen
Copy link
Copy Markdown
Contributor

@CalvinAllen CalvinAllen commented May 14, 2026

Summary

  • The Windows install of Python 3.14.2 (and other python-build-standalone releases) produced no pip.exe, pip3.exe, or pip3.X.exe even though our install spinner reported "pip configured successfully." Users hit a secondary executable not found: pip error from the shim at first invocation. This PR is the root-cause fix for #269; #270 is a complementary defense-in-depth fix that prevents install from creating phantom shims for executables that don't exist.

Why ensurepip silently did nothing

python-build-standalone Windows tarballs ship pip's module tree already present in Lib/site-packages/pip-25.3.dist-info/, with the bundled wheel at Lib/ensurepip/_bundled/pip-25.3-py3-none-any.whl. Internally, ensurepip --upgrade runs:

pip install --upgrade --no-index --find-links <tmpdir>

against that bundled wheel. Because the wheel version (25.3) matches the version already in site-packages, pip short-circuits with Requirement already satisfied: pip and skips the install entirely — including skipping the .exe entry-point script generation. ensurepip exposes no --force flag.

Reproduced locally against the same tarball Brad installed from:

$ python.exe -m ensurepip --default-pip --upgrade
Requirement already satisfied: pip in ...\Lib\site-packages (25.3)
$ ls Scripts/
.empty          ← still no pip.exe

What this PR does

Add a materializePipScripts step that runs after ensurepip. It globs Lib/ensurepip/_bundled/pip-*.whl and invokes:

python -m pip install --no-index --no-deps --force-reinstall <wheel>
  • --no-index keeps it offline — uses the wheel that already shipped with the distribution, no PyPI access
  • --no-deps avoids surprise upgrades to transitive packages
  • --force-reinstall defeats the "already satisfied" short-circuit, which is the entire point — this is what creates Scripts/pip.exe / pip3.exe / pip3.X.exe

Verified against the actual python-build-standalone tarball:

$ python.exe -m pip install --no-index --no-deps --force-reinstall Lib/ensurepip/_bundled/pip-25.3-py3-none-any.whl
Attempting uninstall: pip
  Found existing installation: pip 25.3
  Uninstalling pip-25.3: Successfully uninstalled pip-25.3
Successfully installed pip-25.3
$ ls Scripts/
pip.exe  pip3.14.exe  pip3.exe

ensurepip is kept as the first step because it still handles the case where pip isn't already in site-packages (e.g., python.org embeddable distributions). The force-reinstall is best-effort: minimal distributions may strip ensurepip's _bundled/ directory, and in that case we fall through with whatever ensurepip produced.

Test plan

  • ./rnr check — gofmt clean, golangci-lint 0 issues, full test suite passes on Windows
  • New TestFindBundledPipWheel covers the glob helper: finds wheel when present, errors when bundled dir is empty / missing, ignores non-pip wheels (e.g., the historical setuptools wheel pre-3.12)
  • Manual reproduction against https://builds.dtvem.io/python/3.14.2/windows-amd64.tar.gz: confirmed ensurepip alone leaves Scripts/ empty; force-reinstall step creates pip.exe, pip3.exe, and pip3.14.exe
  • CI passes on Windows / macOS / Linux

Relationship to #270

  • This PR makes pip actually install on Windows (the symptom users see).
  • fix(shim): discover install-time shims from disk, not static provider #270 makes install register shims from disk rather than a static list, so that even when something else prevents an executable from being created we don't leave phantom shims pointing at nothing.
  • They're independently valuable and can land in either order.

The Windows install of Python 3.14.2 (and other python-build-standalone
releases) produced no pip.exe, pip3.exe, or pip3.X.exe even though
"Configuring pip..." reported success. ensurepip --upgrade exited 0 and
printed "Requirement already satisfied: pip" — but did not create any
of the console scripts users expect.

Root cause: python-build-standalone Windows tarballs ship pip's module
tree already present in Lib/site-packages/pip-25.3.dist-info/ with the
bundled wheel sitting at Lib/ensurepip/_bundled/pip-25.3-py3-none-any.whl.
ensurepip --upgrade internally runs `pip install --upgrade --no-index
--find-links <tmpdir>` against that bundled wheel. Because the wheel
version matches the version already installed, pip short-circuits with
"Requirement already satisfied" and skips the install — including
skipping the .exe entry-point script generation. ensurepip exposes no
--force flag, so the only way to materialize the scripts is to bypass
ensurepip and call pip directly with --force-reinstall.

This change adds a materializePipScripts step that runs after ensurepip:
glob Lib/ensurepip/_bundled/pip-*.whl, then invoke

  python -m pip install --no-index --no-deps --force-reinstall <wheel>

against it. --no-index keeps it offline (uses the wheel shipped with
the distribution, no PyPI access). --no-deps avoids surprise upgrades.
--force-reinstall is the part that defeats the "already satisfied"
short-circuit and creates Scripts/pip.exe / pip3.exe / pip3.X.exe.

ensurepip is kept as the first step because it still handles the case
where pip isn't already in site-packages (e.g., python.org embeddable
distributions). The force-reinstall is best-effort: minimal
distributions may strip ensurepip's _bundled/ directory, and in that
case we fall through with whatever ensurepip produced.

Resolves #269
@CalvinAllen CalvinAllen force-pushed the fix/python/force-reinstall-pip branch from dab2ce1 to b82f3cb Compare May 14, 2026 15:20
@CalvinAllen CalvinAllen merged commit 6bf438b into main May 14, 2026
11 checks passed
@CalvinAllen CalvinAllen deleted the fix/python/force-reinstall-pip branch May 14, 2026 15:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant