|
import os |
|
import textwrap |
|
from optparse import Values |
|
from typing import Any, List |
|
|
|
import pip._internal.utils.filesystem as filesystem |
|
from pip._internal.cli.base_command import Command |
|
from pip._internal.cli.status_codes import ERROR, SUCCESS |
|
from pip._internal.exceptions import CommandError, PipError |
|
from pip._internal.utils.logging import getLogger |
|
|
|
logger = getLogger(__name__) |
|
|
|
|
|
class CacheCommand(Command): |
|
""" |
|
Inspect and manage pip's wheel cache. |
|
|
|
Subcommands: |
|
|
|
- dir: Show the cache directory. |
|
- info: Show information about the cache. |
|
- list: List filenames of packages stored in the cache. |
|
- remove: Remove one or more package from the cache. |
|
- purge: Remove all items from the cache. |
|
|
|
``<pattern>`` can be a glob expression or a package name. |
|
""" |
|
|
|
ignore_require_venv = True |
|
usage = """ |
|
%prog dir |
|
%prog info |
|
%prog list [<pattern>] [--format=[human, abspath]] |
|
%prog remove <pattern> |
|
%prog purge |
|
""" |
|
|
|
def add_options(self) -> None: |
|
self.cmd_opts.add_option( |
|
"--format", |
|
action="store", |
|
dest="list_format", |
|
default="human", |
|
choices=("human", "abspath"), |
|
help="Select the output format among: human (default) or abspath", |
|
) |
|
|
|
self.parser.insert_option_group(0, self.cmd_opts) |
|
|
|
def run(self, options: Values, args: List[str]) -> int: |
|
handlers = { |
|
"dir": self.get_cache_dir, |
|
"info": self.get_cache_info, |
|
"list": self.list_cache_items, |
|
"remove": self.remove_cache_items, |
|
"purge": self.purge_cache, |
|
} |
|
|
|
if not options.cache_dir: |
|
logger.error("pip cache commands can not function since cache is disabled.") |
|
return ERROR |
|
|
|
|
|
if not args or args[0] not in handlers: |
|
logger.error( |
|
"Need an action (%s) to perform.", |
|
", ".join(sorted(handlers)), |
|
) |
|
return ERROR |
|
|
|
action = args[0] |
|
|
|
|
|
try: |
|
handlers[action](options, args[1:]) |
|
except PipError as e: |
|
logger.error(e.args[0]) |
|
return ERROR |
|
|
|
return SUCCESS |
|
|
|
def get_cache_dir(self, options: Values, args: List[Any]) -> None: |
|
if args: |
|
raise CommandError("Too many arguments") |
|
|
|
logger.info(options.cache_dir) |
|
|
|
def get_cache_info(self, options: Values, args: List[Any]) -> None: |
|
if args: |
|
raise CommandError("Too many arguments") |
|
|
|
num_http_files = len(self._find_http_files(options)) |
|
num_packages = len(self._find_wheels(options, "*")) |
|
|
|
http_cache_location = self._cache_dir(options, "http") |
|
wheels_cache_location = self._cache_dir(options, "wheels") |
|
http_cache_size = filesystem.format_directory_size(http_cache_location) |
|
wheels_cache_size = filesystem.format_directory_size(wheels_cache_location) |
|
|
|
message = ( |
|
textwrap.dedent( |
|
""" |
|
Package index page cache location: {http_cache_location} |
|
Package index page cache size: {http_cache_size} |
|
Number of HTTP files: {num_http_files} |
|
Locally built wheels location: {wheels_cache_location} |
|
Locally built wheels size: {wheels_cache_size} |
|
Number of locally built wheels: {package_count} |
|
""" |
|
) |
|
.format( |
|
http_cache_location=http_cache_location, |
|
http_cache_size=http_cache_size, |
|
num_http_files=num_http_files, |
|
wheels_cache_location=wheels_cache_location, |
|
package_count=num_packages, |
|
wheels_cache_size=wheels_cache_size, |
|
) |
|
.strip() |
|
) |
|
|
|
logger.info(message) |
|
|
|
def list_cache_items(self, options: Values, args: List[Any]) -> None: |
|
if len(args) > 1: |
|
raise CommandError("Too many arguments") |
|
|
|
if args: |
|
pattern = args[0] |
|
else: |
|
pattern = "*" |
|
|
|
files = self._find_wheels(options, pattern) |
|
if options.list_format == "human": |
|
self.format_for_human(files) |
|
else: |
|
self.format_for_abspath(files) |
|
|
|
def format_for_human(self, files: List[str]) -> None: |
|
if not files: |
|
logger.info("No locally built wheels cached.") |
|
return |
|
|
|
results = [] |
|
for filename in files: |
|
wheel = os.path.basename(filename) |
|
size = filesystem.format_file_size(filename) |
|
results.append(f" - {wheel} ({size})") |
|
logger.info("Cache contents:\n") |
|
logger.info("\n".join(sorted(results))) |
|
|
|
def format_for_abspath(self, files: List[str]) -> None: |
|
if not files: |
|
return |
|
|
|
results = [] |
|
for filename in files: |
|
results.append(filename) |
|
|
|
logger.info("\n".join(sorted(results))) |
|
|
|
def remove_cache_items(self, options: Values, args: List[Any]) -> None: |
|
if len(args) > 1: |
|
raise CommandError("Too many arguments") |
|
|
|
if not args: |
|
raise CommandError("Please provide a pattern") |
|
|
|
files = self._find_wheels(options, args[0]) |
|
|
|
no_matching_msg = "No matching packages" |
|
if args[0] == "*": |
|
|
|
files += self._find_http_files(options) |
|
else: |
|
|
|
no_matching_msg += ' for pattern "{}"'.format(args[0]) |
|
|
|
if not files: |
|
logger.warning(no_matching_msg) |
|
|
|
for filename in files: |
|
os.unlink(filename) |
|
logger.verbose("Removed %s", filename) |
|
logger.info("Files removed: %s", len(files)) |
|
|
|
def purge_cache(self, options: Values, args: List[Any]) -> None: |
|
if args: |
|
raise CommandError("Too many arguments") |
|
|
|
return self.remove_cache_items(options, ["*"]) |
|
|
|
def _cache_dir(self, options: Values, subdir: str) -> str: |
|
return os.path.join(options.cache_dir, subdir) |
|
|
|
def _find_http_files(self, options: Values) -> List[str]: |
|
http_dir = self._cache_dir(options, "http") |
|
return filesystem.find_files(http_dir, "*") |
|
|
|
def _find_wheels(self, options: Values, pattern: str) -> List[str]: |
|
wheel_dir = self._cache_dir(options, "wheels") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pattern = pattern + ("*.whl" if "-" in pattern else "-*.whl") |
|
|
|
return filesystem.find_files(wheel_dir, pattern) |
|
|