diff --git a/tests/rules/test_cd_parent.py b/tests/rules/test_cd_parent.py index 7a0fbc8b..60f87609 100644 --- a/tests/rules/test_cd_parent.py +++ b/tests/rules/test_cd_parent.py @@ -1,4 +1,4 @@ -from thefuck.main import Command +from thefuck.types import Command from thefuck.rules.cd_parent import match, get_new_command diff --git a/tests/rules/test_git_not_command.py b/tests/rules/test_git_not_command.py index 657953fc..91df959e 100644 --- a/tests/rules/test_git_not_command.py +++ b/tests/rules/test_git_not_command.py @@ -1,5 +1,5 @@ import pytest -from thefuck.main import Command +from thefuck.types import Command from thefuck.rules.git_not_command import match, get_new_command diff --git a/tests/rules/test_git_push.py b/tests/rules/test_git_push.py index 7504a72a..57f63591 100644 --- a/tests/rules/test_git_push.py +++ b/tests/rules/test_git_push.py @@ -1,5 +1,5 @@ import pytest -from thefuck.main import Command +from thefuck.types import Command from thefuck.rules.git_push import match, get_new_command diff --git a/tests/rules/test_has_exists_script.py b/tests/rules/test_has_exists_script.py index 36938be2..e00c4bdd 100644 --- a/tests/rules/test_has_exists_script.py +++ b/tests/rules/test_has_exists_script.py @@ -1,5 +1,5 @@ from mock import Mock, patch -from thefuck.rules. has_exists_script import match, get_new_command +from thefuck.rules.has_exists_script import match, get_new_command def test_match(): diff --git a/tests/rules/test_mkdir_p.py b/tests/rules/test_mkdir_p.py index 128be2f3..dae196fa 100644 --- a/tests/rules/test_mkdir_p.py +++ b/tests/rules/test_mkdir_p.py @@ -1,4 +1,4 @@ -from thefuck.main import Command +from thefuck.types import Command from thefuck.rules.mkdir_p import match, get_new_command diff --git a/tests/rules/test_python_command.py b/tests/rules/test_python_command.py index e8071263..56d9b3f9 100644 --- a/tests/rules/test_python_command.py +++ b/tests/rules/test_python_command.py @@ -1,9 +1,11 @@ -from thefuck.main import Command +from thefuck.types import Command from thefuck.rules.python_command import match, get_new_command + def test_match(): assert match(Command('temp.py', '', 'Permission denied'), None) assert not match(Command('', '', ''), None) + def test_get_new_command(): assert get_new_command(Command('./test_sudo.py', '', ''), None) == 'python ./test_sudo.py' diff --git a/tests/rules/test_rm_dir.py b/tests/rules/test_rm_dir.py index 2362d0c3..2d5ed623 100644 --- a/tests/rules/test_rm_dir.py +++ b/tests/rules/test_rm_dir.py @@ -1,4 +1,4 @@ -from thefuck.main import Command +from thefuck.types import Command from thefuck.rules.rm_dir import match, get_new_command diff --git a/tests/rules/test_ssh_known_host.py b/tests/rules/test_ssh_known_host.py index 9c8dc0d1..252cac18 100644 --- a/tests/rules/test_ssh_known_host.py +++ b/tests/rules/test_ssh_known_host.py @@ -1,7 +1,7 @@ import os import pytest from mock import Mock -from thefuck.main import Command +from thefuck.types import Command from thefuck.rules.ssh_known_hosts import match, get_new_command, remove_offending_keys diff --git a/tests/rules/test_sudo.py b/tests/rules/test_sudo.py index 4f0c8318..4f3b3f50 100644 --- a/tests/rules/test_sudo.py +++ b/tests/rules/test_sudo.py @@ -1,4 +1,4 @@ -from thefuck.main import Command +from thefuck.types import Command from thefuck.rules.sudo import match, get_new_command diff --git a/tests/test_conf.py b/tests/test_conf.py index f25e9f5e..b8e6d6c3 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -1,15 +1,8 @@ from mock import patch, Mock -from thefuck.main import Rule +from thefuck.types import Rule from thefuck import conf -def test_rules_names_list(): - assert conf.RulesNamesList(['bash', 'lisp']) == ['bash', 'lisp'] - assert conf.RulesNamesList(['bash', 'lisp']) == conf.RulesNamesList(['bash', 'lisp']) - assert Rule('lisp', None, None, False) in conf.RulesNamesList(['lisp']) - assert Rule('bash', None, None, False) not in conf.RulesNamesList(['lisp']) - - def test_default(): assert Rule('test', None, None, True) in conf.DEFAULT_RULES assert Rule('test', None, None, False) not in conf.DEFAULT_RULES @@ -66,10 +59,3 @@ def test_settings_from_env_with_DEFAULT(): patch('thefuck.conf.os.environ', new_callable=lambda: {'THEFUCK_RULES': 'DEFAULT_RULES:bash:lisp'}): settings = conf.get_settings(Mock()) assert settings.rules == conf.DEFAULT_RULES + ['bash', 'lisp'] - - -def test_update_settings(): - settings = conf.Settings({'key': 'val'}) - new_settings = settings.update(key='new-val') - assert new_settings.key == 'new-val' - assert settings.key == 'val' diff --git a/tests/test_main.py b/tests/test_main.py index 19e460cd..078f1a8e 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,7 +1,7 @@ from subprocess import PIPE from pathlib import PosixPath, Path from mock import patch, Mock -from thefuck import main, conf +from thefuck import main, conf, types def test_load_rule(): @@ -13,7 +13,7 @@ def test_load_rule(): get_new_command=get_new_command, enabled_by_default=True)) as load_source: assert main.load_rule(Path('/rules/bash.py')) \ - == main.Rule('bash', match, get_new_command, True) + == types.Rule('bash', match, get_new_command, True) load_source.assert_called_once_with('bash', '/rules/bash.py') @@ -26,15 +26,15 @@ def test_get_rules(): assert list(main.get_rules( Path('~'), Mock(rules=conf.DEFAULT_RULES))) \ - == [main.Rule('bash', 'bash', 'bash', True), - main.Rule('lisp', 'lisp', 'lisp', True), - main.Rule('bash', 'bash', 'bash', True), - main.Rule('lisp', 'lisp', 'lisp', True)] + == [types.Rule('bash', 'bash', 'bash', True), + types.Rule('lisp', 'lisp', 'lisp', True), + types.Rule('bash', 'bash', 'bash', True), + types.Rule('lisp', 'lisp', 'lisp', True)] assert list(main.get_rules( Path('~'), - Mock(rules=conf.RulesNamesList(['bash'])))) \ - == [main.Rule('bash', 'bash', 'bash', True), - main.Rule('bash', 'bash', 'bash', True)] + Mock(rules=types.RulesNamesList(['bash'])))) \ + == [types.Rule('bash', 'bash', 'bash', True), + types.Rule('bash', 'bash', 'bash', True)] def test_get_command(): @@ -47,7 +47,7 @@ def test_get_command(): Popen.return_value.stderr.read.return_value = b'stderr' assert main.get_command(Mock(), ['thefuck', 'apt-get', 'search', 'vim']) \ - == main.Command('apt-get search vim', 'stdout', 'stderr') + == types.Command('apt-get search vim', 'stdout', 'stderr') Popen.assert_called_once_with('apt-get search vim', shell=True, stdout=PIPE, @@ -57,12 +57,12 @@ def test_get_command(): def test_get_matched_rule(capsys): - rules = [main.Rule('', lambda x, _: x.script == 'cd ..', None, True), - main.Rule('', lambda *_: False, None, True), - main.Rule('rule', Mock(side_effect=OSError('Denied')), None, True)] - assert main.get_matched_rule(main.Command('ls', '', ''), + rules = [types.Rule('', lambda x, _: x.script == 'cd ..', None, True), + types.Rule('', lambda *_: False, None, True), + types.Rule('rule', Mock(side_effect=OSError('Denied')), None, True)] + assert main.get_matched_rule(types.Command('ls', '', ''), rules, Mock(no_colors=True)) is None - assert main.get_matched_rule(main.Command('cd ..', '', ''), + assert main.get_matched_rule(types.Command('cd ..', '', ''), rules, Mock(no_colors=True)) == rules[0] assert capsys.readouterr()[1].split('\n')[0] \ == '[WARN] Rule rule:' @@ -70,11 +70,11 @@ def test_get_matched_rule(capsys): def test_run_rule(capsys): with patch('thefuck.main.confirm', return_value=True): - main.run_rule(main.Rule('', None, lambda *_: 'new-command', True), + main.run_rule(types.Rule('', None, lambda *_: 'new-command', True), None, None) assert capsys.readouterr() == ('new-command\n', '') with patch('thefuck.main.confirm', return_value=False): - main.run_rule(main.Rule('', None, lambda *_: 'new-command', True), + main.run_rule(types.Rule('', None, lambda *_: 'new-command', True), None, None) assert capsys.readouterr() == ('', '') diff --git a/tests/test_types.py b/tests/test_types.py new file mode 100644 index 00000000..0df4a161 --- /dev/null +++ b/tests/test_types.py @@ -0,0 +1,15 @@ +from thefuck.types import Rule, RulesNamesList, Settings + + +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']) + + +def test_update_settings(): + settings = Settings({'key': 'val'}) + new_settings = settings.update(key='new-val') + assert new_settings.key == 'new-val' + assert settings.key == 'val' diff --git a/tests/test_utils.py b/tests/test_utils.py index 43439048..995614d1 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,11 +1,10 @@ from mock import Mock from thefuck.utils import sudo_support, wrap_settings -from thefuck.main import Command -from thefuck.conf import Settings +from thefuck.types import Command, Settings def test_wrap_settings(): - fn = lambda _, settings: settings._conf + fn = lambda _, settings: settings assert wrap_settings({'key': 'val'})(fn)(None, Settings({})) \ == {'key': 'val'} assert wrap_settings({'key': 'new-val'})(fn)( diff --git a/thefuck/conf.py b/thefuck/conf.py index 2da0f5c6..0c6f29a7 100644 --- a/thefuck/conf.py +++ b/thefuck/conf.py @@ -3,17 +3,10 @@ from imp import load_source import os import sys from six import text_type -from . import logs +from . import logs, types -class RulesNamesList(list): - """Wrapper a top of list for string rules names.""" - - def __contains__(self, item): - return super(RulesNamesList, self).__contains__(item.name) - - -class _DefaultRulesNames(RulesNamesList): +class _DefaultRulesNames(types.RulesNamesList): def __add__(self, items): return _DefaultRulesNames(list(self) + items) @@ -31,20 +24,6 @@ class _DefaultRulesNames(RulesNamesList): DEFAULT_RULES = _DefaultRulesNames([]) -class Settings(object): - def __init__(self, conf): - self._conf = conf - - def __getattr__(self, item): - return self._conf.get(item) - - def update(self, **kwargs): - """Returns new settings with new values from `kwargs`.""" - conf = copy(self._conf) - conf.update(kwargs) - return Settings(conf) - - DEFAULT_SETTINGS = {'rules': DEFAULT_RULES, 'wait_command': 3, 'require_confirmation': False, @@ -100,16 +79,16 @@ def get_settings(user_dir): except Exception: logs.exception("Can't load settings from file", sys.exc_info(), - Settings(conf)) + types.Settings(conf)) try: conf.update(_settings_from_env()) except Exception: logs.exception("Can't load settings from env", sys.exc_info(), - Settings(conf)) + types.Settings(conf)) - if not isinstance(conf['rules'], RulesNamesList): - conf['rules'] = RulesNamesList(conf['rules']) + if not isinstance(conf['rules'], types.RulesNamesList): + conf['rules'] = types.RulesNamesList(conf['rules']) - return Settings(conf) + return types.Settings(conf) diff --git a/thefuck/main.py b/thefuck/main.py index 341c9621..ea286eb0 100644 --- a/thefuck/main.py +++ b/thefuck/main.py @@ -1,4 +1,3 @@ -from collections import namedtuple from imp import load_source from pathlib import Path from os.path import expanduser @@ -7,12 +6,7 @@ import os import sys from psutil import Process, TimeoutExpired import colorama -from . import logs, conf - - -Command = namedtuple('Command', ('script', 'stdout', 'stderr')) -Rule = namedtuple('Rule', ('name', 'match', 'get_new_command', - 'enabled_by_default')) +from . import logs, conf, types def setup_user_dir(): @@ -28,16 +22,16 @@ def setup_user_dir(): def load_rule(rule): """Imports rule module and returns it.""" rule_module = load_source(rule.name[:-3], str(rule)) - return Rule(rule.name[:-3], rule_module.match, - rule_module.get_new_command, - getattr(rule_module, 'enabled_by_default', True)) + return types.Rule(rule.name[:-3], rule_module.match, + rule_module.get_new_command, + getattr(rule_module, 'enabled_by_default', True)) def get_rules(user_dir, settings): """Returns all enabled rules.""" - bundled = Path(__file__).parent\ - .joinpath('rules')\ - .glob('*.py') + bundled = Path(__file__).parent \ + .joinpath('rules') \ + .glob('*.py') user = user_dir.joinpath('rules').glob('*.py') for rule in sorted(list(bundled)) + list(user): if rule.name != '__init__.py': @@ -77,8 +71,8 @@ def get_command(settings, args): result = Popen(script, shell=True, stdout=PIPE, stderr=PIPE, env=dict(os.environ, LANG='C')) if wait_output(settings, result): - return Command(script, result.stdout.read().decode('utf-8'), - result.stderr.read().decode('utf-8')) + return types.Command(script, result.stdout.read().decode('utf-8'), + result.stderr.read().decode('utf-8')) def get_matched_rule(command, rules, settings): diff --git a/thefuck/types.py b/thefuck/types.py new file mode 100644 index 00000000..b426788d --- /dev/null +++ b/thefuck/types.py @@ -0,0 +1,26 @@ +from collections import namedtuple + + +Command = namedtuple('Command', ('script', 'stdout', 'stderr')) + +Rule = namedtuple('Rule', ('name', 'match', 'get_new_command', + 'enabled_by_default')) + + +class RulesNamesList(list): + """Wrapper a top of list for string rules names.""" + + def __contains__(self, item): + return super(RulesNamesList, self).__contains__(item.name) + + +class Settings(dict): + + def __getattr__(self, item): + return self.get(item) + + def update(self, **kwargs): + """Returns new settings with new values from `kwargs`.""" + conf = dict(self) + conf.update(kwargs) + return Settings(conf) diff --git a/thefuck/utils.py b/thefuck/utils.py index 4c87970f..7ee66c20 100644 --- a/thefuck/utils.py +++ b/thefuck/utils.py @@ -1,7 +1,7 @@ from functools import wraps import os import six -from thefuck.main import Command +from .types import Command def which(program):