Skip to content

Commit a9303b5

Browse files
authored
Bootstrap pip on both platforms (#1449)
2 parents a56fdff + 6426807 commit a9303b5

1 file changed

Lines changed: 41 additions & 13 deletions

File tree

exporter/SynthesisFusionAddin/src/Dependencies.py

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ def getInternalFusionPythonInstillationFolder() -> str | os.PathLike[str]:
4040
return folder
4141

4242

43-
def executeCommand(*args: str) -> subprocess.CompletedProcess[str]:
43+
def executeCommand(*args: str, check: bool = True) -> subprocess.CompletedProcess[str]:
4444
logger.debug(f"Running Command -> {' '.join(args)}")
4545
try:
4646
result: subprocess.CompletedProcess[str] = subprocess.run(
47-
args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True
47+
args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=check
4848
)
4949
logger.debug(f"Command Output:\n{result.stdout}")
5050
return result
@@ -55,6 +55,40 @@ def executeCommand(*args: str) -> subprocess.CompletedProcess[str]:
5555
raise error
5656

5757

58+
def pipInstalled(pythonExecutablePath: str) -> bool:
59+
"""Check whether pip is importable by the given Fusion python interpreter."""
60+
# Use check=False so the expected "pip missing" case does not log an error.
61+
return executeCommand(pythonExecutablePath, "-m", "pip", "--version", check=False).returncode == 0
62+
63+
64+
def ensurePipInstalled(pythonFolder: str | os.PathLike[str], pythonExecutablePath: str) -> None:
65+
"""Bootstrap pip into Fusion's bundled python if it is missing.
66+
67+
Fusion's runtime does not reliably ship pip: it is absent by default on macOS, and every
68+
Fusion update lands in a fresh 'webdeploy' folder whose python may also lack it on Windows.
69+
Prefer the stdlib 'ensurepip' (no network required); fall back to downloading 'get-pip.py'.
70+
"""
71+
if pipInstalled(pythonExecutablePath):
72+
return
73+
74+
logger.info("pip not found in Fusion python, bootstrapping...")
75+
76+
# ensurepip ships with the CPython standard library and needs no internet access.
77+
try:
78+
executeCommand(pythonExecutablePath, "-m", "ensurepip", "--upgrade")
79+
if pipInstalled(pythonExecutablePath):
80+
return
81+
except subprocess.CalledProcessError:
82+
logger.warning("ensurepip unavailable, falling back to get-pip.py")
83+
84+
# Fallback: download and run get-pip.py (curl ships with both modern Windows and macOS).
85+
pipInstallScriptPath = os.path.join(pythonFolder, "get-pip.py")
86+
if not os.path.exists(pipInstallScriptPath):
87+
executeCommand("curl", "-fsSL", "https://bootstrap.pypa.io/get-pip.py", "-o", pipInstallScriptPath)
88+
89+
executeCommand(pythonExecutablePath, pipInstallScriptPath)
90+
91+
5892
def getInstalledPipPackages(pythonExecutablePath: str) -> dict[str, str]:
5993
result: str = executeCommand(pythonExecutablePath, "-m", "pip", "freeze").stdout
6094
# We don't need to check against packages with a specific hash as those are not required by Synthesis.
@@ -92,17 +126,11 @@ def resolveDependencies() -> bool | None:
92126
progressBar.reset()
93127
progressBar.show("Synthesis", f"Installing dependencies...", 0, len(PIP_DEPENDENCY_VERSION_MAP) * 2 + 2, 0)
94128

95-
# Install pip manually on macos as it is not included by default? Really?
96-
if SYSTEM == "Darwin" and not os.path.exists(os.path.join(pythonFolder, "pip")):
97-
pipInstallScriptPath = os.path.join(pythonFolder, "get-pip.py")
98-
if not os.path.exists(pipInstallScriptPath):
99-
executeCommand("curl", "https://bootstrap.pypa.io/get-pip.py", "-o", pipInstallScriptPath)
100-
progressBar.message = "Downloading PIP Installer..."
101-
102-
progressBar.progressValue += 1
103-
progressBar.message = "Installing PIP..."
104-
executeCommand(pythonExecutablePath, pipInstallScriptPath)
105-
progressBar.progressValue += 1
129+
# Fusion's bundled python does not reliably ship pip (missing by default on macOS, and on
130+
# fresh Windows webdeploy installs after a Fusion update), so bootstrap it if it is absent.
131+
progressBar.message = "Installing PIP..."
132+
ensurePipInstalled(pythonFolder, pythonExecutablePath)
133+
progressBar.progressValue += 2
106134

107135
installedPackages = getInstalledPipPackages(pythonExecutablePath)
108136
if packagesOutOfDate(installedPackages):

0 commit comments

Comments
 (0)