From db68caee11914a89bb39fdda5291d0b9893c2ac5 Mon Sep 17 00:00:00 2001 From: m1ngsama Date: Sat, 16 May 2026 22:42:56 +0800 Subject: [PATCH] Prefer pipx over pip3 --break-system-packages for Python tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous fallback chain (pip3 install → pip3 install --break-system-packages) silently writes into the distro Python's site-packages on PEP 668 systems (Debian 12+, Ubuntu 23.04+). The warn message fires after the damage is done. Since the target audience is engineers SSH-ing into Linux servers, the PEP 668 hit rate is high. New order: 1. pipx install (isolated per-tool venvs) 2. pip3 --user (works on pre-PEP-668 Python) 3. --break-system-packages (gated behind CHOPSTICKS_ALLOW_BREAK_SYSTEM=1) 4. fail with a remediation hint Also: - Bootstrap pipx via the system package manager if it's missing. - Warn if $HOME/.local/bin is absent from PATH (pipx and pip --user both install there). Closes #66 --- CHANGELOG.md | 4 ++++ install.sh | 40 +++++++++++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a885aa..2e0b5e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,10 @@ - `install.sh` no longer silently `PlugClean!`s user-added plugins from `~/.vim/plugged`; it now lists undeclared plugin directories first and asks before removing them (`--yes` skips the removal entirely) +- `install.sh` Python tools now prefer `pipx` and `pip3 --user` over + `pip3 install --break-system-packages`; the break-system path is gated + behind `CHOPSTICKS_ALLOW_BREAK_SYSTEM=1` so PEP 668 distros are no + longer silently polluted - `g:loaded_logipat` typo → `g:loaded_logiPat` — logiPat was loading fully (0.478ms wasted) - `get.sh` now refuses to update an existing `~/.vim` git repo unless its origin is chopsticks diff --git a/install.sh b/install.sh index 9a749e9..6b41b1e 100755 --- a/install.sh +++ b/install.sh @@ -1015,19 +1015,45 @@ elif [[ $_I_PYTHON -lt 0 ]] || ! _selected "$_I_PYTHON"; then skip "Python tool suite (skipped by user)" SKIPPED+=("black" "isort" "flake8" "pylint" "yamllint" "sqlfluff") else + # Prefer pipx (isolated per-tool venvs, the recommended path on PEP 668 + # distros like Debian 12+/Ubuntu 23.04+). Bootstrap pipx via the system + # package manager if missing. Only fall back to --break-system-packages + # if the user explicitly opts in. + if ! command -v pipx >/dev/null 2>&1; then + if pkg_install pipx pipx python-pipx pipx 2>/dev/null; then + ok "pipx (recommended Python CLI installer)" + else + warn "pipx not available — falling back to pip3 --user" + fi + fi + + case ":$PATH:" in + *":$HOME/.local/bin:"*) ;; + *) warn "\$HOME/.local/bin is not on PATH — pipx/pip --user tools will not be found" + info "Add to your shell rc: export PATH=\"\$HOME/.local/bin:\$PATH\"" ;; + esac + pip_install() { local pkg="$1" check="${2:-$1}" if command -v "$check" >/dev/null 2>&1; then ok "$pkg (already installed)"; return fi - if pip3 install --quiet "$pkg" 2>/dev/null; then - ok "$pkg"; INSTALLED+=("$pkg") - elif pip3 install --quiet --break-system-packages "$pkg" 2>/dev/null; then - warn "$pkg installed with --break-system-packages (consider using a virtualenv)" - ok "$pkg"; INSTALLED+=("$pkg") - else - fail "$pkg"; FAILED+=("$pkg") + if command -v pipx >/dev/null 2>&1 && pipx install --quiet "$pkg" 2>/dev/null; then + ok "$pkg (via pipx)"; INSTALLED+=("$pkg") + return fi + if pip3 install --quiet --user "$pkg" 2>/dev/null; then + ok "$pkg (via pip --user)"; INSTALLED+=("$pkg") + return + fi + if [[ "${CHOPSTICKS_ALLOW_BREAK_SYSTEM:-0}" == "1" ]] && \ + pip3 install --quiet --break-system-packages "$pkg" 2>/dev/null; then + warn "$pkg installed with --break-system-packages (CHOPSTICKS_ALLOW_BREAK_SYSTEM=1)" + INSTALLED+=("$pkg") + return + fi + fail "$pkg — install pipx, or set CHOPSTICKS_ALLOW_BREAK_SYSTEM=1 to bypass PEP 668" + FAILED+=("$pkg") } pip_install black pip_install isort