1from __future__ import absolute_import
2
3from pip._vendor.packaging.utils import canonicalize_name
4
5from pip._internal.cli.base_command import Command
6from pip._internal.cli.req_command import SessionCommandMixin
7from pip._internal.cli.status_codes import SUCCESS
8from pip._internal.exceptions import InstallationError
9from pip._internal.req import parse_requirements
10from pip._internal.req.constructors import (
11    install_req_from_line,
12    install_req_from_parsed_requirement,
13)
14from pip._internal.utils.misc import protect_pip_from_modification_on_windows
15from pip._internal.utils.typing import MYPY_CHECK_RUNNING
16
17if MYPY_CHECK_RUNNING:
18    from optparse import Values
19    from typing import List
20
21
22class UninstallCommand(Command, SessionCommandMixin):
23    """
24    Uninstall packages.
25
26    pip is able to uninstall most installed packages. Known exceptions are:
27
28    - Pure distutils packages installed with ``python setup.py install``, which
29      leave behind no metadata to determine what files were installed.
30    - Script wrappers installed by ``python setup.py develop``.
31    """
32
33    usage = """
34      %prog [options] <package> ...
35      %prog [options] -r <requirements file> ..."""
36
37    def add_options(self):
38        # type: () -> None
39        self.cmd_opts.add_option(
40            '-r', '--requirement',
41            dest='requirements',
42            action='append',
43            default=[],
44            metavar='file',
45            help='Uninstall all the packages listed in the given requirements '
46                 'file.  This option can be used multiple times.',
47        )
48        self.cmd_opts.add_option(
49            '-y', '--yes',
50            dest='yes',
51            action='store_true',
52            help="Don't ask for confirmation of uninstall deletions.")
53
54        self.parser.insert_option_group(0, self.cmd_opts)
55
56    def run(self, options, args):
57        # type: (Values, List[str]) -> int
58        session = self.get_default_session(options)
59
60        reqs_to_uninstall = {}
61        for name in args:
62            req = install_req_from_line(
63                name, isolated=options.isolated_mode,
64            )
65            if req.name:
66                reqs_to_uninstall[canonicalize_name(req.name)] = req
67        for filename in options.requirements:
68            for parsed_req in parse_requirements(
69                    filename,
70                    options=options,
71                    session=session):
72                req = install_req_from_parsed_requirement(
73                    parsed_req,
74                    isolated=options.isolated_mode
75                )
76                if req.name:
77                    reqs_to_uninstall[canonicalize_name(req.name)] = req
78        if not reqs_to_uninstall:
79            raise InstallationError(
80                'You must give at least one requirement to {self.name} (see '
81                '"pip help {self.name}")'.format(**locals())
82            )
83
84        protect_pip_from_modification_on_windows(
85            modifying_pip="pip" in reqs_to_uninstall
86        )
87
88        for req in reqs_to_uninstall.values():
89            uninstall_pathset = req.uninstall(
90                auto_confirm=options.yes, verbose=self.verbosity > 0,
91            )
92            if uninstall_pathset:
93                uninstall_pathset.commit()
94
95        return SUCCESS
96