summaryrefslogtreecommitdiffstats
path: root/third_party/python/pip/pip/_internal/commands/show.py
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/python/pip/pip/_internal/commands/show.py')
-rw-r--r--third_party/python/pip/pip/_internal/commands/show.py189
1 files changed, 189 insertions, 0 deletions
diff --git a/third_party/python/pip/pip/_internal/commands/show.py b/third_party/python/pip/pip/_internal/commands/show.py
new file mode 100644
index 0000000000..3f10701f6b
--- /dev/null
+++ b/third_party/python/pip/pip/_internal/commands/show.py
@@ -0,0 +1,189 @@
+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