diff --git a/tests/rules/test_lein_not_task.py b/tests/rules/test_lein_not_task.py index 9eef9b4..9069fcd 100644 --- a/tests/rules/test_lein_not_task.py +++ b/tests/rules/test_lein_not_task.py @@ -1,6 +1,6 @@ import pytest -from mock import Mock from thefuck.rules.lein_not_task import match, get_new_command +from tests.utils import Command @pytest.fixture @@ -14,10 +14,10 @@ Did you mean this? def test_match(is_not_task): - assert match(Mock(script='lein rpl', stderr=is_not_task), None) - assert not match(Mock(script='ls', stderr=is_not_task), None) + assert match(Command(script='lein rpl', stderr=is_not_task), None) + assert not match(Command(script='ls', stderr=is_not_task), None) def test_get_new_command(is_not_task): - assert get_new_command(Mock(script='lein rpl --help', stderr=is_not_task), + assert get_new_command(Command(script='lein rpl --help', stderr=is_not_task), None) == ['lein repl --help', 'lein jar --help'] diff --git a/tests/rules/test_ls_lah.py b/tests/rules/test_ls_lah.py index 66bc836..9732525 100644 --- a/tests/rules/test_ls_lah.py +++ b/tests/rules/test_ls_lah.py @@ -1,16 +1,16 @@ -from mock import patch, Mock from thefuck.rules.ls_lah import match, get_new_command +from tests.utils import Command def test_match(): - assert match(Mock(script='ls'), None) - assert match(Mock(script='ls file.py'), None) - assert match(Mock(script='ls /opt'), None) - assert not match(Mock(script='ls -lah /opt'), None) - assert not match(Mock(script='pacman -S binutils'), None) - assert not match(Mock(script='lsof'), None) + assert match(Command(script='ls'), None) + assert match(Command(script='ls file.py'), None) + assert match(Command(script='ls /opt'), None) + assert not match(Command(script='ls -lah /opt'), None) + assert not match(Command(script='pacman -S binutils'), None) + assert not match(Command(script='lsof'), None) def test_get_new_command(): - assert get_new_command(Mock(script='ls file.py'), None) == 'ls -lah file.py' - assert get_new_command(Mock(script='ls'), None) == 'ls -lah' + assert get_new_command(Command(script='ls file.py'), None) == 'ls -lah file.py' + assert get_new_command(Command(script='ls'), None) == 'ls -lah' diff --git a/thefuck/rules/apt_get_search.py b/thefuck/rules/apt_get_search.py index 6c06ddd..4454e85 100644 --- a/thefuck/rules/apt_get_search.py +++ b/thefuck/rules/apt_get_search.py @@ -1,6 +1,8 @@ import re +from thefuck.utils import for_app +@for_app('apt-get') def match(command, settings): return command.script.startswith('apt-get search') diff --git a/thefuck/rules/cargo_no_command.py b/thefuck/rules/cargo_no_command.py index c4f6c07..ce77fe6 100644 --- a/thefuck/rules/cargo_no_command.py +++ b/thefuck/rules/cargo_no_command.py @@ -1,10 +1,10 @@ import re -from thefuck.utils import replace_argument +from thefuck.utils import replace_argument, for_app +@for_app('cargo') def match(command, settings): - return ('cargo' in command.script - and 'No such subcommand' in command.stderr + return ('No such subcommand' in command.stderr and 'Did you mean' in command.stderr) diff --git a/thefuck/rules/cd_correction.py b/thefuck/rules/cd_correction.py index 1567ff0..33c4fc3 100644 --- a/thefuck/rules/cd_correction.py +++ b/thefuck/rules/cd_correction.py @@ -4,6 +4,7 @@ import os from difflib import get_close_matches from thefuck.specific.sudo import sudo_support from thefuck.rules import cd_mkdir +from thefuck.utils import for_app __author__ = "mmussomele" @@ -16,6 +17,7 @@ def _get_sub_dirs(parent): @sudo_support +@for_app('cd') def match(command, settings): """Match function copied from cd_mkdir.py""" return (command.script.startswith('cd ') diff --git a/thefuck/rules/cd_mkdir.py b/thefuck/rules/cd_mkdir.py index c9ad1f7..2262af7 100644 --- a/thefuck/rules/cd_mkdir.py +++ b/thefuck/rules/cd_mkdir.py @@ -1,13 +1,14 @@ import re from thefuck import shells +from thefuck.utils import for_app from thefuck.specific.sudo import sudo_support @sudo_support +@for_app('cd') def match(command, settings): - return (command.script.startswith('cd ') - and ('no such file or directory' in command.stderr.lower() - or 'cd: can\'t cd to' in command.stderr.lower())) + return (('no such file or directory' in command.stderr.lower() + or 'cd: can\'t cd to' in command.stderr.lower())) @sudo_support diff --git a/thefuck/rules/composer_not_command.py b/thefuck/rules/composer_not_command.py index 115ca18..abea7b3 100644 --- a/thefuck/rules/composer_not_command.py +++ b/thefuck/rules/composer_not_command.py @@ -1,11 +1,11 @@ import re -from thefuck.utils import replace_argument +from thefuck.utils import replace_argument, for_app +@for_app('composer') def match(command, settings): - return ('composer' in command.script - and ('did you mean this?' in command.stderr.lower() - or 'did you mean one of these?' in command.stderr.lower())) + return (('did you mean this?' in command.stderr.lower() + or 'did you mean one of these?' in command.stderr.lower())) def get_new_command(command, settings): diff --git a/thefuck/rules/cp_omitting_directory.py b/thefuck/rules/cp_omitting_directory.py index 9a110e0..51fa129 100644 --- a/thefuck/rules/cp_omitting_directory.py +++ b/thefuck/rules/cp_omitting_directory.py @@ -1,12 +1,13 @@ import re from thefuck.specific.sudo import sudo_support +from thefuck.utils import for_app @sudo_support +@for_app('cp') def match(command, settings): stderr = command.stderr.lower() - return command.script.startswith('cp ') \ - and ('omitting directory' in stderr or 'is a directory' in stderr) + return 'omitting directory' in stderr or 'is a directory' in stderr @sudo_support diff --git a/thefuck/rules/cpp11.py b/thefuck/rules/cpp11.py index 154abab..200bf4d 100644 --- a/thefuck/rules/cpp11.py +++ b/thefuck/rules/cpp11.py @@ -1,8 +1,11 @@ +from thefuck.utils import for_app + + +@for_app(['g++', 'clang++']) def match(command, settings): - return (('g++' in command.script or 'clang++' in command.script) and - ('This file requires compiler and library support for the ' - 'ISO C++ 2011 standard.' in command.stderr or - '-Wc++11-extensions' in command.stderr)) + return ('This file requires compiler and library support for the ' + 'ISO C++ 2011 standard.' in command.stderr or + '-Wc++11-extensions' in command.stderr) def get_new_command(command, settings): diff --git a/thefuck/rules/dirty_untar.py b/thefuck/rules/dirty_untar.py index 25300e7..4fdf4cf 100644 --- a/thefuck/rules/dirty_untar.py +++ b/thefuck/rules/dirty_untar.py @@ -1,6 +1,7 @@ -from thefuck import shells import os import tarfile +from thefuck import shells +from thefuck.utils import for_app def _is_tar_extract(cmd): @@ -20,19 +21,19 @@ def _tar_file(cmd): for c in cmd.split(): for ext in tar_extensions: if c.endswith(ext): - return (c, c[0:len(c)-len(ext)]) + return (c, c[0:len(c) - len(ext)]) +@for_app('tar') def match(command, settings): - return (command.script.startswith('tar') - and '-C' not in command.script + return ('-C' not in command.script and _is_tar_extract(command.script) and _tar_file(command.script) is not None) def get_new_command(command, settings): return shells.and_('mkdir -p {dir}', '{cmd} -C {dir}') \ - .format(dir=_tar_file(command.script)[1], cmd=command.script) + .format(dir=_tar_file(command.script)[1], cmd=command.script) def side_effect(old_cmd, command, settings): diff --git a/thefuck/rules/dirty_unzip.py b/thefuck/rules/dirty_unzip.py index 738cf82..bd2d594 100644 --- a/thefuck/rules/dirty_unzip.py +++ b/thefuck/rules/dirty_unzip.py @@ -1,5 +1,6 @@ import os import zipfile +from thefuck.utils import for_app def _is_bad_zip(file): @@ -20,9 +21,9 @@ def _zip_file(command): return '{}.zip'.format(c) +@for_app('unzip') def match(command, settings): - return (command.script.startswith('unzip') - and '-d' not in command.script + return ('-d' not in command.script and _is_bad_zip(_zip_file(command))) diff --git a/thefuck/rules/docker_not_command.py b/thefuck/rules/docker_not_command.py index 73cb861..44578e3 100644 --- a/thefuck/rules/docker_not_command.py +++ b/thefuck/rules/docker_not_command.py @@ -1,14 +1,14 @@ from itertools import dropwhile, takewhile, islice import re import subprocess -from thefuck.utils import replace_command +from thefuck.utils import replace_command, for_app from thefuck.specific.sudo import sudo_support @sudo_support +@for_app('docker') def match(command, settings): - return command.script.startswith('docker') \ - and 'is not a docker command' in command.stderr + return 'is not a docker command' in command.stderr def get_docker_commands(): diff --git a/thefuck/rules/git_push_force.py b/thefuck/rules/git_push_force.py index 52ecfe3..45a3085 100644 --- a/thefuck/rules/git_push_force.py +++ b/thefuck/rules/git_push_force.py @@ -1,4 +1,3 @@ -from thefuck import utils from thefuck.utils import replace_argument from thefuck.specific.git import git_support diff --git a/thefuck/rules/go_run.py b/thefuck/rules/go_run.py index b32c646..b009324 100644 --- a/thefuck/rules/go_run.py +++ b/thefuck/rules/go_run.py @@ -1,3 +1,4 @@ +from thefuck.utils import for_app # Appends .go when compiling go files # # Example: @@ -5,6 +6,7 @@ # error: go run: no go files listed +@for_app('go') def match(command, settings): return (command.script.startswith('go run ') and not command.script.endswith('.go')) diff --git a/thefuck/rules/grep_recursive.py b/thefuck/rules/grep_recursive.py index f2876fe..94547b2 100644 --- a/thefuck/rules/grep_recursive.py +++ b/thefuck/rules/grep_recursive.py @@ -1,6 +1,9 @@ +from thefuck.utils import for_app + + +@for_app('grep') def match(command, settings): - return (command.script.startswith('grep') - and 'is a directory' in command.stderr.lower()) + return 'is a directory' in command.stderr.lower() def get_new_command(command, settings): diff --git a/thefuck/rules/gulp_not_task.py b/thefuck/rules/gulp_not_task.py index c1a548c..853fa60 100644 --- a/thefuck/rules/gulp_not_task.py +++ b/thefuck/rules/gulp_not_task.py @@ -1,11 +1,11 @@ import re import subprocess -from thefuck.utils import replace_command +from thefuck.utils import replace_command, for_app +@for_app('gulp') def match(command, script): - return command.script.startswith('gulp')\ - and 'is not in your gulpfile' in command.stdout + return 'is not in your gulpfile' in command.stdout def get_gulp_tasks(): diff --git a/thefuck/rules/heroku_not_command.py b/thefuck/rules/heroku_not_command.py index 87360bc..a01e157 100644 --- a/thefuck/rules/heroku_not_command.py +++ b/thefuck/rules/heroku_not_command.py @@ -1,10 +1,10 @@ import re -from thefuck.utils import replace_command +from thefuck.utils import replace_command, for_app +@for_app('heroku') def match(command, settings): - return command.script.startswith('heroku') and \ - 'is not a heroku command' in command.stderr and \ + return 'is not a heroku command' in command.stderr and \ 'Perhaps you meant' in command.stderr diff --git a/thefuck/rules/java.py b/thefuck/rules/java.py index d285232..f7a33c7 100644 --- a/thefuck/rules/java.py +++ b/thefuck/rules/java.py @@ -1,13 +1,16 @@ -# Fixes common java command mistake -# -# Example: -# > java foo.java -# Error: Could not find or load main class foo.java +"""Fixes common java command mistake + +Example: +> java foo.java +Error: Could not find or load main class foo.java + +""" +from thefuck.utils import for_app +@for_app('java') def match(command, settings): - return (command.script.startswith('java ') - and command.script.endswith('.java')) + return command.script.endswith('.java') def get_new_command(command, settings): diff --git a/thefuck/rules/javac.py b/thefuck/rules/javac.py index 80e6b25..be40a5e 100644 --- a/thefuck/rules/javac.py +++ b/thefuck/rules/javac.py @@ -1,14 +1,17 @@ -# Appends .java when compiling java files -# -# Example: -# > javac foo -# error: Class names, 'foo', are only accepted if annotation -# processing is explicitly requested +"""Appends .java when compiling java files + +Example: + > javac foo + error: Class names, 'foo', are only accepted if annotation + processing is explicitly requested + +""" +from thefuck.utils import for_app +@for_app('javac') def match(command, settings): - return (command.script.startswith('javac ') - and not command.script.endswith('.java')) + return not command.script.endswith('.java') def get_new_command(command, settings): diff --git a/thefuck/rules/lein_not_task.py b/thefuck/rules/lein_not_task.py index db98c95..3849ac5 100644 --- a/thefuck/rules/lein_not_task.py +++ b/thefuck/rules/lein_not_task.py @@ -1,9 +1,10 @@ import re -from thefuck.utils import replace_command, get_all_matched_commands +from thefuck.utils import replace_command, get_all_matched_commands, for_app from thefuck.specific.sudo import sudo_support @sudo_support +@for_app('lein') def match(command, settings): return (command.script.startswith('lein') and "is not a task. See 'lein help'" in command.stderr diff --git a/thefuck/rules/ls_lah.py b/thefuck/rules/ls_lah.py index 580744b..b8e6590 100644 --- a/thefuck/rules/ls_lah.py +++ b/thefuck/rules/ls_lah.py @@ -1,7 +1,9 @@ +from thefuck.utils import for_app + + +@for_app('ls') def match(command, settings): - return (command.script == 'ls' - or command.script.startswith('ls ') - and 'ls -' not in command.script) + return 'ls -' not in command.script def get_new_command(command, settings): diff --git a/thefuck/rules/mercurial.py b/thefuck/rules/mercurial.py index c2e9aa6..338629a 100644 --- a/thefuck/rules/mercurial.py +++ b/thefuck/rules/mercurial.py @@ -1,5 +1,5 @@ import re -from thefuck.utils import get_closest +from thefuck.utils import get_closest, for_app def extract_possibilities(command): @@ -12,14 +12,12 @@ def extract_possibilities(command): return possib +@for_app('hg') def match(command, settings): - return (command.script.startswith('hg ') - and ('hg: unknown command' in command.stderr - and '(did you mean one of ' in command.stderr - or "hg: command '" in command.stderr - and "' is ambiguous:" in command.stderr - ) - ) + return ('hg: unknown command' in command.stderr + and '(did you mean one of ' in command.stderr + or "hg: command '" in command.stderr + and "' is ambiguous:" in command.stderr) def get_new_command(command, settings): diff --git a/thefuck/rules/open.py b/thefuck/rules/open.py index 22aaea3..6de2c96 100644 --- a/thefuck/rules/open.py +++ b/thefuck/rules/open.py @@ -5,21 +5,21 @@ # The file ~/github.com does not exist. # Perhaps you meant 'http://github.com'? # +from thefuck.utils import for_app +@for_app('open', 'xdg-open', 'gnome-open', 'kde-open') def match(command, settings): - return (command.script.startswith(('open', 'xdg-open', 'gnome-open', 'kde-open')) - and ( - '.com' in command.script - or '.net' in command.script - or '.org' in command.script - or '.ly' in command.script - or '.io' in command.script - or '.se' in command.script - or '.edu' in command.script - or '.info' in command.script - or '.me' in command.script - or 'www.' in command.script)) + return ('.com' in command.script + or '.net' in command.script + or '.org' in command.script + or '.ly' in command.script + or '.io' in command.script + or '.se' in command.script + or '.edu' in command.script + or '.info' in command.script + or '.me' in command.script + or 'www.' in command.script) def get_new_command(command, settings): diff --git a/thefuck/rules/pip_unknown_command.py b/thefuck/rules/pip_unknown_command.py index 9ae185d..61293f8 100644 --- a/thefuck/rules/pip_unknown_command.py +++ b/thefuck/rules/pip_unknown_command.py @@ -1,7 +1,10 @@ import re -from thefuck.utils import replace_argument +from thefuck.utils import replace_argument, for_app +from thefuck.specific.sudo import sudo_support +@sudo_support +@for_app('pip') def match(command, settings): return ('pip' in command.script and 'unknown command' in command.stderr and diff --git a/thefuck/rules/python_execute.py b/thefuck/rules/python_execute.py index d4d9d26..2de751e 100644 --- a/thefuck/rules/python_execute.py +++ b/thefuck/rules/python_execute.py @@ -3,11 +3,12 @@ # Example: # > python foo # error: python: can't open file 'foo': [Errno 2] No such file or directory +from thefuck.utils import for_app +@for_app('python') def match(command, settings): - return (command.script.startswith('python ') - and not command.script.endswith('.py')) + return not command.script.endswith('.py') def get_new_command(command, settings): diff --git a/thefuck/rules/sed_unterminated_s.py b/thefuck/rules/sed_unterminated_s.py index 80334f9..5a8ea6d 100644 --- a/thefuck/rules/sed_unterminated_s.py +++ b/thefuck/rules/sed_unterminated_s.py @@ -1,10 +1,10 @@ import shlex -from thefuck.utils import quote +from thefuck.utils import quote, for_app +@for_app('sed') def match(command, settings): - return ('sed' in command.script - and "unterminated `s' command" in command.stderr) + return "unterminated `s' command" in command.stderr def get_new_command(command, settings): diff --git a/thefuck/rules/ssh_known_hosts.py b/thefuck/rules/ssh_known_hosts.py index df908d0..b14533b 100644 --- a/thefuck/rules/ssh_known_hosts.py +++ b/thefuck/rules/ssh_known_hosts.py @@ -1,4 +1,5 @@ import re +from thefuck.utils import for_app patterns = [ r'WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!', @@ -12,6 +13,7 @@ offending_pattern = re.compile( commands = ['ssh', 'scp'] +@for_app(*commands) def match(command, settings): if not command.script: return False diff --git a/thefuck/rules/systemctl.py b/thefuck/rules/systemctl.py index ef8c0ca..3edca9f 100644 --- a/thefuck/rules/systemctl.py +++ b/thefuck/rules/systemctl.py @@ -2,15 +2,16 @@ The confusion in systemctl's param order is massive. """ from thefuck.specific.sudo import sudo_support +from thefuck.utils import for_app @sudo_support +@for_app('systemctl') def match(command, settings): # Catches 'Unknown operation 'service'.' when executing systemctl with # misordered arguments cmd = command.script.split() - return ('systemctl' in command.script and - 'Unknown operation \'' in command.stderr and + return ('Unknown operation \'' in command.stderr and len(cmd) - cmd.index('systemctl') == 3) diff --git a/thefuck/rules/tmux.py b/thefuck/rules/tmux.py index 2ba446e..09acb57 100644 --- a/thefuck/rules/tmux.py +++ b/thefuck/rules/tmux.py @@ -1,10 +1,10 @@ -from thefuck.utils import replace_command import re +from thefuck.utils import replace_command, for_app +@for_app('tmux') def match(command, settings): - return ('tmux' in command.script - and 'ambiguous command:' in command.stderr + return ('ambiguous command:' in command.stderr and 'could be:' in command.stderr) diff --git a/thefuck/rules/tsuru_login.py b/thefuck/rules/tsuru_login.py index b71803f..fc91715 100644 --- a/thefuck/rules/tsuru_login.py +++ b/thefuck/rules/tsuru_login.py @@ -1,9 +1,10 @@ from thefuck import shells +from thefuck.utils import for_app +@for_app('tsuru') def match(command, settings): - return (command.script.startswith('tsuru') - and 'not authenticated' in command.stderr + return ('not authenticated' in command.stderr and 'session has expired' in command.stderr) diff --git a/thefuck/rules/tsuru_not_command.py b/thefuck/rules/tsuru_not_command.py index 86b4d15..498a493 100644 --- a/thefuck/rules/tsuru_not_command.py +++ b/thefuck/rules/tsuru_not_command.py @@ -1,10 +1,10 @@ import re -from thefuck.utils import get_all_matched_commands, replace_command +from thefuck.utils import get_all_matched_commands, replace_command, for_app +@for_app('tsuru') def match(command, settings): - return (command.script.startswith('tsuru ') - and ' is not a tsuru command. See "tsuru help".' in command.stderr + return (' is not a tsuru command. See "tsuru help".' in command.stderr and '\nDid you mean?\n\t' in command.stderr) diff --git a/thefuck/rules/vagrant_up.py b/thefuck/rules/vagrant_up.py index 9c0a1e4..7830f30 100644 --- a/thefuck/rules/vagrant_up.py +++ b/thefuck/rules/vagrant_up.py @@ -1,8 +1,10 @@ from thefuck import shells +from thefuck.utils import for_app +@for_app('vagrant') def match(command, settings): - return command.script.startswith('vagrant ') and 'run `vagrant up`' in command.stderr.lower() + return 'run `vagrant up`' in command.stderr.lower() def get_new_command(command, settings): diff --git a/thefuck/specific/git.py b/thefuck/specific/git.py index b6cd22f..4c41598 100644 --- a/thefuck/specific/git.py +++ b/thefuck/specific/git.py @@ -2,21 +2,18 @@ from functools import wraps import re from shlex import split from ..types import Command -from ..utils import quote +from ..utils import quote, for_app def git_support(fn): """Resolves git aliases and supports testing for both git and hub.""" + # supports GitHub's `hub` command + # which is recommended to be used with `alias git=hub` + # but at this point, shell aliases have already been resolved + + @for_app('git', 'hub') @wraps(fn) def wrapper(command, settings): - # supports GitHub's `hub` command - # which is recommended to be used with `alias git=hub` - # but at this point, shell aliases have already been resolved - is_git_cmd = command.script.startswith(('git', 'hub')) - - if not is_git_cmd: - return False - # perform git aliases expansion if 'trace: alias expansion:' in command.stderr: search = re.search("trace: alias expansion: ([^ ]*) => ([^\n]*)",