From 0d86fce9befd5bd1d73d7cb780fdddf7af3058a6 Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Wed, 26 Aug 2015 14:01:36 +0100 Subject: [PATCH 1/5] #35 mvn will auto add clean package --- README.md | 1 + tests/rules/test_mvn_no_command.py | 40 ++++++++++++++++++++++++++++++ thefuck/rules/mvn_no_command.py | 8 ++++++ 3 files changed, 49 insertions(+) create mode 100644 tests/rules/test_mvn_no_command.py create mode 100644 thefuck/rules/mvn_no_command.py diff --git a/README.md b/README.md index 97354de..a78cd64 100644 --- a/README.md +++ b/README.md @@ -181,6 +181,7 @@ using the matched rule and runs it. Rules enabled by default are as follows: * `man_no_space` – fixes man commands without spaces, for example `mandiff`; * `mercurial` – fixes wrong `hg` commands; * `mkdir_p` – adds `-p` when you trying to create directory without parent; +* `mvn_no_command` – adds `clean package` to `mvn`; * `no_command` – fixes wrong console commands, for example `vom/vim`; * `no_such_file` – creates missing directories with `mv` and `cp` commands; * `open` – prepends `http` to address passed to `open`; diff --git a/tests/rules/test_mvn_no_command.py b/tests/rules/test_mvn_no_command.py new file mode 100644 index 0000000..5725add --- /dev/null +++ b/tests/rules/test_mvn_no_command.py @@ -0,0 +1,40 @@ +import pytest +from thefuck.rules.mvn_no_command import match, get_new_command +from tests.utils import Command + + +@pytest.mark.parametrize('command', [ + Command(script='mvn', stdout='[ERROR] No goals have been specified for this build. You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]')]) +def test_match(command): + assert match(command, None) + + +@pytest.mark.parametrize('command', [ + Command(script='mvn clean', stdout=""" +[INFO] Scanning for projects...[INFO] +[INFO] ------------------------------------------------------------------------ +[INFO] Building test 0.2 +[INFO] ------------------------------------------------------------------------ +[INFO] +[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ test --- +[INFO] Deleting /home/mlk/code/test/target +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 0.477s +[INFO] Finished at: Wed Aug 26 13:05:47 BST 2015 +[INFO] Final Memory: 6M/240M +[INFO] ------------------------------------------------------------------------ +"""), + Command(script='mvn --help'), + Command(script='mvn -v') +]) +def test_not_match(command): + assert not match(command, None) + +@pytest.mark.parametrize('command, new_command', [ + (Command(script='mvn', stdout='[ERROR] No goals have been specified for this build. You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]'), ['mvn clean package', 'mvn clean install']), + (Command(script='mvn -N', stdout='[ERROR] No goals have been specified for this build. You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]'), ['mvn -N clean package', 'mvn -N clean install'])]) +def test_get_new_command(command, new_command): + assert get_new_command(command, None) == new_command + diff --git a/thefuck/rules/mvn_no_command.py b/thefuck/rules/mvn_no_command.py new file mode 100644 index 0000000..53c7106 --- /dev/null +++ b/thefuck/rules/mvn_no_command.py @@ -0,0 +1,8 @@ +from thefuck import shells + +def match(command, settings): + return 'No goals have been specified for this build' in command.stdout and command.script.startswith('mvn') + + +def get_new_command(command, settings): + return [ command.script + ' clean package', command.script + ' clean install'] From 301de75aeed2f502a405ad9b81e005e5b46dcfa4 Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Wed, 26 Aug 2015 14:58:45 +0100 Subject: [PATCH 2/5] #35 - Fuzzy matching on maven lifecycle targets --- README.md | 1 + .../rules/test_mvn_unknown_lifecycle_phase.py | 41 +++++++++++++++++++ thefuck/rules/mvn_unknown_lifecycle_phase.py | 20 +++++++++ 3 files changed, 62 insertions(+) create mode 100644 tests/rules/test_mvn_unknown_lifecycle_phase.py create mode 100644 thefuck/rules/mvn_unknown_lifecycle_phase.py diff --git a/README.md b/README.md index a78cd64..93832b9 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,7 @@ using the matched rule and runs it. Rules enabled by default are as follows: * `mercurial` – fixes wrong `hg` commands; * `mkdir_p` – adds `-p` when you trying to create directory without parent; * `mvn_no_command` – adds `clean package` to `mvn`; +* `mvn_unknown_lifecycle_phase` – fixes miss spelt lifecycle phases with `mvn`; * `no_command` – fixes wrong console commands, for example `vom/vim`; * `no_such_file` – creates missing directories with `mv` and `cp` commands; * `open` – prepends `http` to address passed to `open`; diff --git a/tests/rules/test_mvn_unknown_lifecycle_phase.py b/tests/rules/test_mvn_unknown_lifecycle_phase.py new file mode 100644 index 0000000..47ff447 --- /dev/null +++ b/tests/rules/test_mvn_unknown_lifecycle_phase.py @@ -0,0 +1,41 @@ +import pytest +from thefuck.rules.mvn_unknown_lifecycle_phase import match, get_new_command +from tests.utils import Command + + +@pytest.mark.parametrize('command', [ + Command(script='mvn cle', stdout='[ERROR] Unknown lifecycle phase "cle". You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]')]) +def test_match(command): + assert match(command, None) + + +@pytest.mark.parametrize('command', [ + Command(script='mvn clean', stdout=""" +[INFO] Scanning for projects...[INFO] +[INFO] ------------------------------------------------------------------------ +[INFO] Building test 0.2 +[INFO] ------------------------------------------------------------------------ +[INFO] +[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ test --- +[INFO] Deleting /home/mlk/code/test/target +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 0.477s +[INFO] Finished at: Wed Aug 26 13:05:47 BST 2015 +[INFO] Final Memory: 6M/240M +[INFO] ------------------------------------------------------------------------ +"""), + Command(script='mvn --help'), + Command(script='mvn -v') +]) +def test_not_match(command): + assert not match(command, None) + +@pytest.mark.parametrize('command, new_command', [ + (Command(script='mvn cle', stdout='[ERROR] Unknown lifecycle phase "cle". You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]'), ['mvn clean', 'mvn compile']), + (Command(script='mvn claen package', stdout='[ERROR] Unknown lifecycle phase "claen". You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]'), ['mvn clean package'])]) +def test_get_new_command(command, new_command): + print new_command + assert get_new_command(command, None) == new_command + diff --git a/thefuck/rules/mvn_unknown_lifecycle_phase.py b/thefuck/rules/mvn_unknown_lifecycle_phase.py new file mode 100644 index 0000000..1dcf1ad --- /dev/null +++ b/thefuck/rules/mvn_unknown_lifecycle_phase.py @@ -0,0 +1,20 @@ +from thefuck import shells +from thefuck.utils import replace_command +from difflib import get_close_matches +import re + + +def match(command, settings): + failedLifecycle = re.search('\[ERROR\] Unknown lifecycle phase "(.+)"', command.stdout) + availableLifecycles = re.search('Available lifecycle phases are: (.+) -> \[Help 1\]', command.stdout) + return availableLifecycles and failedLifecycle and command.script.startswith('mvn') + + +def get_new_command(command, settings): + failedLifecycle = re.search('\[ERROR\] Unknown lifecycle phase "(.+)"', command.stdout) + availableLifecycles = re.search('Available lifecycle phases are: (.+) -> \[Help 1\]', command.stdout) + if availableLifecycles and failedLifecycle: + selectedLifecycle = get_close_matches(failedLifecycle.group(1), availableLifecycles.group(1).split(", "), 3, 0.6) + return replace_command(command, failedLifecycle.group(1), selectedLifecycle) + else: + return [] From 8c02658a3278c02fc890792f59ba8ecace90d8b9 Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Wed, 26 Aug 2015 15:02:46 +0100 Subject: [PATCH 3/5] Removed debug statement --- tests/rules/test_mvn_unknown_lifecycle_phase.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/rules/test_mvn_unknown_lifecycle_phase.py b/tests/rules/test_mvn_unknown_lifecycle_phase.py index 47ff447..421325d 100644 --- a/tests/rules/test_mvn_unknown_lifecycle_phase.py +++ b/tests/rules/test_mvn_unknown_lifecycle_phase.py @@ -36,6 +36,5 @@ def test_not_match(command): (Command(script='mvn cle', stdout='[ERROR] Unknown lifecycle phase "cle". You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]'), ['mvn clean', 'mvn compile']), (Command(script='mvn claen package', stdout='[ERROR] Unknown lifecycle phase "claen". You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]'), ['mvn clean package'])]) def test_get_new_command(command, new_command): - print new_command assert get_new_command(command, None) == new_command From 9103c1ffd52315df62b2401cc07c702fae3b3eab Mon Sep 17 00:00:00 2001 From: nvbn Date: Thu, 27 Aug 2015 16:08:29 +0300 Subject: [PATCH 4/5] Add is_app/for_app helpers --- tests/test_utils.py | 25 ++++++++++++++++++++++++- thefuck/utils.py | 23 +++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index b66f4c8..5979b20 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -2,8 +2,9 @@ import pytest from mock import Mock from thefuck.utils import wrap_settings,\ memoize, get_closest, get_all_executables, replace_argument, \ - get_all_matched_commands + get_all_matched_commands, is_app, for_app from thefuck.types import Settings +from tests.utils import Command @pytest.mark.parametrize('override, old, new', [ @@ -93,3 +94,25 @@ def test_replace_argument(args, result): 'service-status', 'service-unbind'])]) def test_get_all_matched_commands(stderr, result): assert list(get_all_matched_commands(stderr)) == result + + +@pytest.mark.usefixtures('no_memoize') +@pytest.mark.parametrize('script, names, result', [ + ('git diff', ['git', 'hub'], True), + ('hub diff', ['git', 'hub'], True), + ('hg diff', ['git', 'hub'], False)]) +def test_is_app(script, names, result): + assert is_app(Command(script), *names) == result + + +@pytest.mark.usefixtures('no_memoize') +@pytest.mark.parametrize('script, names, result', [ + ('git diff', ['git', 'hub'], True), + ('hub diff', ['git', 'hub'], True), + ('hg diff', ['git', 'hub'], False)]) +def test_for_app(script, names, result): + @for_app(*names) + def match(command, settings): + return True + + assert match(Command(script), None) == result diff --git a/thefuck/utils.py b/thefuck/utils.py index 8dd777b..6091e7f 100644 --- a/thefuck/utils.py +++ b/thefuck/utils.py @@ -132,3 +132,26 @@ def replace_command(command, broken, matched): new_cmds = get_close_matches(broken, matched, cutoff=0.1) return [replace_argument(command.script, broken, new_cmd.strip()) for new_cmd in new_cmds] + + +@memoize +def is_app(command, *app_names): + """Returns `True` if command is call to one of passed app names.""" + for name in app_names: + if command.script.startswith(u'{} '.format(name)): + return True + return False + + +def for_app(*app_names): + """Specifies that matching script is for on of app names.""" + def decorator(fn): + @wraps(fn) + def wrapper(command, settings): + if is_app(command, *app_names): + return fn(command, settings) + else: + return False + + return wrapper + return decorator From f2a7364e8caa8cd361fb59211aba4e928f73a3ff Mon Sep 17 00:00:00 2001 From: nvbn Date: Thu, 27 Aug 2015 16:10:50 +0300 Subject: [PATCH 5/5] #351 Speed-up mvn rules, pep8 fixes --- thefuck/rules/mvn_no_command.py | 9 ++++-- thefuck/rules/mvn_unknown_lifecycle_phase.py | 32 ++++++++++++++------ thefuck/utils.py | 3 +- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/thefuck/rules/mvn_no_command.py b/thefuck/rules/mvn_no_command.py index 53c7106..7113c57 100644 --- a/thefuck/rules/mvn_no_command.py +++ b/thefuck/rules/mvn_no_command.py @@ -1,8 +1,11 @@ -from thefuck import shells +from thefuck.utils import for_app + +@for_app('mvn') def match(command, settings): - return 'No goals have been specified for this build' in command.stdout and command.script.startswith('mvn') + return 'No goals have been specified for this build' in command.stdout def get_new_command(command, settings): - return [ command.script + ' clean package', command.script + ' clean install'] + return [command.script + ' clean package', + command.script + ' clean install'] diff --git a/thefuck/rules/mvn_unknown_lifecycle_phase.py b/thefuck/rules/mvn_unknown_lifecycle_phase.py index 1dcf1ad..c4a7ee1 100644 --- a/thefuck/rules/mvn_unknown_lifecycle_phase.py +++ b/thefuck/rules/mvn_unknown_lifecycle_phase.py @@ -1,20 +1,32 @@ -from thefuck import shells -from thefuck.utils import replace_command +from thefuck.utils import replace_command, for_app from difflib import get_close_matches import re +def _get_failed_lifecycle(command): + return re.search(r'\[ERROR\] Unknown lifecycle phase "(.+)"', + command.stdout) + + +def _getavailable_lifecycles(command): + return re.search( + r'Available lifecycle phases are: (.+) -> \[Help 1\]', command.stdout) + + +@for_app('mvn') def match(command, settings): - failedLifecycle = re.search('\[ERROR\] Unknown lifecycle phase "(.+)"', command.stdout) - availableLifecycles = re.search('Available lifecycle phases are: (.+) -> \[Help 1\]', command.stdout) - return availableLifecycles and failedLifecycle and command.script.startswith('mvn') + failed_lifecycle = _get_failed_lifecycle(command) + available_lifecycles = _getavailable_lifecycles(command) + return available_lifecycles and failed_lifecycle def get_new_command(command, settings): - failedLifecycle = re.search('\[ERROR\] Unknown lifecycle phase "(.+)"', command.stdout) - availableLifecycles = re.search('Available lifecycle phases are: (.+) -> \[Help 1\]', command.stdout) - if availableLifecycles and failedLifecycle: - selectedLifecycle = get_close_matches(failedLifecycle.group(1), availableLifecycles.group(1).split(", "), 3, 0.6) - return replace_command(command, failedLifecycle.group(1), selectedLifecycle) + failed_lifecycle = _get_failed_lifecycle(command) + available_lifecycles = _getavailable_lifecycles(command) + if available_lifecycles and failed_lifecycle: + selected_lifecycle = get_close_matches( + failed_lifecycle.group(1), available_lifecycles.group(1).split(", "), + 3, 0.6) + return replace_command(command, failed_lifecycle.group(1), selected_lifecycle) else: return [] diff --git a/thefuck/utils.py b/thefuck/utils.py index 6091e7f..2887397 100644 --- a/thefuck/utils.py +++ b/thefuck/utils.py @@ -138,7 +138,8 @@ def replace_command(command, broken, matched): def is_app(command, *app_names): """Returns `True` if command is call to one of passed app names.""" for name in app_names: - if command.script.startswith(u'{} '.format(name)): + if command.script == name \ + or command.script.startswith(u'{} '.format(name)): return True return False