Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store pipx data in platform-specific user directories #1001

Merged
merged 16 commits into from Jul 6, 2023
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@
- Fallback to user's log path if the default log path (`$PIPX_HOME/logs`) is not writable to aid with pipx being used for multi-user (e.g. system-wide) installs of applications
- Fix wrong interpreter usage when injecting local pip-installable dependencies into venvs
- add pre-commit hook support
- Confirm the XDG base directory specification
memchr marked this conversation as resolved.
Show resolved Hide resolved

## 1.2.0

Expand Down
2 changes: 1 addition & 1 deletion README.md
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
10 changes: 5 additions & 5 deletions docs/how-pipx-works.md
Expand Up @@ -2,17 +2,17 @@

When installing a package and its binaries (`pipx install package`) pipx will
memchr marked this conversation as resolved.
Show resolved Hide resolved

- 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
2 changes: 1 addition & 1 deletion docs/installation.md
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 `$XDG_DATA_HOME/pipx`, which is typically `~/.local/share/pipx`, 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.
memchr marked this conversation as resolved.
Show resolved Hide resolved

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 Down
4 changes: 2 additions & 2 deletions docs/troubleshooting.md
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
2 changes: 1 addition & 1 deletion src/pipx/commands/environment.py
Expand Up @@ -25,7 +25,7 @@ def environment(value: str) -> ExitCode:
}
if value is None:
for env_variable in environment_variables:
print(f"{env_variable}={environment_variables[env_variable]}")
print(f"{env_variable}={environment_variables[env_variable] or ''} ")
memchr marked this conversation as resolved.
Show resolved Hide resolved
print("")
print("Only PIPX_HOME and PIPX_BIN_DIR can be set by users in the above list.")
elif value in environment_variables:
Expand Down
2 changes: 1 addition & 1 deletion src/pipx/commands/uninstall.py
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
23 changes: 17 additions & 6 deletions src/pipx/constants.py
Expand Up @@ -4,20 +4,31 @@
from pathlib import Path
from textwrap import dedent
from typing import NewType, Optional
from platformdirs import user_cache_path, user_data_path, user_log_path
memchr marked this conversation as resolved.
Show resolved Hide resolved

DEFAULT_PIPX_HOME = Path.home() / ".local/pipx"
DEFAULT_PIPX_BIN_DIR = Path.home() / ".local/bin"
memchr marked this conversation as resolved.
Show resolved Hide resolved
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 DEFAULT_PIPX_HOME.exists() or os.environ.get("PIPX_HOME") is not None:
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"
PIPX_VENV_CACHEDIR = PIPX_HOME / ".cache"
else:
PIPX_HOME = user_data_path("pipx")
memchr marked this conversation as resolved.
Show resolved Hide resolved
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

ExitCode = NewType("ExitCode", int)
Expand Down