diff options
Diffstat (limited to 'pip/commands/list.py')
-rw-r--r-- | pip/commands/list.py | 184 |
1 files changed, 92 insertions, 92 deletions
diff --git a/pip/commands/list.py b/pip/commands/list.py index 48e0f8915..0310a3eb4 100644 --- a/pip/commands/list.py +++ b/pip/commands/list.py @@ -1,5 +1,6 @@ from __future__ import absolute_import +import json import logging import warnings try: @@ -7,6 +8,8 @@ try: except ImportError: from itertools import izip_longest as zip_longest +from pip._vendor import six + from pip.basecommand import Command from pip.exceptions import CommandError from pip.index import PackageFinder @@ -15,7 +18,6 @@ from pip.utils import ( from pip.utils.deprecation import RemovedInPip10Warning from pip.cmdoptions import make_option_group, index_group - logger = logging.getLogger(__name__) @@ -72,21 +74,21 @@ class ListCommand(Command): "pip only finds stable versions."), ) - # TODO: When we switch the default, set default=True here. cmd_opts.add_option( - '--columns', - action='store_true', - dest='columns', - # default=True, - help="Align package names and versions into vertical columns.", + '--format', + action='store', + dest='list_format', + choices=('legacy', 'columns', 'freeze', 'json'), + help="Select the output format among: legacy (default), columns, " + "freeze or json.", ) cmd_opts.add_option( - '--no-columns', - action='store_false', - dest='columns', - help=("Do not align package names and versions into " - "vertical columns (old-style formatting)"), + '--columns', + action='store_const', + const='columns', + dest='list_format', + help="Align package names and versions into vertical columns.", ) index_opts = make_option_group(index_group, self.parser) @@ -132,11 +134,11 @@ class ListCommand(Command): RemovedInPip10Warning, ) - # TODO: When we switch the default, remove - # ``and options.columns is not None`` - if not options.columns and options.columns is not None: + if options.list_format is None: warnings.warn( - "The --no-columns option will be removed in the future.", + "The default format will switch to columns in the future. " + "You can use --format=legacy (or define a list_format " + "in your pip.conf) to disable this warning.", RemovedInPip10Warning, ) @@ -144,43 +146,37 @@ class ListCommand(Command): raise CommandError( "Options --outdated and --uptodate cannot be combined.") + packages = get_installed_distributions( + local_only=options.local, + user_only=options.user, + editables_only=options.editable, + ) if options.outdated: - self.run_outdated(options) + packages = self.get_outdated(packages, options) elif options.uptodate: - self.run_uptodate(options) - else: - self.run_listing(options) - - def run_outdated(self, options): - latest_pkgs = [] - for dist, latest_version, typ in sorted( - self.find_packages_latest_versions(options), - key=lambda p: p[0].project_name.lower()): - if latest_version > dist.parsed_version: - latest_pkgs.append((dist, latest_version, typ)) - if (hasattr(options, "columns") and - options.columns and - len(latest_pkgs) > 0): - data, header = format_for_columns(latest_pkgs, options) - self.output_package_listing_columns(data, header) - else: - for dist, latest_version, typ in latest_pkgs: - logger.info( - '%s - Latest: %s [%s]', - self.output_package(dist), latest_version, typ, - ) - - def find_packages_latest_versions(self, options): + packages = self.get_uptodate(packages, options) + self.output_package_listing(packages, options) + + def get_outdated(self, packages, options): + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version > dist.parsed_version + ] + + def get_uptodate(self, packages, options): + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version == dist.parsed_version + ] + + def iter_packages_latest_infos(self, packages, options): index_urls = [options.index_url] + options.extra_index_urls if options.no_index: - logger.info('Ignoring indexes: %s', ','.join(index_urls)) + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) index_urls = [] dependency_links = [] - for dist in get_installed_distributions( - local_only=options.local, - user_only=options.user, - editables_only=options.editable): + for dist in packages: if dist.has_metadata('dependency_links.txt'): dependency_links.extend( dist.get_metadata_lines('dependency_links.txt'), @@ -190,12 +186,7 @@ class ListCommand(Command): finder = self._build_package_finder(options, index_urls, session) finder.add_dependency_links(dependency_links) - installed_packages = get_installed_distributions( - local_only=options.local, - user_only=options.user, - editables_only=options.editable, - ) - for dist in installed_packages: + for dist in packages: typ = 'unknown' all_candidates = finder.find_all_candidates(dist.key) if not options.pre: @@ -212,17 +203,12 @@ class ListCommand(Command): typ = 'wheel' else: typ = 'sdist' - yield dist, remote_version, typ + # This is dirty but makes the rest of the code much cleaner + dist.latest_version = remote_version + dist.latest_filetype = typ + yield dist - def run_listing(self, options): - installed_packages = get_installed_distributions( - local_only=options.local, - user_only=options.user, - editables_only=options.editable, - ) - self.output_package_listing(installed_packages, options) - - def output_package(self, dist): + def output_legacy(self, dist): if dist_is_editable(dist): return '%s (%s, %s)' % ( dist.project_name, @@ -232,19 +218,32 @@ class ListCommand(Command): else: return '%s (%s)' % (dist.project_name, dist.version) - def output_package_listing(self, installed_packages, options=None): - installed_packages = sorted( - installed_packages, + def output_legacy_latest(self, dist): + return '%s - Latest: %s [%s]' % ( + self.output_legacy(dist), + dist.latest_version, + dist.latest_filetype, + ) + + def output_package_listing(self, packages, options): + packages = sorted( + packages, key=lambda dist: dist.project_name.lower(), ) - if (hasattr(options, "columns") and - options.columns and - len(installed_packages) > 0): - data, header = format_for_columns(installed_packages, options) + if options.list_format == 'columns' and packages: + data, header = format_for_columns(packages, options) self.output_package_listing_columns(data, header) - else: - for dist in installed_packages: - logger.info(self.output_package(dist)) + elif options.list_format == 'freeze': + for dist in packages: + logger.info("%s==%s", dist.project_name, dist.version) + elif options.list_format == 'json': + logger.info(format_for_json(packages, options)) + else: # legacy + for dist in packages: + if options.outdated: + logger.info(self.output_legacy_latest(dist)) + else: + logger.info(self.output_legacy(dist)) def output_package_listing_columns(self, data, header): # insert the header first: we need to know the size of column names @@ -260,13 +259,6 @@ class ListCommand(Command): for val in pkg_strings: logger.info(val) - def run_uptodate(self, options): - uptodate = [] - for dist, version, typ in self.find_packages_latest_versions(options): - if dist.parsed_version == version: - uptodate.append(dist) - self.output_package_listing(uptodate, options) - def tabulate(vals): # From pfmoore on GitHub: @@ -291,31 +283,25 @@ def format_for_columns(pkgs, options): Convert the package data into something usable by output_package_listing_columns. """ - header = ["Package", "Version"] - running_outdated = False + running_outdated = options.outdated # Adjust the header for the `pip list --outdated` case. - if isinstance(pkgs[0], (list, tuple)): - running_outdated = True + if running_outdated: header = ["Package", "Version", "Latest", "Type"] + else: + header = ["Package", "Version"] data = [] - if any(dist_is_editable(x[0]) - if running_outdated - else dist_is_editable(x) - for x in pkgs): + if any(dist_is_editable(x) for x in pkgs): header.append("Location") for proj in pkgs: # if we're working on the 'outdated' list, separate out the # latest_version and type - if running_outdated: - proj, latest_version, typ = proj - row = [proj.project_name, proj.version] if running_outdated: - row.append(latest_version) - row.append(typ) + row.append(proj.latest_version) + row.append(proj.latest_filetype) if dist_is_editable(proj): row.append(proj.location) @@ -323,3 +309,17 @@ def format_for_columns(pkgs, options): data.append(row) return data, header + + +def format_for_json(packages, options): + data = [] + for dist in packages: + info = { + 'name': dist.project_name, + 'version': six.text_type(dist.version), + } + if options.outdated: + info['latest_version'] = six.text_type(dist.latest_version) + info['latest_filetype'] = dist.latest_filetype + data.append(info) + return json.dumps(data) |