diff --git a/README.md b/README.md index 60efa2aa..56e7bb83 100644 --- a/README.md +++ b/README.md @@ -193,9 +193,7 @@ The Fuck has a few settings parameters, they can be changed in `~/.thefuck/setti * `rules` – list of enabled rules, by default all; * `require_confirmation` – require confirmation before running new command, by default `False`; -* `wait_command` – max amount of time in seconds for getting previous command output; -* `command_not_found` – path to `command_not_found` binary, -by default `/usr/lib/command-not-found`. +* `wait_command` – max amount of time in seconds for getting previous command output. ## Developing diff --git a/tests/rules/test_no_command.py b/tests/rules/test_no_command.py index 55983788..64a2423a 100644 --- a/tests/rules/test_no_command.py +++ b/tests/rules/test_no_command.py @@ -1,62 +1,19 @@ -from subprocess import PIPE from mock import patch, Mock -import pytest from thefuck.rules.no_command import match, get_new_command -from thefuck.main import Command -@pytest.fixture -def command_found(): - return b'''No command 'aptget' found, did you mean: - Command 'apt-get' from package 'apt' (main) -aptget: command not found -''' - -@pytest.fixture -def command_not_found(): - return b'''No command 'vom' found, but there are 19 similar ones -vom: command not found -''' +def test_match(): + with patch('thefuck.rules.no_command._get_all_bins', + return_value=['vim', 'apt-get']): + assert match(Mock(stderr='vom: not found', script='vom file.py'), None) + assert not match(Mock(stderr='qweqwe: not found', script='qweqwe'), None) + assert not match(Mock(stderr='some text', script='vom file.py'), None) -@pytest.fixture -def bins_exists(request): - p = patch('thefuck.rules.no_command.which', - return_value=True) - p.start() - request.addfinalizer(p.stop) - - -@pytest.fixture -def settings(): - class _Settings(object): - pass - return _Settings - - -@pytest.mark.usefixtures('bins_exists') -def test_match(command_found, command_not_found, settings): - with patch('thefuck.rules.no_command.Popen') as Popen: - Popen.return_value.stderr.read.return_value = command_found - assert match(Command('aptget install vim', '', ''), settings) - Popen.assert_called_once_with('/usr/lib/command-not-found aptget', - shell=True, stderr=PIPE) - Popen.return_value.stderr.read.return_value = command_not_found - assert not match(Command('ls', '', ''), settings) - - with patch('thefuck.rules.no_command.Popen') as Popen: - Popen.return_value.stderr.read.return_value = command_found - assert match(Command('sudo aptget install vim', '', ''), - Mock(command_not_found='test')) - Popen.assert_called_once_with('test aptget', - shell=True, stderr=PIPE) - - -@pytest.mark.usefixtures('bins_exists') -def test_get_new_command(command_found): - with patch('thefuck.rules.no_command._get_output', - return_value=command_found.decode()): - assert get_new_command(Command('aptget install vim', '', ''), settings)\ - == 'apt-get install vim' - assert get_new_command(Command('sudo aptget install vim', '', ''), settings) \ - == 'sudo apt-get install vim' +def test_get_new_command(): + with patch('thefuck.rules.no_command._get_all_bins', + return_value=['vim', 'apt-get']): + assert get_new_command( + Mock(stderr='vom: not found', + script='vom file.py'), + None) == 'vim file.py' diff --git a/thefuck/rules/no_command.py b/thefuck/rules/no_command.py index 37b3647f..dd44675e 100644 --- a/thefuck/rules/no_command.py +++ b/thefuck/rules/no_command.py @@ -1,30 +1,23 @@ -from subprocess import Popen, PIPE -import re -from thefuck.utils import which, wrap_settings +from difflib import get_close_matches +import os +from pathlib import Path -local_settings = {'command_not_found': '/usr/lib/command-not-found'} +def _get_all_bins(): + return [exe.name + for path in os.environ['PATH'].split(':') + for exe in Path(path).iterdir() + if exe.is_file()] -def _get_output(command, settings): - name = command.script.split(' ')[command.script.startswith('sudo')] - check_script = u'{} {}'.format(settings.command_not_found, name) - result = Popen(check_script, shell=True, stderr=PIPE) - return result.stderr.read().decode('utf-8') - - -@wrap_settings(local_settings) def match(command, settings): - if which(settings.command_not_found): - output = _get_output(command, settings) - return "No command" in output and "from package" in output + return 'not found' in command.stderr and \ + bool(get_close_matches(command.script.split(' ')[0], + _get_all_bins())) -@wrap_settings(local_settings) def get_new_command(command, settings): - output = _get_output(command, settings) - broken_name = re.findall(r"No command '([^']*)' found", - output)[0] - fixed_name = re.findall(r"Command '([^']*)' from package", - output)[0] - return command.script.replace(broken_name, fixed_name, 1) + old_command = command.script.split(' ')[0] + new_command = get_close_matches(old_command, + _get_all_bins())[0] + return ' '.join([new_command] + command.script.split(' ')[1:])