pip/pip/commands/list.py

326 lines
11 KiB
Python
Raw Normal View History

from __future__ import absolute_import
import logging
import warnings
try:
from itertools import zip_longest
except ImportError:
from itertools import izip_longest as zip_longest
2011-03-19 19:30:56 +01:00
from pip.basecommand import Command
2015-11-05 23:20:21 +01:00
from pip.exceptions import CommandError
from pip.index import PackageFinder
2015-09-06 00:23:45 +02:00
from pip.utils import (
2015-11-05 23:20:21 +01:00
get_installed_distributions, dist_is_editable)
from pip.utils.deprecation import RemovedInPip10Warning
from pip.cmdoptions import make_option_group, index_group
2011-03-19 19:30:56 +01:00
logger = logging.getLogger(__name__)
class ListCommand(Command):
"""
List installed packages, including editables.
Packages are listed in a case-insensitive sorted order.
"""
name = 'list'
2013-01-18 22:25:15 +01:00
usage = """
%prog [options]"""
summary = 'List installed packages.'
2011-03-19 19:30:56 +01:00
def __init__(self, *args, **kw):
super(ListCommand, self).__init__(*args, **kw)
cmd_opts = self.cmd_opts
cmd_opts.add_option(
'-o', '--outdated',
action='store_true',
default=False,
help='List outdated packages')
cmd_opts.add_option(
'-u', '--uptodate',
action='store_true',
default=False,
help='List uptodate packages')
2012-12-16 08:23:20 +01:00
cmd_opts.add_option(
'-e', '--editable',
2012-12-16 08:23:20 +01:00
action='store_true',
default=False,
2013-01-18 22:25:15 +01:00
help='List editable projects.')
2012-12-16 08:31:39 +01:00
cmd_opts.add_option(
'-l', '--local',
action='store_true',
default=False,
help=('If in a virtualenv that has global access, do not list '
'globally-installed packages.'),
)
self.cmd_opts.add_option(
'--user',
dest='user',
action='store_true',
default=False,
help='Only output packages installed in user-site.')
2012-12-16 08:31:39 +01:00
cmd_opts.add_option(
'--pre',
action='store_true',
default=False,
help=("Include pre-release and development versions. By default, "
"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.",
)
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)"),
)
index_opts = make_option_group(index_group, self.parser)
self.parser.insert_option_group(0, index_opts)
self.parser.insert_option_group(0, cmd_opts)
2013-08-16 14:04:27 +02:00
def _build_package_finder(self, options, index_urls, session):
"""
2012-10-01 22:23:49 +02:00
Create a package finder appropriate to this list command.
"""
return PackageFinder(
find_links=options.find_links,
index_urls=index_urls,
allow_all_prereleases=options.pre,
trusted_hosts=options.trusted_hosts,
process_dependency_links=options.process_dependency_links,
session=session,
)
2011-03-19 19:30:56 +01:00
def run(self, options, args):
if options.allow_external:
warnings.warn(
"--allow-external has been deprecated and will be removed in "
"the future. Due to changes in the repository protocol, it no "
"longer has any effect.",
RemovedInPip10Warning,
)
if options.allow_all_external:
warnings.warn(
"--allow-all-external has been deprecated and will be removed "
"in the future. Due to changes in the repository protocol, it "
"no longer has any effect.",
RemovedInPip10Warning,
)
if options.allow_unverified:
warnings.warn(
"--allow-unverified has been deprecated and will be removed "
"in the future. Due to changes in the repository protocol, it "
"no longer has any effect.",
RemovedInPip10Warning,
)
# TODO: When we switch the default, remove
# ``and options.columns is not None``
if not options.columns and options.columns is not None:
warnings.warn(
"The --no-columns option will be removed in the future.",
RemovedInPip10Warning,
)
if options.outdated and options.uptodate:
raise CommandError(
"Options --outdated and --uptodate cannot be combined.")
if options.outdated:
2012-12-16 08:23:20 +01:00
self.run_outdated(options)
elif options.uptodate:
2012-12-16 08:23:20 +01:00
self.run_uptodate(options)
2012-09-13 00:50:25 +02:00
else:
2012-12-16 08:23:20 +01:00
self.run_listing(options)
2012-12-16 08:23:20 +01:00
def run_outdated(self, options):
latest_pkgs = []
2015-10-11 20:15:14 +02:00
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(
2015-10-11 20:15:14 +02:00
'%s - Latest: %s [%s]',
self.output_package(dist), latest_version, typ,
)
def find_packages_latest_versions(self, options):
index_urls = [options.index_url] + options.extra_index_urls
if options.no_index:
logger.info('Ignoring indexes: %s', ','.join(index_urls))
index_urls = []
2011-03-19 19:30:56 +01:00
dependency_links = []
for dist in get_installed_distributions(
local_only=options.local,
user_only=options.user,
editables_only=options.editable):
if dist.has_metadata('dependency_links.txt'):
dependency_links.extend(
dist.get_metadata_lines('dependency_links.txt'),
)
with self._build_session(options) as session:
finder = self._build_package_finder(options, index_urls, session)
finder.add_dependency_links(dependency_links)
2013-08-16 14:04:27 +02:00
installed_packages = get_installed_distributions(
local_only=options.local,
user_only=options.user,
editables_only=options.editable,
)
for dist in installed_packages:
typ = 'unknown'
2015-11-05 23:20:21 +01:00
all_candidates = finder.find_all_candidates(dist.key)
if not options.pre:
# Remove prereleases
all_candidates = [candidate for candidate in all_candidates
if not candidate.version.is_prerelease]
2015-11-05 23:20:21 +01:00
if not all_candidates:
continue
2015-11-05 23:20:21 +01:00
best_candidate = max(all_candidates,
key=finder._candidate_sort_key)
remote_version = best_candidate.version
if best_candidate.location.is_wheel:
typ = 'wheel'
else:
2015-11-05 23:20:21 +01:00
typ = 'sdist'
yield dist, remote_version, typ
2012-09-13 00:50:25 +02:00
2012-12-16 08:23:20 +01:00
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)
2012-12-16 08:23:20 +01:00
2015-10-11 20:15:14 +02:00
def output_package(self, dist):
if dist_is_editable(dist):
return '%s (%s, %s)' % (
dist.project_name,
dist.version,
dist.location,
)
else:
return '%s (%s)' % (dist.project_name, dist.version)
def output_package_listing(self, installed_packages, options=None):
installed_packages = sorted(
installed_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)
self.output_package_listing_columns(data, header)
else:
for dist in installed_packages:
logger.info(self.output_package(dist))
def output_package_listing_columns(self, data, header):
# insert the header first: we need to know the size of column names
if len(data) > 0:
data.insert(0, header)
pkg_strings, sizes = tabulate(data)
# Create and add a separator.
if len(data) > 0:
pkg_strings.insert(1, " ".join(map(lambda x: '-' * x, sizes)))
for val in pkg_strings:
logger.info(val)
2012-09-13 00:50:25 +02:00
2012-12-16 08:23:20 +01:00
def run_uptodate(self, options):
uptodate = []
for dist, version, typ in self.find_packages_latest_versions(options):
2014-07-04 00:56:26 +02:00
if dist.parsed_version == version:
uptodate.append(dist)
self.output_package_listing(uptodate, options)
def tabulate(vals):
# From pfmoore on GitHub:
# https://github.com/pypa/pip/issues/3651#issuecomment-216932564
assert len(vals) > 0
sizes = [0] * max(len(x) for x in vals)
for row in vals:
sizes = [max(s, len(str(c))) for s, c in zip_longest(sizes, row)]
result = []
for row in vals:
display = " ".join([str(c).ljust(s) if c is not None else ''
for s, c in zip_longest(sizes, row)])
result.append(display)
return result, sizes
def format_for_columns(pkgs, options):
"""
Convert the package data into something usable
by output_package_listing_columns.
"""
header = ["Package", "Version"]
running_outdated = False
# Adjust the header for the `pip list --outdated` case.
if isinstance(pkgs[0], (list, tuple)):
running_outdated = True
header = ["Package", "Version", "Latest", "Type"]
data = []
if any(dist_is_editable(x[0])
if running_outdated
else 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)
if dist_is_editable(proj):
row.append(proj.location)
data.append(row)
return data, header