lpip.pipxutils

src/lpip/pipxutils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
"""Locate and invoke the pipx executable (ADR-0024). No pipx Python library."""

from __future__ import annotations

import os
import shutil
import subprocess
from collections.abc import Mapping
from pathlib import Path

from lpip.errors import PipxNotFoundError


def find_pipx_executable(*, environ: Mapping[str, str] | None = None) -> Path | None:
    env = environ if environ is not None else os.environ
    found = shutil.which("pipx", path=env.get("PATH"))
    return Path(found) if found else None


def require_pipx(*, environ: Mapping[str, str] | None = None) -> Path:
    found = find_pipx_executable(environ=environ)
    if found is None:
        raise PipxNotFoundError()
    return found


def pipx_install_argv(pipx: str, requirement: str) -> list[str]:
    """Replace an existing pipx app of this name; do not call pipx upgrade."""
    return [pipx, "install", "--force", requirement]


def format_pipx_install_command(pipx: str, requirement: str) -> str:
    return f'{pipx} install --force "{requirement}"'


def run_pipx_install(
    pipx: str,
    requirement: str,
    *,
    environ: Mapping[str, str] | None = None,
) -> int:
    completed = subprocess.run(  # nosec B603
        pipx_install_argv(pipx, requirement),
        env=dict(environ) if environ is not None else None,
    )
    return completed.returncode