feat: skip installation of pip/pipx packages if already installed

This commit is contained in:
Amin Yahyaabadi 2024-09-17 15:32:20 -07:00
parent 67a1d8d27d
commit 99db11072d
No known key found for this signature in database
GPG Key ID: F52AF77F636088F0
6 changed files with 83 additions and 19 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -56,12 +56,20 @@ async function setupPipx(foundPython: string) {
}
}
await execa(foundPython, ["-m", "pipx", "ensurepath"], { stdio: "inherit" })
await setupPipPackWithPython(foundPython, "venv", undefined, { upgrade: false, usePipx: false })
await setupVenv(foundPython)
} catch (err) {
warning(`Failed to install pipx: ${(err as Error).toString()}. Ignoring...`)
}
}
async function setupVenv(foundPython: string) {
try {
await setupPipPackWithPython(foundPython, "venv", undefined, { upgrade: false, usePipx: false })
} catch (err) {
warning(`Failed to install venv: ${(err as Error).toString()}. Ignoring...`)
}
}
/** Setup wheel and setuptools */
async function setupWheel(foundPython: string) {
try {
@ -70,9 +78,9 @@ async function setupWheel(foundPython: string) {
isLibrary: true,
usePipx: false,
})
await setupPipPackWithPython(foundPython, "wheel", undefined, { upgrade: true, isLibrary: true, usePipx: false })
await setupPipPackWithPython(foundPython, "wheel", undefined, { upgrade: false, isLibrary: true, usePipx: false })
} catch (err) {
warning(`Failed to install setuptools or wheel: ${(err as Error).toString()}. Ignoring...`)
warning(`Failed to install setuptools/wheel: ${(err as Error).toString()}. Ignoring...`)
}
}

View File

@ -52,7 +52,16 @@ export async function setupPipPackWithPython(
const isPipx = usePipx && !isLibrary && (await hasPipx(givenPython))
const pip = isPipx ? "pipx" : "pip"
const hasPackage = await pipHasPackage(givenPython, name)
// if upgrade is not requested, check if the package is already installed, and return if it is
if (!upgrade) {
const installed = await pipPackageIsInstalled(givenPython, pip, name)
if (installed) {
const binDir = await finishPipPackageInstall(givenPython, name)
return { binDir }
}
}
const hasPackage = await pipHasPackage(givenPython, pip, name)
if (hasPackage) {
try {
info(`Installing ${name} ${version ?? ""} via ${pip}`)
@ -79,20 +88,21 @@ export async function setupPipPackWithPython(
throw new Error(`Failed to install ${name} via ${pip}: ${err}.`)
}
}
} else {
if ((await setupPipPackSystem(name)) === null) {
throw new Error(`Failed to install ${name} as it was not found via ${pip} or the system package manager`)
}
} else if ((await setupPipPackSystem(name)) === null) {
throw new Error(`Failed to install ${name} as it was not found via ${pip} or the system package manager`)
}
const execPaths = await addPythonBaseExecPrefix(givenPython)
const binDir = await findBinDir(execPaths, name)
await addPath(binDir, rcOptions)
const binDir = await finishPipPackageInstall(givenPython, name)
return { binDir }
}
async function finishPipPackageInstall(givenPython: string, name: string) {
const pythonBaseExecPrefix = await addPythonBaseExecPrefix(givenPython)
const binDir = await findBinDir(pythonBaseExecPrefix, name)
await addPath(binDir, rcOptions)
return binDir
}
export async function hasPipx(givenPython: string) {
return (await execa(givenPython, ["-m", "pipx", "--help"], { stdio: "ignore", reject: false })).exitCode === 0
}
@ -153,8 +163,54 @@ async function getPython_(): Promise<string> {
}
const getPython = memoize(getPython_, { promise: true })
async function pipHasPackage(python: string, name: string) {
const result = await execa(python, ["-m", "pip", "-qq", "index", "versions", name], {
type PipxShowType = {
venvs: Record<string, {
metadata: {
main_package: {
package: string
package_or_url: string
apps: string[]
}
}
}>
}
async function pipPackageIsInstalled(python: string, pip: string, name: string) {
try {
if (pip === "pipx") {
const result = await execa(python, ["-m", pip, "list", "--json"], {
stdio: "ignore",
reject: false,
})
if (result.exitCode !== 0 || typeof result.stdout !== "string") {
return false
}
const pipxOut = JSON.parse(result.stdout as unknown as string) as PipxShowType
// search among the venvs
if (name in pipxOut.venvs) {
return true
}
// search among the urls
for (const venv of Object.values(pipxOut.venvs)) {
if (venv.metadata.main_package.package_or_url === name || venv.metadata.main_package.package === name) {
return true
}
}
}
const result = await execa(python, ["-m", pip, "-qq", "show", name], {
stdio: "ignore",
reject: false,
})
return result.exitCode === 0
} catch {
return false
}
}
async function pipHasPackage(python: string, pip: string, name: string) {
const result = await execa(python, ["-m", pip, "-qq", "index", "versions", name], {
stdio: "ignore",
reject: false,
})