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

[Refactor] refactor mim uninstall #135

Merged
merged 15 commits into from Jun 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
62 changes: 53 additions & 9 deletions mim/commands/uninstall.py
@@ -1,39 +1,83 @@
# Copyright (c) OpenMMLab. All rights reserved.
import sys
from typing import Any, List, Tuple, Union

import click

from mim.click import argument, get_installed_package, param2lowercase
from mim.utils import call_command


@click.command('uninstall')
@click.command(
'uninstall',
context_settings=dict(ignore_unknown_options=True),
)
@argument(
'package', autocompletion=get_installed_package, callback=param2lowercase)
'args',
autocompletion=get_installed_package,
callback=param2lowercase,
nargs=-1,
type=click.UNPROCESSED)
@click.option(
'-y',
'--yes',
'confirm_yes',
is_flag=True,
help='Don’t ask for confirmation of uninstall deletions.')
def cli(package: str, confirm_yes: bool) -> None:
@click.option(
'-r',
'--requirement',
'requirements',
multiple=True,
help='Uninstall all the packages listed in the given requirements '
'file. This option can be used multiple times.')
def cli(args: Tuple,
confirm_yes: bool = False,
requirements: Tuple = ()) -> None:
"""Uninstall package.

Same as `pip uninstall`.

\b
Example:

> mim uninstall mmcv-full
> mim uninstall -y mmcv-full
> mim uninstall mmdet mmcls

Here we list several commonly used options.

For more options, please refer to `pip uninstall --help`.
"""
uninstall(package, confirm_yes)
exit_code = uninstall(list(args), confirm_yes, requirements)
exit(exit_code)


def uninstall(package: str, confirm_yes=False) -> None:
def uninstall(uninstall_args: Union[str, List],
confirm_yes: bool = True,
requirements: Tuple = ()) -> Any:
"""Uninstall package.

Args:
package (str): The name of uninstalled package, such as mmcv-full.
uninstall_args (str or list): A package name or a list of package names
to uninstalled. You can also put some `pip uninstal` options here.
confirm_yes (bool): Don’t ask for confirmation of uninstall deletions.
Default: True.
requirements (tuple): A tuple of requirements files to uninstalled.

Returns:
The status code returned by `pip uninstall`.
"""
uninstall_cmd = ['python', '-m', 'pip', 'uninstall', package]
if isinstance(uninstall_args, str):
uninstall_args = [uninstall_args]

if confirm_yes:
uninstall_cmd.append('-y')
uninstall_args.append('-y')

for requirement_file in requirements:
uninstall_args += ['-r', requirement_file]

call_command(uninstall_cmd)
# Use the pip official recommend way to invoke `pip uninstall`:
# https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program
pip_uninstall_cmd = [sys.executable, '-m', 'pip', 'uninstall']
return call_command(pip_uninstall_cmd + uninstall_args) # type: ignore
69 changes: 69 additions & 0 deletions tests/test_uninstall.py
@@ -0,0 +1,69 @@
# Copyright (c) OpenMMLab. All rights reserved.
from click.testing import CliRunner

from mim.commands.install import cli as install
from mim.commands.list import list_package
from mim.commands.uninstall import cli as uninstall


def setup_module():
runner = CliRunner()
result = runner.invoke(uninstall, ['mmcv-full', '--yes'])
assert result.exit_code == 0
result = runner.invoke(uninstall, ['mmcls', '--yes'])
zhouzaida marked this conversation as resolved.
Show resolved Hide resolved
assert result.exit_code == 0
result = runner.invoke(uninstall, ['mmsegmentation', '--yes'])
assert result.exit_code == 0


def test_uninstall():
runner = CliRunner()

# mim install mmsegmentation --yes
result = runner.invoke(install, ['mmsegmentation', '--yes'])
# Use importlib reload module in the same process may cause `isinstance`
# invalidation.
# A known issue: `METADATA not found in /tmp/xxx/xxx.whel` will be warning
# in pip 21.3.1, and mmcv-full could not install success as expected.
# So here we install mmsegmentation twice as an ugly workaround.
# TODO: find a better way to deal with this issues.
result = runner.invoke(install, ['mmsegmentation', '--yes'])
assert result.exit_code == 0

# check if install success
result = list_package()
installed_packages = [item[0] for item in result]
assert 'mmsegmentation' in installed_packages
assert 'mmcv-full' in installed_packages
# `mim install mmsegmentation` will install mim extra requirements (via
# mminstall.txt) automatically since PR#132, so we got installed mmcls here. # noqa: E501
assert 'mmcls' in installed_packages

# mim uninstall mmsegmentation --yes
result = runner.invoke(uninstall, ['mmsegmentation', '--yes'])
assert result.exit_code == 0

# check if uninstall success
result = list_package()
installed_packages = [item[0] for item in result]
assert 'mmsegmentation' not in installed_packages

# mim uninstall mmcls mmcv-full --yes
result = runner.invoke(uninstall, ['mmcls', 'mmcv-full', '--yes'])
assert result.exit_code == 0

# check if uninstall success
result = list_package()
installed_packages = [item[0] for item in result]
assert 'mmcls' not in installed_packages
assert 'mmcv-full' not in installed_packages


def teardown_module():
runner = CliRunner()
result = runner.invoke(uninstall, ['mmcv-full', '--yes'])
assert result.exit_code == 0
result = runner.invoke(uninstall, ['mmcls', '--yes'])
zhouzaida marked this conversation as resolved.
Show resolved Hide resolved
assert result.exit_code == 0
result = runner.invoke(uninstall, ['mmsegmentation', '--yes'])
assert result.exit_code == 0