[commands/cache] make `pip cache purge` remove everything from http + wheels caches; make `pip cache remove` prune empty directories.

This commit is contained in:
Ellen Marie Dash 2020-10-26 16:09:52 -04:00
parent 81f6a9fcc6
commit 4d0d414ea8
2 changed files with 66 additions and 0 deletions

View File

@ -183,7 +183,24 @@ class CacheCommand(Command):
for filename in files: for filename in files:
os.unlink(filename) os.unlink(filename)
logger.verbose("Removed %s", filename) logger.verbose("Removed %s", filename)
http_dirs = filesystem.subdirs_with_no_files(self._cache_dir(options, "http"))
wheel_dirs = filesystem.subdirs_with_no_files(
self._cache_dir(options, "wheels")
)
dirs = list(http_dirs) + list(wheel_dirs)
for dirname in dirs:
os.rmdir(dirname)
logger.verbose("Removed %s", dirname)
# selfcheck.json is no longer used by pip.
selfcheck_json = self._cache_dir(options, "selfcheck.json")
if os.path.isfile(selfcheck_json):
os.remove(selfcheck_json)
logger.verbose("Removed legacy selfcheck.json file")
logger.info("Files removed: %s", len(files)) logger.info("Files removed: %s", len(files))
logger.info("Empty directories removed: %s", len(dirs))
def purge_cache(self, options: Values, args: List[Any]) -> None: def purge_cache(self, options: Values, args: List[Any]) -> None:
if args: if args:

View File

@ -4,11 +4,13 @@ import os.path
import random import random
import sys import sys
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from typing import Any, BinaryIO, Generator, List, Union, cast from typing import Any, BinaryIO, Generator, List, Union, cast
from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed
from pip._internal.exceptions import PipError
from pip._internal.utils.compat import get_path_uid from pip._internal.utils.compat import get_path_uid
from pip._internal.utils.misc import format_size from pip._internal.utils.misc import format_size
@ -151,3 +153,50 @@ def directory_size(path: str) -> Union[int, float]:
def format_directory_size(path: str) -> str: def format_directory_size(path: str) -> str:
return format_size(directory_size(path)) return format_size(directory_size(path))
def _leaf_subdirs(path):
"""Traverses the file tree, finding every empty directory."""
path_obj = Path(path)
if not path_obj.exists():
return
for item in path_obj.iterdir():
if not item.is_dir():
continue
subitems = item.iterdir()
# ASSUMPTION: Nothing in subitems will be None or False.
if not any(subitems):
yield item
if not any(subitem.is_file() for subitem in subitems):
yield from _leaf_subdirs(item)
def _leaf_parents_without_files(path, leaf):
"""Yields +leaf+ and each parent directory below +path+, until one of
them includes a file (as opposed to directories or nothing)."""
if not str(leaf).startswith(str(path)):
# If +leaf+ is not a subdirectory of +path+, bail early to avoid
# an endless loop.
raise PipError("leaf is not a subdirectory of path")
path = Path(path)
leaf = Path(leaf)
while leaf != path:
if all(item.is_dir() for item in leaf.iterdir()):
yield str(leaf)
else:
break
leaf = leaf.parent
def subdirs_with_no_files(path):
"""Yields every subdirectory of +path+ that has no files under it."""
for leaf in _leaf_subdirs(path):
yield from _leaf_parents_without_files(path, leaf)