From 90996a7655eba15edc1e32aad12818f18093dc53 Mon Sep 17 00:00:00 2001 From: Marco Esters Date: Fri, 22 Nov 2024 09:42:05 -0800 Subject: [PATCH] Remove notices cache with package caches --- src/entry_point.py | 38 +++++++++++++++------------- tests/test_uninstall.py | 56 ++++++++++++++++++++--------------------- 2 files changed, 48 insertions(+), 46 deletions(-) diff --git a/src/entry_point.py b/src/entry_point.py index 393c26a..a5b86b0 100755 --- a/src/entry_point.py +++ b/src/entry_point.py @@ -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." ), ) @@ -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." ), @@ -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. @@ -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: @@ -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: @@ -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""" @@ -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 diff --git a/tests/test_uninstall.py b/tests/test_uninstall.py index d405c04..2eddb3c 100644 --- a/tests/test_uninstall.py +++ b/tests/test_uninstall.py @@ -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) @@ -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 = { @@ -322,33 +338,18 @@ 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() @@ -356,10 +357,9 @@ def test_uninstallation_remove_conda_caches( 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"))