图片解析应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
6.3 KiB

  1. import logging
  2. from optparse import Values
  3. from typing import Generator, Iterable, Iterator, List, NamedTuple, Optional
  4. from pip._vendor.packaging.utils import canonicalize_name
  5. from pip._internal.cli.base_command import Command
  6. from pip._internal.cli.status_codes import ERROR, SUCCESS
  7. from pip._internal.metadata import BaseDistribution, get_default_environment
  8. from pip._internal.utils.misc import write_output
  9. logger = logging.getLogger(__name__)
  10. class ShowCommand(Command):
  11. """
  12. Show information about one or more installed packages.
  13. The output is in RFC-compliant mail header format.
  14. """
  15. usage = """
  16. %prog [options] <package> ..."""
  17. ignore_require_venv = True
  18. def add_options(self) -> None:
  19. self.cmd_opts.add_option(
  20. "-f",
  21. "--files",
  22. dest="files",
  23. action="store_true",
  24. default=False,
  25. help="Show the full list of installed files for each package.",
  26. )
  27. self.parser.insert_option_group(0, self.cmd_opts)
  28. def run(self, options: Values, args: List[str]) -> int:
  29. if not args:
  30. logger.warning("ERROR: Please provide a package name or names.")
  31. return ERROR
  32. query = args
  33. results = search_packages_info(query)
  34. if not print_results(
  35. results, list_files=options.files, verbose=options.verbose
  36. ):
  37. return ERROR
  38. return SUCCESS
  39. class _PackageInfo(NamedTuple):
  40. name: str
  41. version: str
  42. location: str
  43. editable_project_location: Optional[str]
  44. requires: List[str]
  45. required_by: List[str]
  46. installer: str
  47. metadata_version: str
  48. classifiers: List[str]
  49. summary: str
  50. homepage: str
  51. project_urls: List[str]
  52. author: str
  53. author_email: str
  54. license: str
  55. entry_points: List[str]
  56. files: Optional[List[str]]
  57. def search_packages_info(query: List[str]) -> Generator[_PackageInfo, None, None]:
  58. """
  59. Gather details from installed distributions. Print distribution name,
  60. version, location, and installed files. Installed files requires a
  61. pip generated 'installed-files.txt' in the distributions '.egg-info'
  62. directory.
  63. """
  64. env = get_default_environment()
  65. installed = {dist.canonical_name: dist for dist in env.iter_all_distributions()}
  66. query_names = [canonicalize_name(name) for name in query]
  67. missing = sorted(
  68. [name for name, pkg in zip(query, query_names) if pkg not in installed]
  69. )
  70. if missing:
  71. logger.warning("Package(s) not found: %s", ", ".join(missing))
  72. def _get_requiring_packages(current_dist: BaseDistribution) -> Iterator[str]:
  73. return (
  74. dist.metadata["Name"] or "UNKNOWN"
  75. for dist in installed.values()
  76. if current_dist.canonical_name
  77. in {canonicalize_name(d.name) for d in dist.iter_dependencies()}
  78. )
  79. for query_name in query_names:
  80. try:
  81. dist = installed[query_name]
  82. except KeyError:
  83. continue
  84. requires = sorted((req.name for req in dist.iter_dependencies()), key=str.lower)
  85. required_by = sorted(_get_requiring_packages(dist), key=str.lower)
  86. try:
  87. entry_points_text = dist.read_text("entry_points.txt")
  88. entry_points = entry_points_text.splitlines(keepends=False)
  89. except FileNotFoundError:
  90. entry_points = []
  91. files_iter = dist.iter_declared_entries()
  92. if files_iter is None:
  93. files: Optional[List[str]] = None
  94. else:
  95. files = sorted(files_iter)
  96. metadata = dist.metadata
  97. yield _PackageInfo(
  98. name=dist.raw_name,
  99. version=str(dist.version),
  100. location=dist.location or "",
  101. editable_project_location=dist.editable_project_location,
  102. requires=requires,
  103. required_by=required_by,
  104. installer=dist.installer,
  105. metadata_version=dist.metadata_version or "",
  106. classifiers=metadata.get_all("Classifier", []),
  107. summary=metadata.get("Summary", ""),
  108. homepage=metadata.get("Home-page", ""),
  109. project_urls=metadata.get_all("Project-URL", []),
  110. author=metadata.get("Author", ""),
  111. author_email=metadata.get("Author-email", ""),
  112. license=metadata.get("License", ""),
  113. entry_points=entry_points,
  114. files=files,
  115. )
  116. def print_results(
  117. distributions: Iterable[_PackageInfo],
  118. list_files: bool,
  119. verbose: bool,
  120. ) -> bool:
  121. """
  122. Print the information from installed distributions found.
  123. """
  124. results_printed = False
  125. for i, dist in enumerate(distributions):
  126. results_printed = True
  127. if i > 0:
  128. write_output("---")
  129. write_output("Name: %s", dist.name)
  130. write_output("Version: %s", dist.version)
  131. write_output("Summary: %s", dist.summary)
  132. write_output("Home-page: %s", dist.homepage)
  133. write_output("Author: %s", dist.author)
  134. write_output("Author-email: %s", dist.author_email)
  135. write_output("License: %s", dist.license)
  136. write_output("Location: %s", dist.location)
  137. if dist.editable_project_location is not None:
  138. write_output(
  139. "Editable project location: %s", dist.editable_project_location
  140. )
  141. write_output("Requires: %s", ", ".join(dist.requires))
  142. write_output("Required-by: %s", ", ".join(dist.required_by))
  143. if verbose:
  144. write_output("Metadata-Version: %s", dist.metadata_version)
  145. write_output("Installer: %s", dist.installer)
  146. write_output("Classifiers:")
  147. for classifier in dist.classifiers:
  148. write_output(" %s", classifier)
  149. write_output("Entry-points:")
  150. for entry in dist.entry_points:
  151. write_output(" %s", entry.strip())
  152. write_output("Project-URLs:")
  153. for project_url in dist.project_urls:
  154. write_output(" %s", project_url)
  155. if list_files:
  156. write_output("Files:")
  157. if dist.files is None:
  158. write_output("Cannot locate RECORD or installed-files.txt")
  159. else:
  160. for line in dist.files:
  161. write_output(" %s", line.strip())
  162. return results_printed