Repository: pypa/pipx (python)

Issue:
PIPX_GLOBAL_(HOME|BIN_DIR|MAN_DIR) missing from documentation and `pipx environment`
**Describe the bug**

`pipx environment` does not list `PIPX_GLOBAL_(HOME|BIN_DIR|MAN_DIR)` under `Environment variables (set by user)`. Additionally these variables are missing from the documentation of the `environment` and `install` commands, which makes them pretty hard to discover as a user.

**How to reproduce**

```sh
$ pipx environment
Environment variables (set by user):

PIPX_HOME=
PIPX_BIN_DIR=
PIPX_MAN_DIR=
PIPX_SHARED_LIBS=
PIPX_DEFAULT_PYTHON=
PIPX_FETCH_MISSING_PYTHON=
USE_EMOJI=

Derived values (computed by pipx):

PIPX_HOME=/home/user/.local/share/pipx
PIPX_BIN_DIR=/home/user/.local/bin
PIPX_MAN_DIR=/home/user/.local/share/man
PIPX_SHARED_LIBS=/home/user/.local/share/pipx/shared
PIPX_LOCAL_VENVS=/home/user/.local/share/pipx/venvs
PIPX_LOG_DIR=/home/user/.local/state/pipx/log
PIPX_TRASH_DIR=/home/user/.local/share/pipx/trash
PIPX_VENV_CACHEDIR=/home/user/.cache/pipx
PIPX_STANDALONE_PYTHON_CACHEDIR=/home/user/.local/share/pipx/py
PIPX_DEFAULT_PYTHON=/home/user/local/bin/python3.12
USE_EMOJI=true
```

```sh
$ pipx environment --global
Environment variables (set by user):

PIPX_HOME=
PIPX_BIN_DIR=
PIPX_MAN_DIR=
PIPX_SHARED_LIBS=
PIPX_DEFAULT_PYTHON=
PIPX_FETCH_MISSING_PYTHON=
USE_EMOJI=

Derived values (computed by pipx):

PIPX_HOME=/opt/pipx
PIPX_BIN_DIR=/usr/local/bin
PIPX_MAN_DIR=/usr/local/share/man
PIPX_SHARED_LIBS=/opt/pipx/shared
PIPX_LOCAL_VENVS=/opt/pipx/venvs
PIPX_LOG_DIR=/opt/pipx/logs
PIPX_TRASH_DIR=/opt/pipx/.trash
PIPX_VENV_CACHEDIR=/opt/pipx/.cache
PIPX_STANDALONE_PYTHON_CACHEDIR=/opt/pipx/py
PIPX_DEFAULT_PYTHON=/usr/local/bin/python3.12
USE_EMOJI=true

```

```sh
$ pipx environment --help
usage: pipx environment [-h] [--quiet] [--verbose] [--global] [--value VARIABLE]

[...]

Available variables:
PIPX_HOME, PIPX_BIN_DIR, PIPX_MAN_DIR, PIPX_SHARED_LIBS, PIPX_LOCAL_VENVS,
PIPX_LOG_DIR, PIPX_TRASH_DIR, PIPX_VENV_CACHEDIR, PIPX_DEFAULT_PYTHON, USE_EMOJI, PIPX_HOME_ALLOW_SPACE

[...]
```

**Expected behavior**

```sh
$ pipx environment
Environment variables (set by user):

PIPX_HOME=
PIPX_GLOBAL_HOME=             # <-- added
PIPX_BIN_DIR=
PIPX_GLOBAL_BIN_DIR=          # <-- added
PIPX_MAN_DIR=
PIPX_GLOBAL_MAN_DIR=          # <-- added
PIPX_SHARED_LIBS=
PIPX_DEFAULT_PYTHON=
PIPX_FETCH_MISSING_PYTHON=
USE_EMOJI=
PIPX_HOME_ALLOW_SPACE=

Derived values (computed by pipx):

PIPX_HOME=/home/user/.local/share/pipx
PIPX_BIN_DIR=/home/user/.local/bin
PIPX_MAN_DIR=/home/user/.local/share/man
PIPX_SHARED_LIBS=/home/user/.local/share/pipx/shared
PIPX_LOCAL_VENVS=/home/user/.local/share/pipx/venvs
PIPX_LOG_DIR=/home/user/.local/state/pipx/log
PIPX_TRASH_DIR=/home/user/.local/share/pipx/trash
PIPX_VENV_CACHEDIR=/home/user/.cache/pipx
PIPX_STANDALONE_PYTHON_CACHEDIR=/home/user/.local/share/pipx/py
PIPX_DEFAULT_PYTHON=/usr/local/bin/python3.12
USE_EMOJI=true
PIPX_HOME_ALLOW_SPACE=false
```

```sh
$ pipx environment --global
Environment variables (set by user):

PIPX_HOME=
PIPX_GLOBAL_HOME=             # <-- added
PIPX_BIN_DIR=
PIPX_GLOBAL_BIN_DIR=          # <-- added
PIPX_MAN_DIR=
PIPX_GLOBAL_MAN_DIR=          # <-- added
PIPX_SHARED_LIBS=
PIPX_DEFAULT_PYTHON=
PIPX_FETCH_MISSING_PYTHON=
USE_EMOJI=
PIPX_HOME_ALLOW_SPACE=

Derived values (computed by pipx):

PIPX_HOME=/opt/pipx
PIPX_BIN_DIR=/usr/local/bin
PIPX_MAN_DIR=/usr/local/share/man
PIPX_SHARED_LIBS=/opt/pipx/shared
PIPX_LOCAL_VENVS=/opt/pipx/venvs
PIPX_LOG_DIR=/opt/pipx/logs
PIPX_TRASH_DIR=/opt/pipx/trash
PIPX_VENV_CACHEDIR=/opt/pipx/.cache
PIPX_STANDALONE_PYTHON_CACHEDIR=/opt/pipx/py
PIPX_DEFAULT_PYTHON=/usr/local/bin/python3.12
USE_EMOJI=true
PIPX_HOME_ALLOW_SPACE=false
```

```sh
$ pipx environment --help
usage: pipx environment [-h] [--quiet] [--verbose] [--global] [--value VARIABLE]

[...]

Available variables:
PIPX_HOME, PIPX_GLOBAL_HOME, PIPX_BIN_DIR, PIPX_GLOBAL_BIN_DIR,
PIPX_MAN_DIR, PIPX_GLOBAL_MAN_DIR, PIPX_SHARED_LIBS, PIPX_LOCAL_VENVS,
PIPX_LOG_DIR, PIPX_TRASH_DIR, PIPX_VENV_CACHEDIR, PIPX_DEFAULT_PYTHON,
USE_EMOJI, PIPX_HOME_ALLOW_SPACE

[...]
```

Pull request:
Fix #1492: Add PIPX_GLOBAL_(HOME|BIN_DIR|MAN_DIR) documentation and list them in `pipx environment`
Fixed #1492 

- [x] I have added a news fragment under `changelog.d/` (if the patch affects the end users)

## Summary of changes

* Added `PIPX_GLOBAL_HOME`, `PIPX_GLOBAL_BIN_DIR` and `PIPX_GLOBAL_MAN_DIR` to output of `pipx environment` under the `Environment variables (set by user)` section.
* Added documentation of the `PIPX_GLOBAL_*` vars in `pipx --help`, `pipx install --help`, `pipx environment --help` and `installation.md`.

## Test plan

Tested by running

```
$ pipx environment
$ pipx environment --global
$ pipx environment --help
$ pipx install --help
```


Patch:
diff --git a/changelog.d/1492.feature.md b/changelog.d/1492.feature.md
new file mode 100644
index 0000000..8607e05
--- /dev/null
+++ b/changelog.d/1492.feature.md
@@ -0,0 +1,1 @@
+List `PIPX_GLOBAL_[HOME|BIN_DIR|MAN_DIR]` in `pipx environment`.
diff --git a/docs/installation.md b/docs/installation.md
index 90c9a24..1666b69 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -148,23 +148,27 @@ Example configuration for use of the code linter [yapf](https://github.com/googl
 
 The default binary location for pipx-installed apps is `~/.local/bin`. This can be overridden with the environment
 variable `PIPX_BIN_DIR`. The default manual page location for pipx-installed apps is `~/.local/share/man`. This can be
-overridden with the environment variable `PIPX_MAN_DIR`.
+overridden with the environment variable `PIPX_MAN_DIR`. If the `--global` option is used, the default locations are
+`/usr/local/bin` and `/usr/local/share/man` respectively and can be overridden with `PIPX_GLOBAL_BIN_DIR` and
+`PIPX_GLOBAL_MAN_DIR`.
 
 pipx's default virtual environment location is typically `~/.local/share/pipx` on Linux/Unix, `~/.local/pipx` on MacOS
 and `~\pipx` on Windows. For compatibility reasons, if `~/.local/pipx` on Linux, `%USERPROFILE%\AppData\Local\pipx` or
 `~\.local\pipx` on Windows or `~/Library/Application Support/pipx` on MacOS exists, it will be used as the default location instead.
-This can be overridden with the `PIPX_HOME` environment variable.
+This can be overridden with the `PIPX_HOME` environment variable. If the `--global` option is used, the default location is always
+`/opt/pipx` and can be overridden with `PIPX_GLOBAL_HOME`.
 
 In case one of these fallback locations exist, we recommend either manually moving the pipx files to the new default location
 (see the [Moving your pipx installation](installation.md#moving-your-pipx-installation) section of the docs), or setting the
 `PIPX_HOME` environment variable (discarding files existing in the fallback location).
 
-As an example, you can install global apps accessible by all users on your system with the following command (on MacOS,
+As an example, you can install global apps accessible by all users on your system with either of the following commands (on MacOS,
 Linux, and Windows WSL):
 
 ```
-sudo PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin PIPX_MAN_DIR=/usr/local/share/man pipx install PACKAGE
-# Example: $ sudo PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin PIPX_MAN_DIR=/usr/local/share/man pipx install cowsay
+sudo PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin PIPX_MAN_DIR=/usr/local/share/man pipx install <PACKAGE>
+# or shorter (with pipx>=1.5.0)
+sudo pipx install --global <PACKAGE>
 ```
 
 > [!NOTE]
diff --git a/src/pipx/commands/environment.py b/src/pipx/commands/environment.py
index 78cb734..e824081 100644
--- a/src/pipx/commands/environment.py
+++ b/src/pipx/commands/environment.py
@@ -6,19 +6,23 @@ from pipx.emojis import EMOJI_SUPPORT
 from pipx.interpreter import DEFAULT_PYTHON
 from pipx.util import PipxError
 
+ENVIRONMENT_VARIABLES = [
+    "PIPX_HOME",
+    "PIPX_GLOBAL_HOME",
+    "PIPX_BIN_DIR",
+    "PIPX_GLOBAL_BIN_DIR",
+    "PIPX_MAN_DIR",
+    "PIPX_GLOBAL_MAN_DIR",
+    "PIPX_SHARED_LIBS",
+    "PIPX_DEFAULT_PYTHON",
+    "PIPX_FETCH_MISSING_PYTHON",
+    "USE_EMOJI",
+    "PIPX_HOME_ALLOW_SPACE",
+]
+
 
 def environment(value: str) -> ExitCode:
     """Print a list of environment variables and paths used by pipx"""
-    environment_variables = [
-        "PIPX_HOME",
-        "PIPX_BIN_DIR",
-        "PIPX_MAN_DIR",
-        "PIPX_SHARED_LIBS",
-        "PIPX_DEFAULT_PYTHON",
-        "PIPX_FETCH_MISSING_PYTHON",
-        "USE_EMOJI",
-        "PIPX_HOME_ALLOW_SPACE",
-    ]
     derived_values = {
         "PIPX_HOME": paths.ctx.home,
         "PIPX_BIN_DIR": paths.ctx.bin_dir,
@@ -36,7 +40,7 @@ def environment(value: str) -> ExitCode:
     if value is None:
         print("Environment variables (set by user):")
         print("")
-        for env_variable in environment_variables:
+        for env_variable in ENVIRONMENT_VARIABLES:
             env_value = os.getenv(env_variable, "")
             print(f"{env_variable}={env_value}")
         print("")
diff --git a/src/pipx/main.py b/src/pipx/main.py
index 76cf1dc..4800f64 100644
--- a/src/pipx/main.py
+++ b/src/pipx/main.py
@@ -22,6 +22,7 @@ from packaging.utils import canonicalize_name
 from pipx import commands, constants, paths
 from pipx.animate import hide_cursor, show_cursor
 from pipx.colors import bold, green
+from pipx.commands.environment import ENVIRONMENT_VARIABLES
 from pipx.constants import (
     EXIT_CODE_OK,
     EXIT_CODE_SPECIFIED_PYTHON_EXECUTABLE_NOT_FOUND,
@@ -85,8 +86,11 @@ PIPX_DESCRIPTION += pipx_wrap(
     """
     optional environment variables:
       PIPX_HOME              Overrides default pipx location. Virtual Environments will be installed to $PIPX_HOME/venvs.
+      PIPX_GLOBAL_HOME       Used instead of PIPX_HOME when the `--global` option is given.
       PIPX_BIN_DIR           Overrides location of app installations. Apps are symlinked or copied here.
+      PIPX_GLOBAL_BIN_DIR    Used instead of PIPX_BIN_DIR when the `--global` option is given.
       PIPX_MAN_DIR           Overrides location of manual pages installations. Manual pages are symlinked or copied here.
+      PIPX_GLOBAL_MAN_DIR    Used instead of PIPX_MAN_DIR when the `--global` option is given.
       PIPX_DEFAULT_PYTHON    Overrides default python used for commands.
       USE_EMOJI              Overrides emoji behavior. Default value varies based on platform.
       PIPX_HOME_ALLOW_SPACE  Overrides default warning on spaces in the home path
@@ -122,15 +126,24 @@ INSTALL_DESCRIPTION = textwrap.dedent(
 
     The PACKAGE_SPEC argument is passed directly to `pip install`.
 
-    The default virtual environment location is {paths.DEFAULT_PIPX_HOME}
-    and can be overridden by setting the environment variable `PIPX_HOME`
-    (Virtual Environments will be installed to `$PIPX_HOME/venvs`).
+    Virtual Environments will be installed to `$PIPX_HOME/venvs`.
+    The default pipx home location is {paths.DEFAULT_PIPX_HOME} and can
+    be overridden by setting the environment variable `PIPX_HOME`.
+    If the `--global` option is used, the default pipx home location
+    instead is {paths.DEFAULT_PIPX_GLOBAL_HOME} and can be overridden
+    by setting the environment variable `PIPX_GLOBAL_HOME`.
 
     The default app location is {paths.DEFAULT_PIPX_BIN_DIR} and can be
     overridden by setting the environment variable `PIPX_BIN_DIR`.
+    If the `--global` option is used, the default app location instead
+    is {paths.DEFAULT_PIPX_GLOBAL_BIN_DIR} and can be overridden by
+    setting the environment variable `PIPX_GLOBAL_BIN_DIR`.
 
     The default manual pages location is {paths.DEFAULT_PIPX_MAN_DIR} and
     can be overridden by setting the environment variable `PIPX_MAN_DIR`.
+    If the `--global` option is used, the default manual pages location
+    instead is {paths.DEFAULT_PIPX_GLOBAL_MAN_DIR} and can be overridden
+    by setting the environment variable `PIPX_GLOBAL_MAN_DIR`.
 
     The default python executable used to install a package is
     {DOC_DEFAULT_PYTHON} and can be overridden
@@ -907,10 +920,9 @@ def _add_environment(subparsers: argparse._SubParsersAction, shared_parser: argp
             variables and platform specific default values.
 
             Available variables:
-            PIPX_HOME, PIPX_BIN_DIR, PIPX_MAN_DIR, PIPX_SHARED_LIBS, PIPX_LOCAL_VENVS,
-            PIPX_LOG_DIR, PIPX_TRASH_DIR, PIPX_VENV_CACHEDIR, PIPX_DEFAULT_PYTHON, USE_EMOJI, PIPX_HOME_ALLOW_SPACE
             """
-        ),
+        )
+        + textwrap.fill(", ".join(ENVIRONMENT_VARIABLES), break_long_words=False),
         parents=[shared_parser],
     )
     p.add_argument("--value", "-V", metavar="VARIABLE", help="Print the value of the variable.")


Test patch:
diff --git a/tests/test_environment.py b/tests/test_environment.py
index e2b0683..e382154 100644
--- a/tests/test_environment.py
+++ b/tests/test_environment.py
@@ -3,6 +3,7 @@ from pathlib import Path
 
 from helpers import run_pipx_cli, skip_if_windows
 from pipx import paths
+from pipx.commands.environment import ENVIRONMENT_VARIABLES
 from pipx.paths import get_expanded_environ
 
 
@@ -18,10 +19,8 @@ def test_cli(pipx_temp_env, monkeypatch, capsys):
     assert fnmatch.fnmatch(captured.out, "*PIPX_TRASH_DIR=*subdir/pipxhome/.trash*")
     assert fnmatch.fnmatch(captured.out, "*PIPX_VENV_CACHEDIR=*subdir/pipxhome/.cache*")
     # Checking just for the sake of completeness
-    assert "PIPX_DEFAULT_PYTHON" in captured.out
-    assert "USE_EMOJI" in captured.out
-    assert "PIPX_HOME_ALLOW_SPACE" in captured.out
-    assert "Environment variables (set by user):" in captured.out
+    for env_var in ENVIRONMENT_VARIABLES:
+        assert env_var in captured.out
 
 
 def test_cli_with_args(monkeypatch, capsys):
@@ -88,6 +87,5 @@ def test_cli_global(pipx_temp_env, monkeypatch, capsys):
     assert fnmatch.fnmatch(captured.out, "*PIPX_TRASH_DIR=*global/pipxhome/.trash*")
     assert fnmatch.fnmatch(captured.out, "*PIPX_VENV_CACHEDIR=*global/pipxhome/.cache*")
     # Checking just for the sake of completeness
-    assert "PIPX_DEFAULT_PYTHON" in captured.out
-    assert "USE_EMOJI" in captured.out
-    assert "PIPX_DEFAULT_PYTHON" in captured.out
+    for env_var in ENVIRONMENT_VARIABLES:
+        assert env_var in captured.out