Skip to content

Commit 5592301

Browse files
committed
fix ensurepip shebangs via in-process pip monkey-patching
Replace the unsupported --executable pip argument with an in-process monkey-patch of pip's internal script generation logic. The bundled version of pip does not support the --executable flag, causing integration failures (e.g., during 'make install'). This change intercepts the --executable argument in _run_pip and applies it by patching pip._internal.operations.install.wheel and pip._vendor.distlib.scripts.ScriptMaker before the installation starts. This ensures that scripts installed via ensurepip (with --prefix or --root) use the correct target interpreter shebang even when cross-compiling or performing staged installs.
1 parent 60417f3 commit 5592301

1 file changed

Lines changed: 46 additions & 1 deletion

File tree

Lib/ensurepip/__init__.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,53 @@ def _run_pip(args, additional_paths=None):
7070
code = f"""
7171
import runpy
7272
import sys
73+
import os
74+
75+
# Extract --executable from args if present to avoid "unknown option" error from pip
76+
# while still honoring the requested shebang.
77+
pip_args = {args}
78+
executable_path = None
79+
if "--executable" in pip_args:
80+
try:
81+
idx = pip_args.index("--executable")
82+
executable_path = pip_args[idx + 1]
83+
del pip_args[idx:idx + 2]
84+
except (ValueError, IndexError):
85+
pass
86+
7387
sys.path = {additional_paths or []} + sys.path
74-
sys.argv[1:] = {args}
88+
89+
if executable_path:
90+
try:
91+
import pip._internal.operations.install.wheel as w
92+
from pip._vendor.distlib.scripts import ScriptMaker
93+
94+
def patched_fix_script(path):
95+
with open(path, "rb") as script:
96+
firstline = script.readline()
97+
if not firstline.startswith(b"#!python"):
98+
return False
99+
exename = executable_path.encode(sys.getfilesystemencoding())
100+
firstline = b"#!" + exename + os.linesep.encode("ascii")
101+
rest = script.read()
102+
with open(path, "wb") as script:
103+
script.write(firstline)
104+
script.write(rest)
105+
return True
106+
107+
w.fix_script = patched_fix_script
108+
109+
orig_init = ScriptMaker.__init__
110+
def patched_init(self, *a, **kw):
111+
orig_init(self, *a, **kw)
112+
self.executable = executable_path
113+
ScriptMaker.__init__ = patched_init
114+
except ImportError:
115+
# If pip internals changed, we might not be able to patch.
116+
# But we still want to run pip.
117+
pass
118+
119+
sys.argv[1:] = pip_args
75120
runpy.run_module("pip", run_name="__main__", alter_sys=True)
76121
"""
77122

0 commit comments

Comments
 (0)