Skip to content

Commit

Permalink
Store pipx data in platform-specific user directories (#1001)
Browse files Browse the repository at this point in the history
Co-authored-by: chrysle <[email protected]>
Co-authored-by: Tzu-ping Chung <[email protected]>
Co-authored-by: Jason Lam <[email protected]>
  • Loading branch information
4 people authored Jul 6, 2023
1 parent c538eb1 commit c3d8de9
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Fix wrong interpreter usage when injecting local pip-installable dependencies into venvs
- add pre-commit hook support
- Don't show escaped backslashes for paths in console output
- Move `pipx` paths to ensure compatibility with the platform-specific user directories
- [docs] Add more examples for `pipx run`
- [docs] Add subsection to make README easier to read

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ done! ✨ 🌟 ✨
>> pipx list
venvs are in /home/user/.local/pipx/venvs
venvs are in /home/user/.local/share/pipx/venvs
apps are exposed on your $PATH at /home/user/.local/bin
package pycowsay 2.0.3, Python 3.7.3
- pycowsay
Expand Down
12 changes: 6 additions & 6 deletions docs/how-pipx-works.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
## How it Works

When installing a package and its binaries (`pipx install package`) pipx will
When installing a package and its binaries on linux (`pipx install package`) pipx will

- create directory `~/.local/pipx/venvs/PACKAGE`
- create or re-use a shared virtual environment that contains shared packaging libraries `pip`, `setuptools` and `wheel` in `~/.local/pipx/shared/`
- create directory `~/.local/share/pipx/venvs/PACKAGE`
- create or re-use a shared virtual environment that contains shared packaging libraries `pip`, `setuptools` and `wheel` in `~/.local/share/pipx/shared/`
- ensure all packaging libraries are updated to their latest versions
- create a Virtual Environment in `~/.local/pipx/venvs/PACKAGE` that uses the shared pip mentioned above but otherwise is isolated (pipx uses a [.pth file]( https://docs.python.org/3/library/site.html) to do this)
- create a Virtual Environment in `~/.local/share/pipx/venvs/PACKAGE` that uses the shared pip mentioned above but otherwise is isolated (pipx uses a [.pth file]( https://docs.python.org/3/library/site.html) to do this)
- install the desired package in the Virtual Environment
- expose binaries at `~/.local/bin` that point to new binaries in `~/.local/pipx/venvs/PACKAGE/bin` (such as `~/.local/bin/black` -> `~/.local/pipx/venvs/black/bin/black`)
- expose binaries at `~/.local/bin` that point to new binaries in `~/.local/share/pipx/venvs/PACKAGE/bin` (such as `~/.local/bin/black` -> `~/.local/share/pipx/venvs/black/bin/black`)
- As long as `~/.local/bin/` is on your PATH, you can now invoke the new binaries globally

When running a binary (`pipx run BINARY`), pipx will

- create or re-use a shared virtual environment that contains shared packaging libraries `pip`, `setuptools` and `wheel` in `~/.local/pipx/shared/`
- create or re-use a shared virtual environment that contains shared packaging libraries `pip`, `setuptools` and `wheel` in `~/.local/share/pipx/shared/`
- ensure all packaging libraries are updated to their latest versions
- create a temporary directory (or reuse a cached virtual environment for this package) with a name based on a hash of the attributes that make the run reproducible. This includes things like the package name, spec, python version, and pip arguments.
- create a Virtual Environment inside it with `python -m venv`
Expand Down
17 changes: 16 additions & 1 deletion docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ 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`.

pipx's default virtual environment location is `~/.local/pipx`. This can be overridden with the environment variable `PIPX_HOME`.
pipx's default virtual environment location is typically `~/.local/share/pipx` on Linux/Unix, `%USERPROFILE%\AppData\Local\pipx` on Windows and `~/Library/Application Support/pipx` on macOS, and for compatibility reasons, if `~/.local/pipx` exists, it will be used as the default location instead. This can be overridden with the `PIPX_HOME` environment variable.

As an example, you can install global apps accessible by all users on your system with the following command (on MacOS, Linux, and Windows WSL):

Expand All @@ -71,6 +71,21 @@ sudo PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install PACKAGE
# Example: $ sudo PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install cowsay
```

!!! note

After version 1.2.0, the default pipx paths have been moved from `~/.local/pipx` to specific user data directories on each platform using [platformdirs](https://pypi.org/project/platformdirs/) library

| Old Path | New Path |
| ---------------------- | ------------------------------------------ |
| `~/.local/pipx/.trash` | `platformdirs.user_data_dir()/pipx/trash` |
| `~/.local/pipx/shared` | `platformdirs.user_data_dir()/pipx/shared` |
| `~/.local/pipx/venvs` | `platformdirs.user_data_dir()/pipx/venv` |
| `~/.local/pipx/.cache` | `platformdirs.user_cache_dir()/pipx` |
| `~/.local/pipx/logs` | `platformdirs.user_log_dir()/pipx/log` |

`user_data_dir()`, `user_cache_dir()` and `user_log_dir()` resolve to appropriate platform-specific user data, cache and log directories.
See the [platformdirs documentation](https://platformdirs.readthedocs.io/en/latest/api.html#platforms) for details.

## Upgrade pipx

On macOS:
Expand Down
22 changes: 20 additions & 2 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ Reference: [pip Environment Variables](https://pip.pypa.io/en/stable/user_guide/

## `pipx` log files
Pipx records a verbose log file for every `pipx` command invocation. The logs
for the last 10 `pipx` commands can be found in `$PIPX_HOME/logs` or user's log path
for the last 10 `pipx` commands can be found in `$XDG_STATE_HOME/pipx/logs` or user's log path
if the former is not writable by the user.

For most users this location is `~/.local/pipx/logs`, where `~` is your home
For most users this location is `~/.local/state/pipx/logs`, where `~` is your home
directory.

## Debian, Ubuntu issues
Expand Down Expand Up @@ -119,3 +119,21 @@ To clean up after this experiment:
```
rm -rf test_venv
```

## Pipx files not in expected locations according to documentation

The default PIPX_HOME is `~/.local/pipx`, prior to the adoption of the XDG base
directory specification after version 1.2.0. To maintain compatibility with older
versions, pipx will automatically detect the old paths and use them accordingly.
For a map of old and new paths, See [Installation](installation.md#installation-options)

To migrate from the old path to the new path, you can remove the `~/.local/pipx` directory and
reinstall all packages.

For example, on Linux systems, you could read out `pipx`'s package information in JSON via `jq` (which you might need to install first):

```
packages=($(pipx list --json | jq '.venvs | keys[]' -r))
rm -rf ~/.local/pipx
for p in ${packages[@]}; do pipx install "$p"; done
```
2 changes: 1 addition & 1 deletion src/pipx/commands/uninstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def _get_venv_bin_dir_app_paths(venv: Venv, local_bin_dir: Path) -> Set[Path]:
# We'll take our best guess on what to uninstall here based on symlink
# location for symlink-capable systems.
# The heuristic here is any symlink in ~/.local/bin pointing to
# .local/pipx/venvs/VENV_NAME/{bin,Scripts} should be uninstalled.
# .local/share/pipx/venvs/VENV_NAME/{bin,Scripts} should be uninstalled.

# For non-symlink systems we give up and return an empty set.
if not can_symlink(local_bin_dir):
Expand Down
27 changes: 20 additions & 7 deletions src/pipx/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,32 @@
from textwrap import dedent
from typing import NewType, Optional

DEFAULT_PIPX_HOME = Path.home() / ".local/pipx"
from platformdirs import user_cache_path, user_data_path, user_log_path

DEFAULT_PIPX_HOME = user_data_path("pipx")
FALLBACK_PIPX_HOME = Path.home() / ".local/pipx"
DEFAULT_PIPX_BIN_DIR = Path.home() / ".local/bin"
PIPX_HOME = Path(os.environ.get("PIPX_HOME", DEFAULT_PIPX_HOME)).resolve()
PIPX_LOCAL_VENVS = PIPX_HOME / "venvs"
PIPX_LOG_DIR = PIPX_HOME / "logs"
DEFAULT_PIPX_SHARED_LIBS = PIPX_HOME / "shared"
PIPX_TRASH_DIR = PIPX_HOME / ".trash"

if FALLBACK_PIPX_HOME.exists() or os.environ.get("PIPX_HOME") is not None:
PIPX_HOME = Path(os.environ.get("PIPX_HOME", FALLBACK_PIPX_HOME)).resolve()
PIPX_LOCAL_VENVS = PIPX_HOME / "venvs"
PIPX_LOG_DIR = PIPX_HOME / "logs"
DEFAULT_PIPX_SHARED_LIBS = PIPX_HOME / "shared"
PIPX_TRASH_DIR = PIPX_HOME / ".trash"
PIPX_VENV_CACHEDIR = PIPX_HOME / ".cache"
else:
PIPX_HOME = DEFAULT_PIPX_HOME
PIPX_LOCAL_VENVS = PIPX_HOME / "venvs"
PIPX_LOG_DIR = user_log_path("pipx")
DEFAULT_PIPX_SHARED_LIBS = PIPX_HOME / "shared"
PIPX_TRASH_DIR = PIPX_HOME / "trash"
PIPX_VENV_CACHEDIR = user_cache_path("pipx")

PIPX_SHARED_LIBS = Path(
os.environ.get("PIPX_SHARED_LIBS", DEFAULT_PIPX_SHARED_LIBS)
).resolve()
PIPX_SHARED_PTH = "pipx_shared.pth"
LOCAL_BIN_DIR = Path(os.environ.get("PIPX_BIN_DIR", DEFAULT_PIPX_BIN_DIR)).resolve()
PIPX_VENV_CACHEDIR = PIPX_HOME / ".cache"
TEMP_VENV_EXPIRATION_THRESHOLD_DAYS = 14
MINIMUM_PYTHON_VERSION = "3.8"

Expand Down

0 comments on commit c3d8de9

Please sign in to comment.