Skip to content

Commit

Permalink
Remove notices cache with package caches
Browse files Browse the repository at this point in the history
  • Loading branch information
marcoesters committed Nov 22, 2024
1 parent e41d1e3 commit 90996a7
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 46 deletions.
38 changes: 20 additions & 18 deletions src/entry_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,13 @@ def __call__(self, parser, namespace, values, option_string=None):
help="Path to the conda directory to uninstall.",
)
uninstall_subcommand.add_argument(
"--conda-clean",
"--clean-caches",
action="store_true",
required=False,
help=(
"Run conda --clean --all to remove package caches outside the installation directory."
" This is only useful when pkgs_dirs is set in a .condarc file."
"Removes the notices cache and runs conda --clean --all to clean package caches"
" outside the installation directory."
" This is especially useful when pkgs_dirs is set in a .condarc file."
" Not recommended with multiple conda installations when softlinks are enabled."
),
)
Expand All @@ -179,12 +180,12 @@ def __call__(self, parser, namespace, values, option_string=None):
),
)
uninstall_subcommand.add_argument(
"--remove-conda-caches",
"--remove-user-data",
action="store_true",
required=False,
help=(
"Remove all cache directories created by conda."
" This includes the ~/.conda directory, the notice chache, and anaconda-client data."
"Removes all non-cache directories created by conda."
" This includes the ~/.conda directory and anaconda-client data."
" Not recommended when multiple conda installations are on the system"
" or when running on an environments directory."
),
Expand Down Expand Up @@ -363,9 +364,9 @@ def _get_init_reverse_plan(

def _constructor_uninstall_subcommand(
uninstall_dir: str,
conda_clean: bool = False,
clean_caches: bool = False,
remove_condarcs: str | None = None,
remove_conda_caches: bool = False,
remove_user_data: bool = False,
):
"""
Remove a conda prefix or a directory containing conda environments.
Expand Down Expand Up @@ -523,7 +524,9 @@ def _remove_config_file_and_parents(file: Path):
if delete_uninstall_prefix:
_remove_file_directory(uninstall_prefix)

if conda_clean:
if clean_caches:
print("Cleaning cache directories.")
from conda.notices.cache import get_notices_cache_dir
conda_main("clean", "--all", "-y")
# Delete empty package cache directories
for directory in context.pkgs_dirs:
Expand All @@ -534,6 +537,9 @@ def _remove_config_file_and_parents(file: Path):
if all(file in expected_files for file in pkgs_dir.iterdir()):
_remove_file_directory(pkgs_dir)

notices_dir = Path(get_notices_cache_dir()).expanduser()
_remove_config_file_and_parents(notices_dir)

if remove_condarcs:
print("Removing .condarc files...")
for config_file in context.config_files:
Expand All @@ -547,24 +553,20 @@ def _remove_config_file_and_parents(file: Path):
continue
_remove_config_file_and_parents(config_file)

if remove_conda_caches:
if remove_user_data:
from conda.gateways.anaconda_client import _get_binstar_token_directory
from conda.notices.cache import get_notices_cache_dir

print("Removing config and cache directories...")
print("Removing user data...")
_remove_file_directory(Path("~/.conda").expanduser())
# The binstar token is either `$BINSTAR_TOKEN_DIR/data`,
# or `binstar/ContinuumIO` (Windows) or `binstar` inside the
# user cache directory
binstar_token_dir = Path(_get_binstar_token_directory())
if "BINSTAR_TOKEN_DIR" not in os.environ or sys.platform == "win32":
if "BINSTAR_CONFIG_DIR" in os.environ or sys.platform == "win32":
_remove_file_directory(binstar_token_dir.parent)
else:
_remove_file_directory(binstar_token_dir)

notices_dir = Path(get_notices_cache_dir()).expanduser()
_remove_config_file_and_parents(notices_dir)


def _constructor_subcommand():
r"""
Expand All @@ -580,9 +582,9 @@ def _constructor_subcommand():
if args.command == "uninstall":
_constructor_uninstall_subcommand(
args.prefix,
conda_clean=args.conda_clean,
clean_caches=args.clean_caches,
remove_condarcs=args.remove_condarcs,
remove_conda_caches=args.remove_conda_caches,
remove_user_data=args.remove_user_data,
)
# os.chdir will break conda --clean, so return early
return
Expand Down
56 changes: 28 additions & 28 deletions tests/test_uninstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,24 +70,23 @@ def mock_system_paths(
monkeypatch.setenv("XDG_CACHE_HOME", str(paths["cachehome"]))
monkeypatch.setenv("XDG_DATA_HOME", str(paths["datahome"]))
monkeypatch.setenv("BINSTAR_CONFIG_DIR", str(paths["binstar"]))

return paths


def run_uninstaller(
prefix: Path,
conda_clean: bool = False,
clean_caches: bool = False,
remove_condarcs: str | None = None,
remove_conda_caches: bool = False,
remove_user_data: bool = False,
needs_sudo: bool = False,
):
args = ["--prefix", str(prefix)]
if conda_clean:
args.append("--conda-clean")
if clean_caches:
args.append("--clean-caches")
if remove_condarcs:
args.extend(["--remove-condarcs", remove_condarcs])
if remove_conda_caches:
args.append("--remove-conda-caches")
if remove_user_data:
args.append("--remove-user-data")
run_conda("constructor", "uninstall", *args, needs_sudo=needs_sudo, check=True)


Expand Down Expand Up @@ -300,11 +299,28 @@ def _shortcuts_found(shortcut_env: Path) -> list:
(True, False),
ids=("shared pkgs", "remove pkgs"),
)
def test_uninstallation_conda_clean(
def test_uninstallation_clean_caches(
mock_system_paths: dict[str, Path],
tmp_env: TmpEnvFixture,
shared_pkgs: bool,
):
# Set up notices
if ON_WIN:
try:
import ctypes

if not hasattr(ctypes, "windll"):
pytest.skip("Test requires windll.ctypes for mocked locations to work.")
except ImportError:
pytest.skip("Test requires ctypes for mocked locations to work.")
notices_dir = Path(
mock_system_paths["cachehome"], "conda", "conda", "Cache", "notices"
)
else:
notices_dir = Path(mock_system_paths["cachehome"], "conda", "notices")
notices_dir.mkdir(parents=True, exist_ok=True)
(notices_dir / "notices.cache").touch()

yaml = YAML()
pkgs_dir = mock_system_paths["home"] / "pkgs"
condarc = {
Expand All @@ -322,44 +338,28 @@ def test_uninstallation_conda_clean(
assert pkgs_dir.exists()
assert list(pkgs_dir.glob("constructor*")) != []
assert list(pkgs_dir.glob("python*")) != []
run_uninstaller(base_env, conda_clean=True)
run_uninstaller(base_env, clean_caches=True)
assert pkgs_dir.exists() == shared_pkgs
if shared_pkgs:
assert list(pkgs_dir.glob("constructor*")) == []
assert list(pkgs_dir.glob("python*")) != []
assert not notices_dir.exists()


def test_uninstallation_remove_conda_caches(
def test_uninstallation_remove_user_data(
mock_system_paths: dict[str, Path],
tmp_env: TmpEnvFixture,
):
if ON_WIN:
try:
import ctypes

if not hasattr(ctypes, "windll"):
pytest.skip("Test requires windll.ctypes for mocked locations to work.")
except ImportError:
pytest.skip("Test requires ctypes for mocked locations to work.")
notices_dir = Path(
mock_system_paths["cachehome"], "conda", "conda", "Cache", "notices"
)
else:
notices_dir = Path(mock_system_paths["cachehome"], "conda", "notices")
notices_dir.mkdir(parents=True, exist_ok=True)
(notices_dir / "notices.cache").touch()

binstar_dir = mock_system_paths["binstar"] / "data"
binstar_dir.mkdir(parents=True, exist_ok=True)
(binstar_dir / "token").touch()

with tmp_env() as base_env:
dot_conda_dir = mock_system_paths["home"] / ".conda"
assert dot_conda_dir.exists()
run_uninstaller(base_env, remove_conda_caches=True)
run_uninstaller(base_env, remove_user_data=True)
assert not dot_conda_dir.exists()
assert not mock_system_paths["binstar"].exists()
assert not notices_dir.exists()


@pytest.mark.parametrize("remove", ("user", "system", "all"))
Expand Down

0 comments on commit 90996a7

Please sign in to comment.