Skip to content
Open
1 change: 1 addition & 0 deletions changelog.d/1650.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add PIPX_DISABLE_SHARED_LIBS_AUTO_UPGRADE environment variable to disable automatic shared library upgrades.
15 changes: 15 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,18 @@ This example pins `pip` (temporarily, until the next automatic upgrade, if that
```shell
> pipx upgrade-shared --pip-args=pip==24.0
```
### Disabling automatic shared library upgrades

By default, pipx automatically upgrades shared libraries (like pip, setuptools, and wheel) when you install, inject, reinstall, run, or upgrade packages. This ensures that the shared libraries are kept up-to-date. However, in some cases (e.g., when you need to maintain compatibility with an older Python version), you may want to disable this automatic upgrade behavior.

You can disable automatic shared library upgrades by setting the `PIPX_DISABLE_SHARED_LIBS_AUTO_UPGRADE` environment variable:

``shell
> export PIPX_DISABLE_SHARED_LIBS_AUTO_UPGRADE=1 # Linux/MacOS
> # or
> set PIPX_DISABLE_SHARED_LIBS_AUTO_UPGRADE=1 # Windows (cmd)
> # or
> $env:PIPX_DISABLE_SHARED_LIBS_AUTO_UPGRADE=1 # Windows (PowerShell)
``

With this environment variable set, pipx will skip automatic upgrades of shared libraries. You can still manually upgrade them using `pipx upgrade-shared`.
1 change: 1 addition & 0 deletions src/pipx/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
MINIMUM_PYTHON_VERSION = "3.9"
MAN_SECTIONS = [f"man{i}" for i in range(1, 10)]
FETCH_MISSING_PYTHON = os.environ.get("PIPX_FETCH_MISSING_PYTHON", False)
DISABLE_SHARED_LIBS_AUTO_UPGRADE = os.environ.get("PIPX_DISABLE_SHARED_LIBS_AUTO_UPGRADE", False)


ExitCode = NewType("ExitCode", int)
Expand Down
4 changes: 2 additions & 2 deletions src/pipx/venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from packaging.utils import canonicalize_name

from pipx.animate import animate
from pipx.constants import PIPX_SHARED_PTH, ExitCode
from pipx.constants import DISABLE_SHARED_LIBS_AUTO_UPGRADE, PIPX_SHARED_PTH, ExitCode
from pipx.emojis import hazard
from pipx.interpreter import DEFAULT_PYTHON
from pipx.package_specifier import (
Expand Down Expand Up @@ -106,7 +106,7 @@ def check_upgrade_shared_libs(self, verbose: bool, pip_args: List[str], force_up
"""
if self._existing and self.uses_shared_libs:
if shared_libs.is_valid:
if force_upgrade or shared_libs.needs_upgrade:
if not DISABLE_SHARED_LIBS_AUTO_UPGRADE and (force_upgrade or shared_libs.needs_upgrade):
shared_libs.upgrade(verbose=verbose, pip_args=pip_args)
else:
shared_libs.create(verbose=verbose, pip_args=pip_args)
Expand Down
29 changes: 29 additions & 0 deletions tests/test_shared_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,32 @@ def test_auto_update_shared_libs(capsys, pipx_ultra_temp_env, mtime_minus_now, n
os.utime(shared_libs.shared_libs.pip_path, (access_time, mtime_minus_now + now))

assert shared_libs.shared_libs.needs_upgrade is needs_upgrade


def test_disable_auto_upgrade_env_var(capsys, pipx_ultra_temp_env, monkeypatch):
"""Test that PIPX_DISABLE_SHARED_LIBS_AUTO_UPGRADE prevents automatic upgrades."""
# Set the environment variable
monkeypatch.setenv("PIPX_DISABLE_SHARED_LIBS_AUTO_UPGRADE", "1")

# Reimport to pick up the new environment variable value
from importlib import reload

import pipx.constants

reload(pipx.constants)

# Verify the constant is set
from pipx.constants import DISABLE_SHARED_LIBS_AUTO_UPGRADE

assert DISABLE_SHARED_LIBS_AUTO_UPGRADE == "1"

# Install a package which normally triggers auto-upgrade
from helpers import run_pipx_cli

assert run_pipx_cli(["install", "pycowsay"]) == 0

# Verify shared libs were NOT automatically upgraded during install
# by checking the logs/output
capsys.readouterr()
# Note: The exact assertion may need adjustment based on actual output
# The key is that we should NOT see upgrade messages when env var is set