Repository: pypa/pipx
Pull request #793 (2022-01-13 10:44:13)

Add `pipx environment` command
<!-- add an 'x' in the brackets below -->
* [x] I have added an entry to `docs/changelog.md`

## Summary of changes

Add `pipx environment` command.
See #713 

Example output:
```
$ pipx environment
PIPX_HOME=/home/user/.local/pipx
PIPX_BIN_DIR=/home/user/.local/bin
PIPX_SHARED_LIBS=/home/user/.local/pipx/shared
PIPX_LOCAL_VENVS=/home/user/.local/pipx/venvs
PIPX_LOG_DIR=/home/user/.local/pipx/logs
PIPX_TRASH_DIR=/home/user/.local/pipx/.trash
PIPX_VENV_CACHEDIR=/home/user/.local/pipx/.cache
```

```
$ pipx environment --value PIPX_HOME
/home/user/.local/pipx
```

## Test plan
<!-- provide evidence of testing, preferably with command(s) that can be copy+pasted by others -->
Tested by running
```
pipx install .
pipx environment
pipx environment --value PIPX_HOME
pipx environment -v PIPX_LOCAL_VENVS
pipx environment --value=PIPX_BIN_DIR
```



Patch:
diff --git a/docs/changelog.md b/docs/changelog.md
index d3939c5..d880c85 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -1,10 +1,12 @@
 dev
 
+
 - Fix encoding issue on Windows when pip fails to install a package
 - [docs] Fix the command for [installing development version](https://pypa.github.io/pipx/installation/#install-pipx-development-versions). (#801)
 - [docs] Fix test status badge in readme file
 - [dev] Change test names
 - Improve the behaviour of `shlex.split` on Windows, so paths on Windows can be handled peoperly when they are passed in `--pip-args`. (#794)
+- Add `pipx environment` command. (#793)
 - Add `list --short` option to list only package names. (#804)
 
   1.0.0
diff --git a/src/pipx/commands/__init__.py b/src/pipx/commands/__init__.py
index b8f9cc0..047775e 100644
--- a/src/pipx/commands/__init__.py
+++ b/src/pipx/commands/__init__.py
@@ -1,4 +1,5 @@
 from pipx.commands.ensure_path import ensure_pipx_paths
+from pipx.commands.environment import environment
 from pipx.commands.inject import inject
 from pipx.commands.install import install
 from pipx.commands.list_packages import list_packages
@@ -21,4 +22,5 @@ __all__ = [
     "list_packages",
     "run_pip",
     "ensure_pipx_paths",
+    "environment",
 ]
diff --git a/src/pipx/commands/environment.py b/src/pipx/commands/environment.py
new file mode 100644
index 0000000..461bcb7
--- /dev/null
+++ b/src/pipx/commands/environment.py
@@ -0,0 +1,36 @@
+from pipx.constants import (
+    EXIT_CODE_OK,
+    LOCAL_BIN_DIR,
+    PIPX_HOME,
+    PIPX_LOCAL_VENVS,
+    PIPX_LOG_DIR,
+    PIPX_SHARED_LIBS,
+    PIPX_TRASH_DIR,
+    PIPX_VENV_CACHEDIR,
+    ExitCode,
+)
+from pipx.util import PipxError
+
+
+def environment(value: str) -> ExitCode:
+    """Print a list of variables used in `pipx.constants`"""
+    environment_variables = {
+        "PIPX_HOME": PIPX_HOME,
+        "PIPX_BIN_DIR": LOCAL_BIN_DIR,
+        "PIPX_SHARED_LIBS": PIPX_SHARED_LIBS,
+        "PIPX_LOCAL_VENVS": PIPX_LOCAL_VENVS,
+        "PIPX_LOG_DIR": PIPX_LOG_DIR,
+        "PIPX_TRASH_DIR": PIPX_TRASH_DIR,
+        "PIPX_VENV_CACHEDIR": PIPX_VENV_CACHEDIR,
+    }
+    if value is None:
+        for env_variable in environment_variables:
+            print(f"{env_variable}={environment_variables[env_variable]}")
+        print("")
+        print("Only PIPX_HOME and PIPX_BIN_DIR can be set by users in the above list.")
+    elif value in environment_variables:
+        print(environment_variables[value])
+    else:
+        raise PipxError("Variable not found.")
+
+    return EXIT_CODE_OK
diff --git a/src/pipx/main.py b/src/pipx/main.py
index f5520b0..c615760 100644
--- a/src/pipx/main.py
+++ b/src/pipx/main.py
@@ -64,8 +64,8 @@ PIPX_DESCRIPTION += pipx_wrap(
     optional environment variables:
       PIPX_HOME             Overrides default pipx location. Virtual Environments will be installed to $PIPX_HOME/venvs.
       PIPX_BIN_DIR          Overrides location of app installations. Apps are symlinked or copied here.
-      USE_EMOJI             Overrides emoji behavior. Default value varies based on platform.
       PIPX_DEFAULT_PYTHON   Overrides default python used for commands.
+      USE_EMOJI             Overrides emoji behavior. Default value varies based on platform.
     """,
     subsequent_indent=" " * 24,  # match the indent of argparse options
     keep_newlines=True,
@@ -275,6 +275,8 @@ def run_pipx_command(args: argparse.Namespace) -> ExitCode:  # noqa: C901
     elif args.command == "completions":
         print(constants.completion_instructions)
         return ExitCode(0)
+    elif args.command == "environment":
+        return commands.environment(value=args.value)
     else:
         raise PipxError(f"Unknown command {args.command}")
 
@@ -609,6 +611,27 @@ def _add_ensurepath(subparsers: argparse._SubParsersAction) -> None:
     )
 
 
+def _add_environment(subparsers: argparse._SubParsersAction) -> None:
+    p = subparsers.add_parser(
+        "environment",
+        formatter_class=LineWrapRawTextHelpFormatter,
+        help=("Print a list of variables used in pipx.constants."),
+        description=textwrap.dedent(
+            """
+            Available variables:
+            PIPX_HOME, PIPX_BIN_DIR, PIPX_SHARED_LIBS, PIPX_LOCAL_VENVS, PIPX_LOG_DIR,
+            PIPX_TRASH_DIR, PIPX_VENV_CACHEDIR
+
+            Only PIPX_HOME and PIPX_BIN_DIR can be set by users in the above list.
+
+            """
+        ),
+    )
+    p.add_argument(
+        "--value", "-v", metavar="VARIABLE", help="Print the value of the variable."
+    )
+
+
 def get_command_parser() -> argparse.ArgumentParser:
     venv_container = VenvContainer(constants.PIPX_LOCAL_VENVS)
 
@@ -637,6 +660,7 @@ def get_command_parser() -> argparse.ArgumentParser:
     _add_run(subparsers)
     _add_runpip(subparsers, completer_venvs.use)
     _add_ensurepath(subparsers)
+    _add_environment(subparsers)
 
     parser.add_argument("--version", action="store_true", help="Print version and exit")
     subparsers.add_parser(


Test patch:
diff --git a/tests/test_environment.py b/tests/test_environment.py
new file mode 100644
index 0000000..d15cabe
--- /dev/null
+++ b/tests/test_environment.py
@@ -0,0 +1,31 @@
+from helpers import run_pipx_cli
+
+
+def test_cli(monkeypatch, capsys):
+    assert not run_pipx_cli(["environment"])
+    captured = capsys.readouterr()
+    assert "PIPX_HOME" in captured.out
+    assert "PIPX_BIN_DIR" in captured.out
+    assert "PIPX_SHARED_LIBS" in captured.out
+    assert "PIPX_LOCAL_VENVS" in captured.out
+    assert "PIPX_LOG_DIR" in captured.out
+    assert "PIPX_TRASH_DIR" in captured.out
+    assert "PIPX_VENV_CACHEDIR" in captured.out
+    assert (
+        "Only PIPX_HOME and PIPX_BIN_DIR can be set by users in the above list."
+        in captured.out
+    )
+
+
+def test_cli_with_args(monkeypatch, capsys):
+    assert not run_pipx_cli(["environment", "--value", "PIPX_HOME"])
+    assert not run_pipx_cli(["environment", "--value", "PIPX_BIN_DIR"])
+    assert not run_pipx_cli(["environment", "--value", "PIPX_SHARED_LIBS"])
+    assert not run_pipx_cli(["environment", "--value", "PIPX_LOCAL_VENVS"])
+    assert not run_pipx_cli(["environment", "--value", "PIPX_LOG_DIR"])
+    assert not run_pipx_cli(["environment", "--value", "PIPX_TRASH_DIR"])
+    assert not run_pipx_cli(["environment", "--value", "PIPX_VENV_CACHEDIR"])
+
+    assert run_pipx_cli(["environment", "--value", "SSS"])
+    captured = capsys.readouterr()
+    assert "Variable not found." in captured.err