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

More respectful convention for caching #814

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
46 changes: 42 additions & 4 deletions pip_audit/_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import logging
import os
import platform
import subprocess
import sys
from pathlib import Path
Expand All @@ -28,7 +29,41 @@

_PIP_VERSION = Version(str(pip_api.PIP_VERSION))

_PIP_AUDIT_INTERNAL_CACHE = Path.home() / ".pip-audit-cache"

def _get_internal_cache_path() -> Path:
"""
Returns the default cache directory path used internally by `pip-audit`.

It uses the same pip's default paths to select the caching directory.
See https://pip.pypa.io/en/latest/topics/caching/#default-paths.

On macOS, it will be in `$HOME/Library/Caches/pip-audit`.

On Windows, it will be in `%LOCALAPPDATA%/Cache/pip-audit` and
fallback to the `XDG Base Directory Specification` default folder.

On other OS, it will follow the `XDG Base Directory Specification` and
place the cache in either `$HOME/.cache/pip-audit` or respect `$XDG_CACHE_HOME`
environment variable i-e `$XDG_CACHE_HOME/pip-audit`.
"""
os_platform = platform.system()
default_cache_dir = Path.home() / ".cache" / "pip-audit"

if os_platform == "Windows":
return (
default_cache_dir
if (local_app_data := os.getenv("LOCALAPPDATA")) is None
else Path(local_app_data) / "pip-audit" / "Cache"
)
elif os_platform == "Darwin":
return Path.home() / "Library" / "Caches" / "pip-audit"
else:
# Follow XDG Base Directory Specification
return (
default_cache_dir
if (xdg_cache_home := os.getenv("XDG_CACHE_HOME")) is None
else Path(xdg_cache_home) / "pip-audit"
)


def _get_pip_cache() -> Path:
Expand Down Expand Up @@ -60,6 +95,9 @@ def _get_cache_dir(custom_cache_dir: Path | None, *, use_pip: bool = True) -> Pa
if custom_cache_dir is not None:
return custom_cache_dir

# Retrieve pip-audit's default internal cache path following OS conventions.
pip_audit_cache_dir = _get_internal_cache_path()

# Respect pip's PIP_NO_CACHE_DIR environment setting.
if use_pip and not os.getenv("PIP_NO_CACHE_DIR"):
pip_cache_dir = _get_pip_cache() if _PIP_VERSION >= _MINIMUM_PIP_VERSION else None
Expand All @@ -68,11 +106,11 @@ def _get_cache_dir(custom_cache_dir: Path | None, *, use_pip: bool = True) -> Pa
else:
logger.warning(
f"pip {_PIP_VERSION} doesn't support the `cache dir` subcommand, "
f"using {_PIP_AUDIT_INTERNAL_CACHE} instead"
f"using {pip_audit_cache_dir} instead"
)
return _PIP_AUDIT_INTERNAL_CACHE
return pip_audit_cache_dir
else:
return _PIP_AUDIT_INTERNAL_CACHE
return pip_audit_cache_dir


class _SafeFileCache(FileCache):
Expand Down
55 changes: 54 additions & 1 deletion test/test_cache.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,63 @@
import platform
from pathlib import Path

import pretend # type: ignore
import pytest
from packaging.version import Version

import pip_audit._cache as cache
from pip_audit._cache import _get_cache_dir, _get_pip_cache
from pip_audit._cache import _get_cache_dir, _get_internal_cache_path, _get_pip_cache


@pytest.mark.parametrize(
"sys_platform,expected,is_xdg_set,is_local_app_data_set",
[
pytest.param(
"Linux",
Path.home() / ".cache" / "pip-audit",
False,
False,
id="default XDG's convention cache on Linux",
),
pytest.param(
"Darwin",
Path.home() / "Library" / "Caches" / "pip-audit",
False,
False,
id="default cache on macOS",
),
pytest.param(
"Windows",
Path.home() / ".cache" / "pip-audit",
False,
False,
id="default fallback cache on Windows",
),
pytest.param(
"Linux",
Path("/tmp/foo/cache_dir/pip-audit"),
True,
False,
id="custom cache dir on Linux with XDG_CACHE_HOME set",
),
pytest.param(
"Windows",
Path("/tmp/bar/cache_dir/pip-audit/Cache"),
False,
True,
id="custom cache dir on Windows with LOCALAPPDATA set",
),
],
)
def test_get_internal_cache_path(
monkeypatch, sys_platform, expected, is_xdg_set, is_local_app_data_set
):
monkeypatch.setattr(platform, "system", lambda: sys_platform)
if is_xdg_set:
monkeypatch.setenv("XDG_CACHE_HOME", "/tmp/foo/cache_dir")
if is_local_app_data_set:
monkeypatch.setenv("LOCALAPPDATA", "/tmp/bar/cache_dir")
assert _get_internal_cache_path() == expected


def test_get_cache_dir(monkeypatch):
Expand Down