From cab933e7e6603af7e8fdcf74c3bbbeba073171c5 Mon Sep 17 00:00:00 2001 From: nvbn Date: Wed, 13 Jan 2016 21:53:11 +0300 Subject: [PATCH 1/2] #429: Add `apt_invalid_operation` rule --- README.md | 1 + tests/rules/test_apt_invalid_operation.py | 122 ++++++++++++++++++++++ thefuck/rules/apt_invalid_operation.py | 53 ++++++++++ 3 files changed, 176 insertions(+) create mode 100644 tests/rules/test_apt_invalid_operation.py create mode 100644 thefuck/rules/apt_invalid_operation.py diff --git a/README.md b/README.md index 8b5343b87..155cbf206 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,7 @@ Enabled by default only on specific platforms: * `apt_get` – installs app from apt if it not installed (requires `python-commandnotfound` / `python3-commandnotfound`); * `apt_get_search` – changes trying to search using `apt-get` with searching using `apt-cache`; +* `apt_invalid_operation` – fixes invalid `apt` and `apt-get` calls, like `apt-get isntall vim`; * `brew_install` – fixes formula name for `brew install`; * `brew_unknown_command` – fixes wrong brew commands, for example `brew docto/brew doctor`; * `brew_upgrade` – appends `--all` to `brew upgrade` as per Homebrew's new behaviour; diff --git a/tests/rules/test_apt_invalid_operation.py b/tests/rules/test_apt_invalid_operation.py new file mode 100644 index 000000000..0e4a9cc3c --- /dev/null +++ b/tests/rules/test_apt_invalid_operation.py @@ -0,0 +1,122 @@ +from io import BytesIO +import pytest +from tests.utils import Command +from thefuck.rules.apt_invalid_operation import match, get_new_command, \ + _get_operations + +invalid_operation = 'E: Invalid operation {}'.format +apt_help = b'''apt 1.0.10.2ubuntu1 for amd64 compiled on Oct 5 2015 15:55:05 +Usage: apt [options] command + +CLI for apt. +Basic commands: + list - list packages based on package names + search - search in package descriptions + show - show package details + + update - update list of available packages + + install - install packages + remove - remove packages + + upgrade - upgrade the system by installing/upgrading packages + full-upgrade - upgrade the system by removing/installing/upgrading packages + + edit-sources - edit the source information file +''' +apt_operations = ['list', 'search', 'show', 'update', 'install', 'remove', + 'upgrade', 'full-upgrade', 'edit-sources'] + +apt_get_help = b'''apt 1.0.10.2ubuntu1 for amd64 compiled on Oct 5 2015 15:55:05 +Usage: apt-get [options] command + apt-get [options] install|remove pkg1 [pkg2 ...] + apt-get [options] source pkg1 [pkg2 ...] + +apt-get is a simple command line interface for downloading and +installing packages. The most frequently used commands are update +and install. + +Commands: + update - Retrieve new lists of packages + upgrade - Perform an upgrade + install - Install new packages (pkg is libc6 not libc6.deb) + remove - Remove packages + autoremove - Remove automatically all unused packages + purge - Remove packages and config files + source - Download source archives + build-dep - Configure build-dependencies for source packages + dist-upgrade - Distribution upgrade, see apt-get(8) + dselect-upgrade - Follow dselect selections + clean - Erase downloaded archive files + autoclean - Erase old downloaded archive files + check - Verify that there are no broken dependencies + changelog - Download and display the changelog for the given package + download - Download the binary package into the current directory + +Options: + -h This help text. + -q Loggable output - no progress indicator + -qq No output except for errors + -d Download only - do NOT install or unpack archives + -s No-act. Perform ordering simulation + -y Assume Yes to all queries and do not prompt + -f Attempt to correct a system with broken dependencies in place + -m Attempt to continue if archives are unlocatable + -u Show a list of upgraded packages as well + -b Build the source package after fetching it + -V Show verbose version numbers + -c=? Read this configuration file + -o=? Set an arbitrary configuration option, eg -o dir::cache=/tmp +See the apt-get(8), sources.list(5) and apt.conf(5) manual +pages for more information and options. + This APT has Super Cow Powers. +''' +apt_get_operations = ['update', 'upgrade', 'install', 'remove', 'autoremove', + 'purge', 'source', 'build-dep', 'dist-upgrade', + 'dselect-upgrade', 'clean', 'autoclean', 'check', + 'changelog', 'download'] + + +@pytest.mark.parametrize('script, stderr', [ + ('apt', invalid_operation('saerch')), + ('apt-get', invalid_operation('isntall')), + ('apt-cache', invalid_operation('rumove'))]) +def test_match(script, stderr): + assert match(Command(script, stderr=stderr)) + + +@pytest.mark.parametrize('script, stderr', [ + ('vim', invalid_operation), + ('apt-get', "")]) +def test_not_match(script, stderr): + assert not match(Command(script, stderr=stderr)) + + +@pytest.fixture +def set_help(mocker): + mock = mocker.patch('subprocess.Popen') + + def _set_text(text): + mock.return_value.stdout = BytesIO(text) + + return _set_text + + +@pytest.mark.parametrize('app, help_text, operations', [ + ('apt', apt_help, apt_operations), + ('apt-get', apt_get_help, apt_get_operations) +]) +def test_get_operations(set_help, app, help_text, operations): + set_help(help_text) + assert _get_operations(app) == operations + + +@pytest.mark.parametrize('script, stderr, help_text, result', [ + ('apt-get isntall vim', invalid_operation('isntall'), + apt_get_help, 'apt-get install vim'), + ('apt saerch vim', invalid_operation('saerch'), + apt_help, 'apt search vim'), +]) +def test_get_new_command(set_help, stderr, script, help_text, result): + set_help(help_text) + assert get_new_command(Command(script, stderr=stderr))[0] == result diff --git a/thefuck/rules/apt_invalid_operation.py b/thefuck/rules/apt_invalid_operation.py new file mode 100644 index 000000000..574e60d2d --- /dev/null +++ b/thefuck/rules/apt_invalid_operation.py @@ -0,0 +1,53 @@ +import subprocess +from thefuck.specific.sudo import sudo_support +from thefuck.utils import for_app, eager, replace_command + + +@for_app('apt', 'apt-get', 'apt-cache') +@sudo_support +def match(command): + return 'E: Invalid operation' in command.stderr + + +@eager +def _parse_apt_operations(help_text_lines): + is_commands_list = False + for line in help_text_lines: + line = line.decode().strip() + if is_commands_list and line: + yield line.split()[0] + elif line.startswith('Basic commands:'): + is_commands_list = True + + +@eager +def _parse_apt_get_and_cache_operations(help_text_lines): + is_commands_list = False + for line in help_text_lines: + line = line.decode().strip() + if is_commands_list: + if not line: + return + + yield line.split()[0] + elif line.startswith('Commands:'): + is_commands_list = True + + +def _get_operations(app): + proc = subprocess.Popen([app, '--help'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + lines = proc.stdout.readlines() + + if app == 'apt': + return _parse_apt_operations(lines) + else: + return _parse_apt_get_and_cache_operations(lines) + + +@sudo_support +def get_new_command(command): + invalid_operation = command.stderr.split()[-1] + operations = _get_operations(command.script_parts[0]) + return replace_command(command, invalid_operation, operations) From 20fff3142c3dd0fec3f360664b7445259c08765b Mon Sep 17 00:00:00 2001 From: nvbn Date: Wed, 13 Jan 2016 22:08:24 +0300 Subject: [PATCH 2/2] #429: Fix tests with python 2 --- tests/rules/test_apt_invalid_operation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rules/test_apt_invalid_operation.py b/tests/rules/test_apt_invalid_operation.py index 0e4a9cc3c..23ae9d5f5 100644 --- a/tests/rules/test_apt_invalid_operation.py +++ b/tests/rules/test_apt_invalid_operation.py @@ -86,7 +86,7 @@ def test_match(script, stderr): @pytest.mark.parametrize('script, stderr', [ - ('vim', invalid_operation), + ('vim', invalid_operation('vim')), ('apt-get', "")]) def test_not_match(script, stderr): assert not match(Command(script, stderr=stderr))