diff --git a/README.md b/README.md index 5181adec..593ca5f6 100644 --- a/README.md +++ b/README.md @@ -271,6 +271,7 @@ requires_output = True The Fuck has a few settings parameters which can be changed in `~/.thefuck/settings.py`: * `rules` – list of enabled rules, by default `thefuck.conf.DEFAULT_RULES`; +* `exclude_rules` – list of disabled rules, by default `[]`; * `require_confirmation` – requires confirmation before running new command, by default `True`; * `wait_command` – max amount of time in seconds for getting previous command output; * `no_colors` – disable colored output; @@ -281,6 +282,7 @@ Example of `settings.py`: ```python rules = ['sudo', 'no_command'] +exclude_rules = ['git_push'] require_confirmation = True wait_command = 10 no_colors = False @@ -291,6 +293,7 @@ debug = False Or via environment variables: * `THEFUCK_RULES` – list of enabled rules, like `DEFAULT_RULES:rm_root` or `sudo:no_command`; +* `THEFUCK_EXCLUDE_RULES` – list of disabled rules, like `git_pull:git_push`; * `THEFUCK_REQUIRE_CONFIRMATION` – require confirmation before running new command, `true/false`; * `THEFUCK_WAIT_COMMAND` – max amount of time in seconds for getting previous command output; * `THEFUCK_NO_COLORS` – disable colored output, `true/false`; @@ -302,6 +305,7 @@ For example: ```bash export THEFUCK_RULES='sudo:no_command' +export THEFUCK_EXCLUDE_RULES='git_pull:git_push' export THEFUCK_REQUIRE_CONFIRMATION='true' export THEFUCK_WAIT_COMMAND=10 export THEFUCK_NO_COLORS='false' diff --git a/tests/test_conf.py b/tests/test_conf.py index 1fde6a67..2137f7c7 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -39,17 +39,20 @@ class TestSettingsFromFile(object): wait_command=10, require_confirmation=True, no_colors=True, - priority={'vim': 100}) + priority={'vim': 100}, + exclude_rules=['git']) settings = conf.get_settings(Mock()) assert settings.rules == ['test'] assert settings.wait_command == 10 assert settings.require_confirmation is True assert settings.no_colors is True assert settings.priority == {'vim': 100} + assert settings.exclude_rules == ['git'] def test_from_file_with_DEFAULT(self, load_source): load_source.return_value = Mock(rules=conf.DEFAULT_RULES + ['test'], wait_command=10, + exclude_rules=[], require_confirmation=True, no_colors=True) settings = conf.get_settings(Mock()) @@ -60,12 +63,14 @@ class TestSettingsFromFile(object): class TestSettingsFromEnv(object): def test_from_env(self, environ): environ.update({'THEFUCK_RULES': 'bash:lisp', + 'THEFUCK_EXCLUDE_RULES': 'git:vim', 'THEFUCK_WAIT_COMMAND': '55', 'THEFUCK_REQUIRE_CONFIRMATION': 'true', 'THEFUCK_NO_COLORS': 'false', 'THEFUCK_PRIORITY': 'bash=10:lisp=wrong:vim=15'}) settings = conf.get_settings(Mock()) assert settings.rules == ['bash', 'lisp'] + assert settings.exclude_rules == ['git', 'vim'] assert settings.wait_command == 55 assert settings.require_confirmation is True assert settings.no_colors is False diff --git a/tests/test_corrector.py b/tests/test_corrector.py index 2c58a017..1071ae47 100644 --- a/tests/test_corrector.py +++ b/tests/test_corrector.py @@ -22,23 +22,39 @@ def test_load_rule(mocker): class TestGetRules(object): - @pytest.fixture(autouse=True) + @pytest.fixture def glob(self, mocker): - return mocker.patch('thefuck.corrector.Path.glob', return_value=[]) + results = {} + mocker.patch('thefuck.corrector.Path.glob', + new_callable=lambda: lambda *_: results.pop('value', [])) + return lambda value: results.update({'value': value}) - def _compare_names(self, rules, names): - return [r.name for r in rules] == names - - @pytest.mark.parametrize('conf_rules, rules', [ - (conf.DEFAULT_RULES, ['bash', 'lisp', 'bash', 'lisp']), - (types.RulesNamesList(['bash']), ['bash', 'bash'])]) - def test_get(self, monkeypatch, glob, conf_rules, rules): - glob.return_value = [PosixPath('bash.py'), PosixPath('lisp.py')] + @pytest.fixture(autouse=True) + def load_source(self, monkeypatch): monkeypatch.setattr('thefuck.corrector.load_source', lambda x, _: Rule(x)) - assert self._compare_names( - corrector.get_rules(Path('~'), Mock(rules=conf_rules, priority={})), - rules) + + def _compare_names(self, rules, names): + assert {r.name for r in rules} == set(names) + + def _prepare_rules(self, rules): + if rules == conf.DEFAULT_RULES: + return rules + else: + return types.RulesNamesList(rules) + + @pytest.mark.parametrize('paths, conf_rules, exclude_rules, loaded_rules', [ + (['git.py', 'bash.py'], conf.DEFAULT_RULES, [], ['git', 'bash']), + (['git.py', 'bash.py'], ['git'], [], ['git']), + (['git.py', 'bash.py'], conf.DEFAULT_RULES, ['git'], ['bash']), + (['git.py', 'bash.py'], ['git'], ['git'], [])]) + def test_get_rules(self, glob, paths, conf_rules, exclude_rules, loaded_rules): + glob([PosixPath(path) for path in paths]) + settings = Mock(rules=self._prepare_rules(conf_rules), + priority={}, + exclude_rules=self._prepare_rules(exclude_rules)) + rules = corrector.get_rules(Path('~'), settings) + self._compare_names(rules, loaded_rules) class TestIsRuleMatch(object): diff --git a/thefuck/conf.py b/thefuck/conf.py index 2efb6897..e107329a 100644 --- a/thefuck/conf.py +++ b/thefuck/conf.py @@ -26,6 +26,7 @@ DEFAULT_PRIORITY = 1000 DEFAULT_SETTINGS = {'rules': DEFAULT_RULES, + 'exclude_rules': [], 'wait_command': 3, 'require_confirmation': True, 'no_colors': False, @@ -34,6 +35,7 @@ DEFAULT_SETTINGS = {'rules': DEFAULT_RULES, 'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'}} ENV_TO_ATTR = {'THEFUCK_RULES': 'rules', + 'THEFUCK_EXCLUDE_RULES': 'exclude_rules', 'THEFUCK_WAIT_COMMAND': 'wait_command', 'THEFUCK_REQUIRE_CONFIRMATION': 'require_confirmation', 'THEFUCK_NO_COLORS': 'no_colors', @@ -84,7 +86,7 @@ def _priority_from_env(val): def _val_from_env(env, attr): """Transforms env-strings to python.""" val = os.environ[env] - if attr == 'rules': + if attr in ('rules', 'exclude_rules'): return _rules_from_env(val) elif attr == 'priority': return dict(_priority_from_env(val)) @@ -123,6 +125,9 @@ def get_settings(user_dir): if not isinstance(conf['rules'], types.RulesNamesList): conf['rules'] = types.RulesNamesList(conf['rules']) + if not isinstance(conf['exclude_rules'], types.RulesNamesList): + conf['exclude_rules'] = types.RulesNamesList(conf['exclude_rules']) + return types.Settings(conf) diff --git a/thefuck/corrector.py b/thefuck/corrector.py index 1126817e..de442cd2 100644 --- a/thefuck/corrector.py +++ b/thefuck/corrector.py @@ -23,7 +23,8 @@ def get_loaded_rules(rules, settings): for rule in rules: if rule.name != '__init__.py': loaded_rule = load_rule(rule, settings) - if loaded_rule in settings.rules: + if loaded_rule in settings.rules and \ + loaded_rule not in settings.exclude_rules: yield loaded_rule