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.
 
 
 
 

203 lines
7.1 KiB

  1. from __future__ import absolute_import
  2. import logging
  3. from pip._vendor import pkg_resources
  4. from pip.basecommand import Command
  5. from pip.exceptions import DistributionNotFound
  6. from pip.index import FormatControl, fmt_ctl_formats, PackageFinder, Search
  7. from pip.req import InstallRequirement
  8. from pip.utils import get_installed_distributions, dist_is_editable
  9. from pip.wheel import WheelCache
  10. from pip.cmdoptions import make_option_group, index_group
  11. logger = logging.getLogger(__name__)
  12. class ListCommand(Command):
  13. """
  14. List installed packages, including editables.
  15. Packages are listed in a case-insensitive sorted order.
  16. """
  17. name = 'list'
  18. usage = """
  19. %prog [options]"""
  20. summary = 'List installed packages.'
  21. def __init__(self, *args, **kw):
  22. super(ListCommand, self).__init__(*args, **kw)
  23. cmd_opts = self.cmd_opts
  24. cmd_opts.add_option(
  25. '-o', '--outdated',
  26. action='store_true',
  27. default=False,
  28. help='List outdated packages (excluding editables)')
  29. cmd_opts.add_option(
  30. '-u', '--uptodate',
  31. action='store_true',
  32. default=False,
  33. help='List uptodate packages (excluding editables)')
  34. cmd_opts.add_option(
  35. '-e', '--editable',
  36. action='store_true',
  37. default=False,
  38. help='List editable projects.')
  39. cmd_opts.add_option(
  40. '-l', '--local',
  41. action='store_true',
  42. default=False,
  43. help=('If in a virtualenv that has global access, do not list '
  44. 'globally-installed packages.'),
  45. )
  46. self.cmd_opts.add_option(
  47. '--user',
  48. dest='user',
  49. action='store_true',
  50. default=False,
  51. help='Only output packages installed in user-site.')
  52. cmd_opts.add_option(
  53. '--pre',
  54. action='store_true',
  55. default=False,
  56. help=("Include pre-release and development versions. By default, "
  57. "pip only finds stable versions."),
  58. )
  59. index_opts = make_option_group(index_group, self.parser)
  60. self.parser.insert_option_group(0, index_opts)
  61. self.parser.insert_option_group(0, cmd_opts)
  62. def _build_package_finder(self, options, index_urls, session):
  63. """
  64. Create a package finder appropriate to this list command.
  65. """
  66. return PackageFinder(
  67. find_links=options.find_links,
  68. index_urls=index_urls,
  69. allow_external=options.allow_external,
  70. allow_unverified=options.allow_unverified,
  71. allow_all_external=options.allow_all_external,
  72. allow_all_prereleases=options.pre,
  73. trusted_hosts=options.trusted_hosts,
  74. process_dependency_links=options.process_dependency_links,
  75. session=session,
  76. )
  77. def run(self, options, args):
  78. if options.outdated:
  79. self.run_outdated(options)
  80. elif options.uptodate:
  81. self.run_uptodate(options)
  82. elif options.editable:
  83. self.run_editables(options)
  84. else:
  85. self.run_listing(options)
  86. def run_outdated(self, options):
  87. for dist, version, typ in self.find_packages_latest_versions(options):
  88. if version > dist.parsed_version:
  89. logger.info(
  90. '%s (Current: %s Latest: %s [%s])',
  91. dist.project_name, dist.version, version, typ,
  92. )
  93. def find_packages_latest_versions(self, options):
  94. index_urls = [options.index_url] + options.extra_index_urls
  95. if options.no_index:
  96. logger.info('Ignoring indexes: %s', ','.join(index_urls))
  97. index_urls = []
  98. dependency_links = []
  99. for dist in get_installed_distributions(local_only=options.local,
  100. user_only=options.user):
  101. if dist.has_metadata('dependency_links.txt'):
  102. dependency_links.extend(
  103. dist.get_metadata_lines('dependency_links.txt'),
  104. )
  105. with self._build_session(options) as session:
  106. finder = self._build_package_finder(options, index_urls, session)
  107. finder.add_dependency_links(dependency_links)
  108. installed_packages = get_installed_distributions(
  109. local_only=options.local,
  110. user_only=options.user,
  111. include_editables=False,
  112. )
  113. format_control = FormatControl(set(), set())
  114. wheel_cache = WheelCache(options.cache_dir, format_control)
  115. for dist in installed_packages:
  116. req = InstallRequirement.from_line(
  117. dist.key, None, isolated=options.isolated_mode,
  118. wheel_cache=wheel_cache
  119. )
  120. typ = 'unknown'
  121. try:
  122. link = finder.find_requirement(req, True)
  123. # If link is None, means installed version is most
  124. # up-to-date
  125. if link is None:
  126. continue
  127. except DistributionNotFound:
  128. continue
  129. else:
  130. canonical_name = pkg_resources.safe_name(req.name).lower()
  131. formats = fmt_ctl_formats(format_control, canonical_name)
  132. search = Search(
  133. req.name,
  134. canonical_name,
  135. formats)
  136. remote_version = finder._link_package_versions(
  137. link, search).version
  138. if link.is_wheel:
  139. typ = 'wheel'
  140. else:
  141. typ = 'sdist'
  142. yield dist, remote_version, typ
  143. def run_listing(self, options):
  144. installed_packages = get_installed_distributions(
  145. local_only=options.local,
  146. user_only=options.user,
  147. )
  148. self.output_package_listing(installed_packages)
  149. def run_editables(self, options):
  150. installed_packages = get_installed_distributions(
  151. local_only=options.local,
  152. user_only=options.user,
  153. editables_only=True,
  154. )
  155. self.output_package_listing(installed_packages)
  156. def output_package_listing(self, installed_packages):
  157. installed_packages = sorted(
  158. installed_packages,
  159. key=lambda dist: dist.project_name.lower(),
  160. )
  161. for dist in installed_packages:
  162. if dist_is_editable(dist):
  163. line = '%s (%s, %s)' % (
  164. dist.project_name,
  165. dist.version,
  166. dist.location,
  167. )
  168. else:
  169. line = '%s (%s)' % (dist.project_name, dist.version)
  170. logger.info(line)
  171. def run_uptodate(self, options):
  172. uptodate = []
  173. for dist, version, typ in self.find_packages_latest_versions(options):
  174. if dist.parsed_version == version:
  175. uptodate.append(dist)
  176. self.output_package_listing(uptodate)