Spaces:
Sleeping
Sleeping
import logging | |
from optparse import Values | |
from typing import Generator, Iterable, Iterator, List, NamedTuple, Optional | |
from pip._vendor.packaging.utils import canonicalize_name | |
from pip._internal.cli.base_command import Command | |
from pip._internal.cli.status_codes import ERROR, SUCCESS | |
from pip._internal.metadata import BaseDistribution, get_default_environment | |
from pip._internal.utils.misc import write_output | |
logger = logging.getLogger(__name__) | |
class ShowCommand(Command): | |
""" | |
Show information about one or more installed packages. | |
The output is in RFC-compliant mail header format. | |
""" | |
usage = """ | |
%prog [options] <package> ...""" | |
ignore_require_venv = True | |
def add_options(self) -> None: | |
self.cmd_opts.add_option( | |
"-f", | |
"--files", | |
dest="files", | |
action="store_true", | |
default=False, | |
help="Show the full list of installed files for each package.", | |
) | |
self.parser.insert_option_group(0, self.cmd_opts) | |
def run(self, options: Values, args: List[str]) -> int: | |
if not args: | |
logger.warning("ERROR: Please provide a package name or names.") | |
return ERROR | |
query = args | |
results = search_packages_info(query) | |
if not print_results( | |
results, list_files=options.files, verbose=options.verbose | |
): | |
return ERROR | |
return SUCCESS | |
class _PackageInfo(NamedTuple): | |
name: str | |
version: str | |
location: str | |
editable_project_location: Optional[str] | |
requires: List[str] | |
required_by: List[str] | |
installer: str | |
metadata_version: str | |
classifiers: List[str] | |
summary: str | |
homepage: str | |
project_urls: List[str] | |
author: str | |
author_email: str | |
license: str | |
entry_points: List[str] | |
files: Optional[List[str]] | |
def search_packages_info(query: List[str]) -> Generator[_PackageInfo, None, None]: | |
""" | |
Gather details from installed distributions. Print distribution name, | |
version, location, and installed files. Installed files requires a | |
pip generated 'installed-files.txt' in the distributions '.egg-info' | |
directory. | |
""" | |
env = get_default_environment() | |
installed = {dist.canonical_name: dist for dist in env.iter_all_distributions()} | |
query_names = [canonicalize_name(name) for name in query] | |
missing = sorted( | |
[name for name, pkg in zip(query, query_names) if pkg not in installed] | |
) | |
if missing: | |
logger.warning("Package(s) not found: %s", ", ".join(missing)) | |
def _get_requiring_packages(current_dist: BaseDistribution) -> Iterator[str]: | |
return ( | |
dist.metadata["Name"] or "UNKNOWN" | |
for dist in installed.values() | |
if current_dist.canonical_name | |
in {canonicalize_name(d.name) for d in dist.iter_dependencies()} | |
) | |
for query_name in query_names: | |
try: | |
dist = installed[query_name] | |
except KeyError: | |
continue | |
requires = sorted((req.name for req in dist.iter_dependencies()), key=str.lower) | |
required_by = sorted(_get_requiring_packages(dist), key=str.lower) | |
try: | |
entry_points_text = dist.read_text("entry_points.txt") | |
entry_points = entry_points_text.splitlines(keepends=False) | |
except FileNotFoundError: | |
entry_points = [] | |
files_iter = dist.iter_declared_entries() | |
if files_iter is None: | |
files: Optional[List[str]] = None | |
else: | |
files = sorted(files_iter) | |
metadata = dist.metadata | |
yield _PackageInfo( | |
name=dist.raw_name, | |
version=str(dist.version), | |
location=dist.location or "", | |
editable_project_location=dist.editable_project_location, | |
requires=requires, | |
required_by=required_by, | |
installer=dist.installer, | |
metadata_version=dist.metadata_version or "", | |
classifiers=metadata.get_all("Classifier", []), | |
summary=metadata.get("Summary", ""), | |
homepage=metadata.get("Home-page", ""), | |
project_urls=metadata.get_all("Project-URL", []), | |
author=metadata.get("Author", ""), | |
author_email=metadata.get("Author-email", ""), | |
license=metadata.get("License", ""), | |
entry_points=entry_points, | |
files=files, | |
) | |
def print_results( | |
distributions: Iterable[_PackageInfo], | |
list_files: bool, | |
verbose: bool, | |
) -> bool: | |
""" | |
Print the information from installed distributions found. | |
""" | |
results_printed = False | |
for i, dist in enumerate(distributions): | |
results_printed = True | |
if i > 0: | |
write_output("---") | |
write_output("Name: %s", dist.name) | |
write_output("Version: %s", dist.version) | |
write_output("Summary: %s", dist.summary) | |
write_output("Home-page: %s", dist.homepage) | |
write_output("Author: %s", dist.author) | |
write_output("Author-email: %s", dist.author_email) | |
write_output("License: %s", dist.license) | |
write_output("Location: %s", dist.location) | |
if dist.editable_project_location is not None: | |
write_output( | |
"Editable project location: %s", dist.editable_project_location | |
) | |
write_output("Requires: %s", ", ".join(dist.requires)) | |
write_output("Required-by: %s", ", ".join(dist.required_by)) | |
if verbose: | |
write_output("Metadata-Version: %s", dist.metadata_version) | |
write_output("Installer: %s", dist.installer) | |
write_output("Classifiers:") | |
for classifier in dist.classifiers: | |
write_output(" %s", classifier) | |
write_output("Entry-points:") | |
for entry in dist.entry_points: | |
write_output(" %s", entry.strip()) | |
write_output("Project-URLs:") | |
for project_url in dist.project_urls: | |
write_output(" %s", project_url) | |
if list_files: | |
write_output("Files:") | |
if dist.files is None: | |
write_output("Cannot locate RECORD or installed-files.txt") | |
else: | |
for line in dist.files: | |
write_output(" %s", line.strip()) | |
return results_printed | |