Path: blob/master/venv/Lib/site-packages/pip/_internal/commands/show.py
811 views
# The following comment should be removed at some point in the future.1# mypy: disallow-untyped-defs=False23from __future__ import absolute_import45import logging6import os7from email.parser import FeedParser89from pip._vendor import pkg_resources10from pip._vendor.packaging.utils import canonicalize_name1112from pip._internal.cli.base_command import Command13from pip._internal.cli.status_codes import ERROR, SUCCESS14from pip._internal.utils.misc import write_output1516logger = logging.getLogger(__name__)171819class ShowCommand(Command):20"""21Show information about one or more installed packages.2223The output is in RFC-compliant mail header format.24"""2526usage = """27%prog [options] <package> ..."""28ignore_require_venv = True2930def __init__(self, *args, **kw):31super(ShowCommand, self).__init__(*args, **kw)32self.cmd_opts.add_option(33'-f', '--files',34dest='files',35action='store_true',36default=False,37help='Show the full list of installed files for each package.')3839self.parser.insert_option_group(0, self.cmd_opts)4041def run(self, options, args):42if not args:43logger.warning('ERROR: Please provide a package name or names.')44return ERROR45query = args4647results = search_packages_info(query)48if not print_results(49results, list_files=options.files, verbose=options.verbose):50return ERROR51return SUCCESS525354def search_packages_info(query):55"""56Gather details from installed distributions. Print distribution name,57version, location, and installed files. Installed files requires a58pip generated 'installed-files.txt' in the distributions '.egg-info'59directory.60"""61installed = {}62for p in pkg_resources.working_set:63installed[canonicalize_name(p.project_name)] = p6465query_names = [canonicalize_name(name) for name in query]66missing = sorted(67[name for name, pkg in zip(query, query_names) if pkg not in installed]68)69if missing:70logger.warning('Package(s) not found: %s', ', '.join(missing))7172def get_requiring_packages(package_name):73canonical_name = canonicalize_name(package_name)74return [75pkg.project_name for pkg in pkg_resources.working_set76if canonical_name in77[canonicalize_name(required.name) for required in78pkg.requires()]79]8081for dist in [installed[pkg] for pkg in query_names if pkg in installed]:82package = {83'name': dist.project_name,84'version': dist.version,85'location': dist.location,86'requires': [dep.project_name for dep in dist.requires()],87'required_by': get_requiring_packages(dist.project_name)88}89file_list = None90metadata = None91if isinstance(dist, pkg_resources.DistInfoDistribution):92# RECORDs should be part of .dist-info metadatas93if dist.has_metadata('RECORD'):94lines = dist.get_metadata_lines('RECORD')95paths = [l.split(',')[0] for l in lines]96paths = [os.path.join(dist.location, p) for p in paths]97file_list = [os.path.relpath(p, dist.location) for p in paths]9899if dist.has_metadata('METADATA'):100metadata = dist.get_metadata('METADATA')101else:102# Otherwise use pip's log for .egg-info's103if dist.has_metadata('installed-files.txt'):104paths = dist.get_metadata_lines('installed-files.txt')105paths = [os.path.join(dist.egg_info, p) for p in paths]106file_list = [os.path.relpath(p, dist.location) for p in paths]107108if dist.has_metadata('PKG-INFO'):109metadata = dist.get_metadata('PKG-INFO')110111if dist.has_metadata('entry_points.txt'):112entry_points = dist.get_metadata_lines('entry_points.txt')113package['entry_points'] = entry_points114115if dist.has_metadata('INSTALLER'):116for line in dist.get_metadata_lines('INSTALLER'):117if line.strip():118package['installer'] = line.strip()119break120121# @todo: Should pkg_resources.Distribution have a122# `get_pkg_info` method?123feed_parser = FeedParser()124feed_parser.feed(metadata)125pkg_info_dict = feed_parser.close()126for key in ('metadata-version', 'summary',127'home-page', 'author', 'author-email', 'license'):128package[key] = pkg_info_dict.get(key)129130# It looks like FeedParser cannot deal with repeated headers131classifiers = []132for line in metadata.splitlines():133if line.startswith('Classifier: '):134classifiers.append(line[len('Classifier: '):])135package['classifiers'] = classifiers136137if file_list:138package['files'] = sorted(file_list)139yield package140141142def print_results(distributions, list_files=False, verbose=False):143"""144Print the information from installed distributions found.145"""146results_printed = False147for i, dist in enumerate(distributions):148results_printed = True149if i > 0:150write_output("---")151152write_output("Name: %s", dist.get('name', ''))153write_output("Version: %s", dist.get('version', ''))154write_output("Summary: %s", dist.get('summary', ''))155write_output("Home-page: %s", dist.get('home-page', ''))156write_output("Author: %s", dist.get('author', ''))157write_output("Author-email: %s", dist.get('author-email', ''))158write_output("License: %s", dist.get('license', ''))159write_output("Location: %s", dist.get('location', ''))160write_output("Requires: %s", ', '.join(dist.get('requires', [])))161write_output("Required-by: %s", ', '.join(dist.get('required_by', [])))162163if verbose:164write_output("Metadata-Version: %s",165dist.get('metadata-version', ''))166write_output("Installer: %s", dist.get('installer', ''))167write_output("Classifiers:")168for classifier in dist.get('classifiers', []):169write_output(" %s", classifier)170write_output("Entry-points:")171for entry in dist.get('entry_points', []):172write_output(" %s", entry.strip())173if list_files:174write_output("Files:")175for line in dist.get('files', []):176write_output(" %s", line.strip())177if "files" not in dist:178write_output("Cannot locate installed-files.txt")179return results_printed180181182