Skip to content

Commit

Permalink
feat: skip installation of pip/pipx packages if already installed
Browse files Browse the repository at this point in the history
  • Loading branch information
aminya committed Sep 17, 2024
1 parent 67a1d8d commit 99db110
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 18 deletions.
2 changes: 1 addition & 1 deletion dist/legacy/setup-cpp.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/legacy/setup-cpp.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/modern/setup-cpp.mjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/modern/setup-cpp.mjs.map

Large diffs are not rendered by default.

14 changes: 11 additions & 3 deletions src/python/python.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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...`)
}
}

Expand Down
78 changes: 67 additions & 11 deletions src/utils/setup/setupPipPack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`)
Expand All @@ -79,18 +88,19 @@ 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)
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 }
return binDir
}

export async function hasPipx(givenPython: string) {
Expand Down Expand Up @@ -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,
})
Expand Down

0 comments on commit 99db110

Please sign in to comment.