Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added a upgrade-all command #10040

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/4551.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added an ``upgrade-all`` command. This command will update all packages that can be updated.
4 changes: 4 additions & 0 deletions src/pip/_internal/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
'pip._internal.commands.check', 'CheckCommand',
'Verify installed packages have compatible dependencies.',
)),
('upgrade-all', CommandInfo(
'pip._internal.commands.upgrade_all', 'UpgradeAllCommand',
'Upgrade all packages to latest version',
)),
('config', CommandInfo(
'pip._internal.commands.configuration', 'ConfigurationCommand',
'Manage local and global configuration.',
Expand Down
105 changes: 105 additions & 0 deletions src/pip/_internal/commands/upgrade_all.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import logging
from optparse import Values
from typing import List

from pip._internal.commands.install import InstallCommand
from pip._internal.commands.list import ListCommand
from pip._internal.utils.compat import stdlib_pkgs
from pip._internal.utils.misc import get_installed_distributions

logger = logging.getLogger(__name__)


class UpgradeAllCommand(InstallCommand, ListCommand):
"""
Upgrades all out of date packages, exactly like this old oneliner used to do:
pip list --format freeze | \
grep --invert-match "pkg-resources" | \
cut --delimiter "=" --fields 1 | \
xargs pip install --upgrade
"""
usage = """
%prog [options]
%prog [options] [-e] <vcs project url> ...
%prog [options] [-e] <local project path> ...
%prog [options] <archive url/path> ..."""

def add_options(self):
# type: () -> None
# install all options from installcommand
InstallCommand.add_options(self)
# we don't upgrade in editable mode AND listcommand also has an editable option
self.cmd_opts.remove_option('--editable')
# redefine user later
self.cmd_opts.remove_option('--user')
# upgrade all always upgrade, so the help text for target makes no sense
self.cmd_opts.remove_option('--target')
# we always upgrade
self.cmd_opts.remove_option('--upgrade')
# pre is defined in installcommand and listcommand, so remove it once
self.cmd_opts.remove_option('--pre')
self.cmd_opts.remove_option('--index-url')
self.cmd_opts.remove_option('--extra-index-url')
self.cmd_opts.remove_option('--no-index')
self.cmd_opts.remove_option('--find-links')

# install command will have added the cmd_opts to self.parser already,
# so pop them here because well' add them again later
del self.parser.option_groups[0]
# same for package index options
del self.parser.option_groups[0]

# install all options from listcommand
ListCommand.add_options(self)

# also remove options listcommand have added so we can add our real
# options later
del self.parser.option_groups[0]

# redefine user later
self.cmd_opts.remove_option('--user')
self.cmd_opts.add_option(
'--user',
dest='user',
action='store_true',
default=False,
help='Only upgrade packages installed in user-site.')
self.cmd_opts.add_option(
'-t', '--target',
dest='target_dir',
metavar='dir',
default=None,
help='Install packages into <dir>. '
'This will replace existing files/folders in '
'<dir> with new versions.'
)

self.parser.insert_option_group(0, self.cmd_opts)

def run(self, options, args):
# type: (Values, List[str]) -> int
skip = set(stdlib_pkgs)
if options.excludes:
skip.update(options.excludes)

packages = get_installed_distributions(
local_only=options.local,
user_only=options.user,
editables_only=options.editable,
include_editables=options.include_editable,
paths=options.path,
skip=skip,
)
if options.not_required:
packages = self.get_not_required(packages, options)

if options.outdated:
packages = self.get_outdated(packages, options)
packages = [dist.project_name for dist in packages]

logging.info("upgrading %s", packages)

options.upgrade = True
# we don't upgrade in editable mode
options.editable = False
return InstallCommand.run(self, options, packages)
12 changes: 5 additions & 7 deletions tests/unit/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@

# These are the expected names of the commands whose classes inherit from
# IndexGroupCommand.
EXPECTED_INDEX_GROUP_COMMANDS = [
'download', 'index', 'install', 'list', 'wheel']
EXPECTED_INDEX_GROUP_COMMANDS = ['download', 'index', 'install', 'list', 'upgrade-all', 'wheel']


def check_commands(pred, expected):
Expand Down Expand Up @@ -50,9 +49,8 @@ def test_session_commands():
def is_session_command(command):
return isinstance(command, SessionCommandMixin)

expected = [
'download', 'index', 'install', 'list', 'search', 'uninstall', 'wheel'
]
expected = ['download', 'index' 'install', 'list', 'search', 'uninstall',
'upgrade-all', 'wheel']
check_commands(is_session_command, expected)


Expand Down Expand Up @@ -113,5 +111,5 @@ def test_requirement_commands():
"""
def is_requirement_command(command):
return isinstance(command, RequirementCommand)

check_commands(is_requirement_command, ['download', 'install', 'wheel'])
check_commands(is_requirement_command, ['download', 'install', 'upgrade-all',
'wheel'])