Skip to content

Commit 99db110

Browse files
committed
feat: skip installation of pip/pipx packages if already installed
1 parent 67a1d8d commit 99db110

File tree

6 files changed

+82
-18
lines changed

6 files changed

+82
-18
lines changed

dist/legacy/setup-cpp.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/legacy/setup-cpp.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/modern/setup-cpp.mjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

dist/modern/setup-cpp.mjs.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/python/python.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,20 @@ async function setupPipx(foundPython: string) {
5656
}
5757
}
5858
await execa(foundPython, ["-m", "pipx", "ensurepath"], { stdio: "inherit" })
59-
await setupPipPackWithPython(foundPython, "venv", undefined, { upgrade: false, usePipx: false })
59+
await setupVenv(foundPython)
6060
} catch (err) {
6161
warning(`Failed to install pipx: ${(err as Error).toString()}. Ignoring...`)
6262
}
6363
}
6464

65+
async function setupVenv(foundPython: string) {
66+
try {
67+
await setupPipPackWithPython(foundPython, "venv", undefined, { upgrade: false, usePipx: false })
68+
} catch (err) {
69+
warning(`Failed to install venv: ${(err as Error).toString()}. Ignoring...`)
70+
}
71+
}
72+
6573
/** Setup wheel and setuptools */
6674
async function setupWheel(foundPython: string) {
6775
try {
@@ -70,9 +78,9 @@ async function setupWheel(foundPython: string) {
7078
isLibrary: true,
7179
usePipx: false,
7280
})
73-
await setupPipPackWithPython(foundPython, "wheel", undefined, { upgrade: true, isLibrary: true, usePipx: false })
81+
await setupPipPackWithPython(foundPython, "wheel", undefined, { upgrade: false, isLibrary: true, usePipx: false })
7482
} catch (err) {
75-
warning(`Failed to install setuptools or wheel: ${(err as Error).toString()}. Ignoring...`)
83+
warning(`Failed to install setuptools/wheel: ${(err as Error).toString()}. Ignoring...`)
7684
}
7785
}
7886

src/utils/setup/setupPipPack.ts

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,16 @@ export async function setupPipPackWithPython(
5252
const isPipx = usePipx && !isLibrary && (await hasPipx(givenPython))
5353
const pip = isPipx ? "pipx" : "pip"
5454

55-
const hasPackage = await pipHasPackage(givenPython, name)
55+
// if upgrade is not requested, check if the package is already installed, and return if it is
56+
if (!upgrade) {
57+
const installed = await pipPackageIsInstalled(givenPython, pip, name)
58+
if (installed) {
59+
const binDir = await finishPipPackageInstall(givenPython, name)
60+
return { binDir }
61+
}
62+
}
63+
64+
const hasPackage = await pipHasPackage(givenPython, pip, name)
5665
if (hasPackage) {
5766
try {
5867
info(`Installing ${name} ${version ?? ""} via ${pip}`)
@@ -79,18 +88,19 @@ export async function setupPipPackWithPython(
7988
throw new Error(`Failed to install ${name} via ${pip}: ${err}.`)
8089
}
8190
}
82-
} else {
83-
if ((await setupPipPackSystem(name)) === null) {
84-
throw new Error(`Failed to install ${name} as it was not found via ${pip} or the system package manager`)
85-
}
91+
} else if ((await setupPipPackSystem(name)) === null) {
92+
throw new Error(`Failed to install ${name} as it was not found via ${pip} or the system package manager`)
8693
}
8794

88-
const execPaths = await addPythonBaseExecPrefix(givenPython)
89-
const binDir = await findBinDir(execPaths, name)
95+
const binDir = await finishPipPackageInstall(givenPython, name)
96+
return { binDir }
97+
}
9098

99+
async function finishPipPackageInstall(givenPython: string, name: string) {
100+
const pythonBaseExecPrefix = await addPythonBaseExecPrefix(givenPython)
101+
const binDir = await findBinDir(pythonBaseExecPrefix, name)
91102
await addPath(binDir, rcOptions)
92-
93-
return { binDir }
103+
return binDir
94104
}
95105

96106
export async function hasPipx(givenPython: string) {
@@ -153,8 +163,54 @@ async function getPython_(): Promise<string> {
153163
}
154164
const getPython = memoize(getPython_, { promise: true })
155165

156-
async function pipHasPackage(python: string, name: string) {
157-
const result = await execa(python, ["-m", "pip", "-qq", "index", "versions", name], {
166+
type PipxShowType = {
167+
venvs: Record<string, {
168+
metadata: {
169+
main_package: {
170+
package: string
171+
package_or_url: string
172+
apps: string[]
173+
}
174+
}
175+
}>
176+
}
177+
178+
async function pipPackageIsInstalled(python: string, pip: string, name: string) {
179+
try {
180+
if (pip === "pipx") {
181+
const result = await execa(python, ["-m", pip, "list", "--json"], {
182+
stdio: "ignore",
183+
reject: false,
184+
})
185+
if (result.exitCode !== 0 || typeof result.stdout !== "string") {
186+
return false
187+
}
188+
189+
const pipxOut = JSON.parse(result.stdout as unknown as string) as PipxShowType
190+
// search among the venvs
191+
if (name in pipxOut.venvs) {
192+
return true
193+
}
194+
// search among the urls
195+
for (const venv of Object.values(pipxOut.venvs)) {
196+
if (venv.metadata.main_package.package_or_url === name || venv.metadata.main_package.package === name) {
197+
return true
198+
}
199+
}
200+
}
201+
202+
const result = await execa(python, ["-m", pip, "-qq", "show", name], {
203+
stdio: "ignore",
204+
reject: false,
205+
})
206+
return result.exitCode === 0
207+
} catch {
208+
return false
209+
}
210+
}
211+
212+
async function pipHasPackage(python: string, pip: string, name: string) {
213+
const result = await execa(python, ["-m", pip, "-qq", "index", "versions", name], {
158214
stdio: "ignore",
159215
reject: false,
160216
})

0 commit comments

Comments
 (0)