from __future__ import absolute_import import logging import os from email.parser import FeedParser from pip._vendor import pkg_resources 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.utils.misc import write_output from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: from optparse import Values from typing import List, Dict, Iterator 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): # type: () -> 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, args): # type: (Values, 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 def search_packages_info(query): # type: (List[str]) -> Iterator[Dict[str, str]] """ 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. """ installed = {} for p in pkg_resources.working_set: installed[canonicalize_name(p.project_name)] = p 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(package_name): # type: (str) -> List[str] canonical_name = canonicalize_name(package_name) return [ pkg.project_name for pkg in pkg_resources.working_set if canonical_name in [canonicalize_name(required.name) for required in pkg.requires()] ] for dist in [installed[pkg] for pkg in query_names if pkg in installed]: package = { 'name': dist.project_name, 'version': dist.version, 'location': dist.location, 'requires': [dep.project_name for dep in dist.requires()], 'required_by': get_requiring_packages(dist.project_name) } file_list = None metadata = '' if isinstance(dist, pkg_resources.DistInfoDistribution): # RECORDs should be part of .dist-info metadatas if dist.has_metadata('RECORD'): lines = dist.get_metadata_lines('RECORD') paths = [line.split(',')[0] for line in lines] paths = [os.path.join(dist.location, p) for p in paths] file_list = [os.path.relpath(p, dist.location) for p in paths] if dist.has_metadata('METADATA'): metadata = dist.get_metadata('METADATA') else: # Otherwise use pip's log for .egg-info's if dist.has_metadata('installed-files.txt'): paths = dist.get_metadata_lines('installed-files.txt') paths = [os.path.join(dist.egg_info, p) for p in paths] file_list = [os.path.relpath(p, dist.location) for p in paths] if dist.has_metadata('PKG-INFO'): metadata = dist.get_metadata('PKG-INFO') if dist.has_metadata('entry_points.txt'): entry_points = dist.get_metadata_lines('entry_points.txt') package['entry_points'] = entry_points if dist.has_metadata('INSTALLER'): for line in dist.get_metadata_lines('INSTALLER'): if line.strip(): package['installer'] = line.strip() break # @todo: Should pkg_resources.Distribution have a # `get_pkg_info` method? feed_parser = FeedParser() feed_parser.feed(metadata) pkg_info_dict = feed_parser.close() for key in ('metadata-version', 'summary', 'home-page', 'author', 'author-email', 'license'): package[key] = pkg_info_dict.get(key) # It looks like FeedParser cannot deal with repeated headers classifiers = [] for line in metadata.splitlines(): if line.startswith('Classifier: '): classifiers.append(line[len('Classifier: '):]) package['classifiers'] = classifiers if file_list: package['files'] = sorted(file_list) yield package def print_results(distributions, list_files=False, verbose=False): # type: (Iterator[Dict[str, str]], bool, 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.get('name', '')) write_output("Version: %s", dist.get('version', '')) write_output("Summary: %s", dist.get('summary', '')) write_output("Home-page: %s", dist.get('home-page', '')) write_output("Author: %s", dist.get('author', '')) write_output("Author-email: %s", dist.get('author-email', '')) write_output("License: %s", dist.get('license', '')) write_output("Location: %s", dist.get('location', '')) write_output("Requires: %s", ', '.join(dist.get('requires', []))) write_output("Required-by: %s", ', '.join(dist.get('required_by', []))) if verbose: write_output("Metadata-Version: %s", dist.get('metadata-version', '')) write_output("Installer: %s", dist.get('installer', '')) write_output("Classifiers:") for classifier in dist.get('classifiers', []): write_output(" %s", classifier) write_output("Entry-points:") for entry in dist.get('entry_points', []): write_output(" %s", entry.strip()) if list_files: write_output("Files:") for line in dist.get('files', []): write_output(" %s", line.strip()) if "files" not in dist: write_output("Cannot locate installed-files.txt") return results_printed
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
__pycache__ | Folder | 0755 |
|
|
.__init__.pyo.40009 | File | 3.48 KB | 0644 |
|
.cache.pyo.40009 | File | 5.45 KB | 0644 |
|
.check.pyo.40009 | File | 1.91 KB | 0644 |
|
.completion.pyo.40009 | File | 3.51 KB | 0644 |
|
.configuration.pyo.40009 | File | 9.52 KB | 0644 |
|
.debug.pyo.40009 | File | 7.9 KB | 0644 |
|
.download.pyo.40009 | File | 4.7 KB | 0644 |
|
.freeze.pyo.40009 | File | 3.69 KB | 0644 |
|
.hash.pyo.40009 | File | 2.67 KB | 0644 |
|
.help.pyo.40009 | File | 1.68 KB | 0644 |
|
.list.pyo.40009 | File | 10.33 KB | 0644 |
|
.show.pyo.40009 | File | 6.59 KB | 0644 |
|
.uninstall.pyo.40009 | File | 3.47 KB | 0644 |
|
__init__.py | File | 4 KB | 0644 |
|
__init__.pyc | File | 3.48 KB | 0644 |
|
__init__.pyo | File | 3.48 KB | 0644 |
|
cache.py | File | 5.54 KB | 0644 |
|
cache.pyc | File | 5.45 KB | 0644 |
|
cache.pyo | File | 5.45 KB | 0644 |
|
check.py | File | 1.64 KB | 0644 |
|
check.pyc | File | 1.91 KB | 0644 |
|
check.pyo | File | 1.91 KB | 0644 |
|
completion.py | File | 3.01 KB | 0644 |
|
completion.pyc | File | 3.51 KB | 0644 |
|
completion.pyo | File | 3.51 KB | 0644 |
|
configuration.py | File | 9.13 KB | 0644 |
|
configuration.pyc | File | 9.52 KB | 0644 |
|
configuration.pyo | File | 9.52 KB | 0644 |
|
debug.py | File | 7.14 KB | 0644 |
|
debug.pyc | File | 7.9 KB | 0644 |
|
debug.pyo | File | 7.9 KB | 0644 |
|
download.py | File | 4.8 KB | 0644 |
|
download.pyc | File | 4.7 KB | 0644 |
|
download.pyo | File | 4.7 KB | 0644 |
|
freeze.py | File | 3.37 KB | 0644 |
|
freeze.pyc | File | 3.69 KB | 0644 |
|
freeze.pyo | File | 3.69 KB | 0644 |
|
hash.py | File | 1.8 KB | 0644 |
|
hash.pyc | File | 2.67 KB | 0644 |
|
hash.pyo | File | 2.67 KB | 0644 |
|
help.py | File | 1.24 KB | 0644 |
|
help.pyc | File | 1.68 KB | 0644 |
|
help.pyo | File | 1.68 KB | 0644 |
|
install.py | File | 28.05 KB | 0644 |
|
install.pyc | File | 20.44 KB | 0644 |
|
install.pyo | File | 20.38 KB | 0644 |
|
list.py | File | 11.05 KB | 0644 |
|
list.pyc | File | 10.33 KB | 0644 |
|
list.pyo | File | 10.33 KB | 0644 |
|
search.py | File | 5.62 KB | 0644 |
|
search.pyc | File | 5.81 KB | 0644 |
|
search.pyo | File | 5.77 KB | 0644 |
|
show.py | File | 6.83 KB | 0644 |
|
show.pyc | File | 6.59 KB | 0644 |
|
show.pyo | File | 6.59 KB | 0644 |
|
uninstall.py | File | 3.23 KB | 0644 |
|
uninstall.pyc | File | 3.47 KB | 0644 |
|
uninstall.pyo | File | 3.47 KB | 0644 |
|
wheel.py | File | 6.27 KB | 0644 |
|
wheel.pyc | File | 5.8 KB | 0644 |
|
wheel.pyo | File | 5.72 KB | 0644 |
|