From b985dfbffc51e21078c1fceb96abfcd71ea22d0e Mon Sep 17 00:00:00 2001 From: nvbn Date: Fri, 1 May 2015 04:39:37 +0200 Subject: [PATCH] #102 Add support of rules with side effects --- tests/test_main.py | 29 ++++++++++++++++++++++++----- tests/test_types.py | 7 ++++--- tests/utils.py | 6 ++++-- thefuck/logs.py | 11 +++++++---- thefuck/main.py | 13 ++++++++----- thefuck/types.py | 4 ++-- 6 files changed, 49 insertions(+), 21 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index cc5bf79a..776f5c99 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -74,6 +74,15 @@ def test_run_rule(capsys): main.run_rule(Rule(get_new_command=lambda *_: 'new-command'), None, None) assert capsys.readouterr() == ('new-command\n', '') + # With side effect: + side_effect = Mock() + settings = Mock() + command = Mock() + main.run_rule(Rule(get_new_command=lambda *_: 'new-command', + side_effect=side_effect), + command, settings) + assert capsys.readouterr() == ('new-command\n', '') + side_effect.assert_called_once_with(command, settings) with patch('thefuck.main.confirm', return_value=False): main.run_rule(Rule(get_new_command=lambda *_: 'new-command'), None, None) @@ -82,15 +91,25 @@ def test_run_rule(capsys): def test_confirm(capsys): # When confirmation not required: - assert main.confirm('command', Mock(require_confirmation=False)) + assert main.confirm('command', None, Mock(require_confirmation=False)) assert capsys.readouterr() == ('', 'command\n') + # With side effect and without confirmation: + assert main.confirm('command', Mock(), Mock(require_confirmation=False)) + assert capsys.readouterr() == ('', 'command*\n') # When confirmation required and confirmed: with patch('thefuck.main.sys.stdin.read', return_value='\n'): - assert main.confirm('command', Mock(require_confirmation=True, - no_colors=True)) + assert main.confirm( + 'command', None, Mock(require_confirmation=True, + no_colors=True)) assert capsys.readouterr() == ('', 'command [enter/ctrl+c]') + # With side effect: + assert main.confirm( + 'command', Mock(), Mock(require_confirmation=True, + no_colors=True)) + assert capsys.readouterr() == ('', 'command* [enter/ctrl+c]') # When confirmation required and ctrl+c: with patch('thefuck.main.sys.stdin.read', side_effect=KeyboardInterrupt): - assert not main.confirm('command', Mock(require_confirmation=True, - no_colors=True)) + assert not main.confirm('command', None, + Mock(require_confirmation=True, + no_colors=True)) assert capsys.readouterr() == ('', 'command [enter/ctrl+c]Aborted\n') diff --git a/tests/test_types.py b/tests/test_types.py index 0df4a161..9c587ea1 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -1,11 +1,12 @@ -from thefuck.types import Rule, RulesNamesList, Settings +from thefuck.types import RulesNamesList, Settings +from tests.utils import Rule def test_rules_names_list(): assert RulesNamesList(['bash', 'lisp']) == ['bash', 'lisp'] assert RulesNamesList(['bash', 'lisp']) == RulesNamesList(['bash', 'lisp']) - assert Rule('lisp', None, None, False) in RulesNamesList(['lisp']) - assert Rule('bash', None, None, False) not in RulesNamesList(['lisp']) + assert Rule('lisp') in RulesNamesList(['lisp']) + assert Rule('bash') not in RulesNamesList(['lisp']) def test_update_settings(): diff --git a/tests/utils.py b/tests/utils.py index 5191d2c8..02e19e7b 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -7,5 +7,7 @@ def Command(script='', stdout='', stderr=''): def Rule(name='', match=lambda *_: True, get_new_command=lambda *_: '', - enabled_by_default=True): - return types.Rule(name, match, get_new_command, enabled_by_default) + enabled_by_default=True, + side_effect=None): + return types.Rule(name, match, get_new_command, + enabled_by_default, side_effect) diff --git a/thefuck/logs.py b/thefuck/logs.py index b21b2451..3d765f69 100644 --- a/thefuck/logs.py +++ b/thefuck/logs.py @@ -26,17 +26,20 @@ def rule_failed(rule, exc_info, settings): exception('Rule {}'.format(rule.name), exc_info, settings) -def show_command(new_command, settings): - sys.stderr.write('{bold}{command}{reset}\n'.format( +def show_command(new_command, side_effect, settings): + sys.stderr.write('{bold}{command}{side_effect}{reset}\n'.format( command=new_command, + side_effect='*' if side_effect else '', bold=color(colorama.Style.BRIGHT, settings), reset=color(colorama.Style.RESET_ALL, settings))) -def confirm_command(new_command, settings): +def confirm_command(new_command, side_effect, settings): sys.stderr.write( - '{bold}{command}{reset} [{green}enter{reset}/{red}ctrl+c{reset}]'.format( + '{bold}{command}{side_effect}{reset} ' + '[{green}enter{reset}/{red}ctrl+c{reset}]'.format( command=new_command, + side_effect='*' if side_effect else '', bold=color(colorama.Style.BRIGHT, settings), green=color(colorama.Fore.GREEN, settings), red=color(colorama.Fore.RED, settings), diff --git a/thefuck/main.py b/thefuck/main.py index 524fe0dd..235784b8 100644 --- a/thefuck/main.py +++ b/thefuck/main.py @@ -24,7 +24,8 @@ def load_rule(rule): rule_module = load_source(rule.name[:-3], str(rule)) return types.Rule(rule.name[:-3], rule_module.match, rule_module.get_new_command, - getattr(rule_module, 'enabled_by_default', True)) + getattr(rule_module, 'enabled_by_default', True), + getattr(rule_module, 'side_effect', None)) def get_rules(user_dir, settings): @@ -85,13 +86,13 @@ def get_matched_rule(command, rules, settings): logs.rule_failed(rule, sys.exc_info(), settings) -def confirm(new_command, settings): +def confirm(new_command, side_effect, settings): """Returns `True` when running of new command confirmed.""" if not settings.require_confirmation: - logs.show_command(new_command, settings) + logs.show_command(new_command, side_effect, settings) return True - logs.confirm_command(new_command, settings) + logs.confirm_command(new_command, side_effect, settings) try: sys.stdin.read(1) return True @@ -103,7 +104,9 @@ def confirm(new_command, settings): def run_rule(rule, command, settings): """Runs command from rule for passed command.""" new_command = rule.get_new_command(command, settings) - if confirm(new_command, settings): + if confirm(new_command, rule.side_effect, settings): + if rule.side_effect: + rule.side_effect(command, settings) print(new_command) diff --git a/thefuck/types.py b/thefuck/types.py index b426788d..221b0e9e 100644 --- a/thefuck/types.py +++ b/thefuck/types.py @@ -4,11 +4,11 @@ from collections import namedtuple Command = namedtuple('Command', ('script', 'stdout', 'stderr')) Rule = namedtuple('Rule', ('name', 'match', 'get_new_command', - 'enabled_by_default')) + 'enabled_by_default', 'side_effect')) class RulesNamesList(list): - """Wrapper a top of list for string rules names.""" + """Wrapper a top of list for storing rules names.""" def __contains__(self, item): return super(RulesNamesList, self).__contains__(item.name)