mirror of
				https://github.com/nvbn/thefuck.git
				synced 2025-11-03 00:22:10 +00:00 
			
		
		
		
	Compare commits
	
		
			14 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					29e70e14a0 | ||
| 
						 | 
					0cdd23edcf | ||
| 
						 | 
					36d80859a4 | ||
| 
						 | 
					2b12b4bfce | ||
| 
						 | 
					91c1fe414a | ||
| 
						 | 
					f3d377114e | ||
| 
						 | 
					05f594b918 | ||
| 
						 | 
					5bf1424613 | ||
| 
						 | 
					fc3fcf028a | ||
| 
						 | 
					5864faadef | ||
| 
						 | 
					608d48e408 | ||
| 
						 | 
					9380eb1f56 | ||
| 
						 | 
					fb069b74d7 | ||
| 
						 | 
					6624ecb3b8 | 
							
								
								
									
										13
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								README.md
									
									
									
									
									
								
							@@ -145,12 +145,14 @@ using matched rule and run it. Rules enabled by default:
 | 
				
			|||||||
* `cd_mkdir` – creates directories before cd'ing into them;
 | 
					* `cd_mkdir` – creates directories before cd'ing into them;
 | 
				
			||||||
* `cp_omitting_directory` – adds `-a` when you `cp` directory;
 | 
					* `cp_omitting_directory` – adds `-a` when you `cp` directory;
 | 
				
			||||||
* `fix_alt_space` – replaces Alt+Space with Space character;
 | 
					* `fix_alt_space` – replaces Alt+Space with Space character;
 | 
				
			||||||
 | 
					* `git_add` – fix *"Did you forget to 'git add'?"*;
 | 
				
			||||||
* `git_no_command` – fixes wrong git commands like `git brnch`;
 | 
					* `git_no_command` – fixes wrong git commands like `git brnch`;
 | 
				
			||||||
* `git_push` – adds `--set-upstream origin $branch` to previous failed `git push`;
 | 
					* `git_push` – adds `--set-upstream origin $branch` to previous failed `git push`;
 | 
				
			||||||
* `has_exists_script` – prepends `./` when script/binary exists;
 | 
					* `has_exists_script` – prepends `./` when script/binary exists;
 | 
				
			||||||
* `lein_not_task` – fixes wrong `lein` tasks like `lein rpl`;
 | 
					* `lein_not_task` – fixes wrong `lein` tasks like `lein rpl`;
 | 
				
			||||||
* `mkdir_p` – adds `-p` when you trying to create directory without parent;
 | 
					* `mkdir_p` – adds `-p` when you trying to create directory without parent;
 | 
				
			||||||
* `no_command` – fixes wrong console commands, for example `vom/vim`;
 | 
					* `no_command` – fixes wrong console commands, for example `vom/vim`;
 | 
				
			||||||
 | 
					* `pacman` – installs app with `pacman` or `yaourt` if it is not installed;
 | 
				
			||||||
* `pip_unknown_command` – fixes wrong pip commands, for example `pip instatl/pip install`;
 | 
					* `pip_unknown_command` – fixes wrong pip commands, for example `pip instatl/pip install`;
 | 
				
			||||||
* `python_command` – prepends `python` when you trying to run not executable/without `./` python script;
 | 
					* `python_command` – prepends `python` when you trying to run not executable/without `./` python script;
 | 
				
			||||||
* `sl_ls` – changes `sl` to `ls`;
 | 
					* `sl_ls` – changes `sl` to `ls`;
 | 
				
			||||||
@@ -197,6 +199,8 @@ enabled_by_default = True
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def side_effect(command, settings):
 | 
					def side_effect(command, settings):
 | 
				
			||||||
    subprocess.call('chmod 777 .', shell=True)
 | 
					    subprocess.call('chmod 777 .', shell=True)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					priority = 1000  # Lower first
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[More examples of rules](https://github.com/nvbn/thefuck/tree/master/thefuck/rules),
 | 
					[More examples of rules](https://github.com/nvbn/thefuck/tree/master/thefuck/rules),
 | 
				
			||||||
@@ -209,7 +213,8 @@ The Fuck has a few settings parameters, they can be changed in `~/.thefuck/setti
 | 
				
			|||||||
* `rules` – list of enabled rules, by default `thefuck.conf.DEFAULT_RULES`;
 | 
					* `rules` – list of enabled rules, by default `thefuck.conf.DEFAULT_RULES`;
 | 
				
			||||||
* `require_confirmation` – require confirmation before running new command, by default `False`;
 | 
					* `require_confirmation` – require confirmation before running new command, by default `False`;
 | 
				
			||||||
* `wait_command` – max amount of time in seconds for getting previous command output;
 | 
					* `wait_command` – max amount of time in seconds for getting previous command output;
 | 
				
			||||||
* `no_colors` – disable colored output.
 | 
					* `no_colors` – disable colored output;
 | 
				
			||||||
 | 
					* `priority` – dict with rules priorities, rule with lower `priority` will be matched first.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Example of `settings.py`:
 | 
					Example of `settings.py`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -218,6 +223,7 @@ rules = ['sudo', 'no_command']
 | 
				
			|||||||
require_confirmation = True
 | 
					require_confirmation = True
 | 
				
			||||||
wait_command = 10
 | 
					wait_command = 10
 | 
				
			||||||
no_colors = False
 | 
					no_colors = False
 | 
				
			||||||
 | 
					priority = {'sudo': 100, 'no_command': 9999}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Or via environment variables:
 | 
					Or via environment variables:
 | 
				
			||||||
@@ -225,7 +231,9 @@ Or via environment variables:
 | 
				
			|||||||
* `THEFUCK_RULES` – list of enabled rules, like `DEFAULT_RULES:rm_root` or `sudo:no_command`;
 | 
					* `THEFUCK_RULES` – list of enabled rules, like `DEFAULT_RULES:rm_root` or `sudo:no_command`;
 | 
				
			||||||
* `THEFUCK_REQUIRE_CONFIRMATION` – require confirmation before running new command, `true/false`;
 | 
					* `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_WAIT_COMMAND` – max amount of time in seconds for getting previous command output;
 | 
				
			||||||
* `THEFUCK_NO_COLORS` – disable colored output, `true/false`.
 | 
					* `THEFUCK_NO_COLORS` – disable colored output, `true/false`;
 | 
				
			||||||
 | 
					* `THEFUCK_PRIORITY` – priority of the rules, like `no_command=9999:apt_get=100`,
 | 
				
			||||||
 | 
					rule with lower `priority` will be matched first.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
For example:
 | 
					For example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -234,6 +242,7 @@ export THEFUCK_RULES='sudo:no_command'
 | 
				
			|||||||
export THEFUCK_REQUIRE_CONFIRMATION='true'
 | 
					export THEFUCK_REQUIRE_CONFIRMATION='true'
 | 
				
			||||||
export THEFUCK_WAIT_COMMAND=10
 | 
					export THEFUCK_WAIT_COMMAND=10
 | 
				
			||||||
export THEFUCK_NO_COLORS='false'
 | 
					export THEFUCK_NO_COLORS='false'
 | 
				
			||||||
 | 
					export THEFUCK_PRIORITY='no_command=9999:apt_get=100'
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Developing
 | 
					## Developing
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,4 +28,4 @@ call('git commit -am "Bump to {}"'.format(version), shell=True)
 | 
				
			|||||||
call('git tag {}'.format(version), shell=True)
 | 
					call('git tag {}'.format(version), shell=True)
 | 
				
			||||||
call('git push', shell=True)
 | 
					call('git push', shell=True)
 | 
				
			||||||
call('git push --tags', shell=True)
 | 
					call('git push --tags', shell=True)
 | 
				
			||||||
call('python setup.py sdist upload', shell=True)
 | 
					call('python setup.py sdist bdist_wheel upload', shell=True)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,2 +1,4 @@
 | 
				
			|||||||
pytest
 | 
					pytest
 | 
				
			||||||
mock
 | 
					mock
 | 
				
			||||||
 | 
					pytest-mock
 | 
				
			||||||
 | 
					wheel
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								setup.py
									
									
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
				
			|||||||
from setuptools import setup, find_packages
 | 
					from setuptools import setup, find_packages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VERSION = '1.35'
 | 
					VERSION = '1.37'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
setup(name='thefuck',
 | 
					setup(name='thefuck',
 | 
				
			||||||
@@ -17,4 +17,5 @@ setup(name='thefuck',
 | 
				
			|||||||
      zip_safe=False,
 | 
					      zip_safe=False,
 | 
				
			||||||
      install_requires=['pathlib', 'psutil', 'colorama', 'six'],
 | 
					      install_requires=['pathlib', 'psutil', 'colorama', 'six'],
 | 
				
			||||||
      entry_points={'console_scripts': [
 | 
					      entry_points={'console_scripts': [
 | 
				
			||||||
          'thefuck = thefuck.main:main', 'thefuck-alias = thefuck.main:alias']})
 | 
					          'thefuck = thefuck.main:main',
 | 
				
			||||||
 | 
					          'thefuck-alias = thefuck.shells:app_alias']})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,10 +14,8 @@ def test_default(enabled, rules, result):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.fixture
 | 
					@pytest.fixture
 | 
				
			||||||
def load_source(monkeypatch):
 | 
					def load_source(mocker):
 | 
				
			||||||
    mock = Mock()
 | 
					    return mocker.patch('thefuck.conf.load_source')
 | 
				
			||||||
    monkeypatch.setattr('thefuck.conf.load_source', mock)
 | 
					 | 
				
			||||||
    return mock
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.fixture
 | 
					@pytest.fixture
 | 
				
			||||||
@@ -40,12 +38,14 @@ class TestSettingsFromFile(object):
 | 
				
			|||||||
        load_source.return_value = Mock(rules=['test'],
 | 
					        load_source.return_value = Mock(rules=['test'],
 | 
				
			||||||
                                        wait_command=10,
 | 
					                                        wait_command=10,
 | 
				
			||||||
                                        require_confirmation=True,
 | 
					                                        require_confirmation=True,
 | 
				
			||||||
                                        no_colors=True)
 | 
					                                        no_colors=True,
 | 
				
			||||||
 | 
					                                        priority={'vim': 100})
 | 
				
			||||||
        settings = conf.get_settings(Mock())
 | 
					        settings = conf.get_settings(Mock())
 | 
				
			||||||
        assert settings.rules == ['test']
 | 
					        assert settings.rules == ['test']
 | 
				
			||||||
        assert settings.wait_command == 10
 | 
					        assert settings.wait_command == 10
 | 
				
			||||||
        assert settings.require_confirmation is True
 | 
					        assert settings.require_confirmation is True
 | 
				
			||||||
        assert settings.no_colors is True
 | 
					        assert settings.no_colors is True
 | 
				
			||||||
 | 
					        assert settings.priority == {'vim': 100}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_from_file_with_DEFAULT(self, load_source):
 | 
					    def test_from_file_with_DEFAULT(self, load_source):
 | 
				
			||||||
        load_source.return_value = Mock(rules=conf.DEFAULT_RULES + ['test'],
 | 
					        load_source.return_value = Mock(rules=conf.DEFAULT_RULES + ['test'],
 | 
				
			||||||
@@ -62,12 +62,14 @@ class TestSettingsFromEnv(object):
 | 
				
			|||||||
        environ.update({'THEFUCK_RULES': 'bash:lisp',
 | 
					        environ.update({'THEFUCK_RULES': 'bash:lisp',
 | 
				
			||||||
                        'THEFUCK_WAIT_COMMAND': '55',
 | 
					                        'THEFUCK_WAIT_COMMAND': '55',
 | 
				
			||||||
                        'THEFUCK_REQUIRE_CONFIRMATION': 'true',
 | 
					                        'THEFUCK_REQUIRE_CONFIRMATION': 'true',
 | 
				
			||||||
                        'THEFUCK_NO_COLORS': 'false'})
 | 
					                        'THEFUCK_NO_COLORS': 'false',
 | 
				
			||||||
 | 
					                        'THEFUCK_PRIORITY': 'bash=10:lisp=wrong:vim=15'})
 | 
				
			||||||
        settings = conf.get_settings(Mock())
 | 
					        settings = conf.get_settings(Mock())
 | 
				
			||||||
        assert settings.rules == ['bash', 'lisp']
 | 
					        assert settings.rules == ['bash', 'lisp']
 | 
				
			||||||
        assert settings.wait_command == 55
 | 
					        assert settings.wait_command == 55
 | 
				
			||||||
        assert settings.require_confirmation is True
 | 
					        assert settings.require_confirmation is True
 | 
				
			||||||
        assert settings.no_colors is False
 | 
					        assert settings.no_colors is False
 | 
				
			||||||
 | 
					        assert settings.priority == {'bash': 10, 'vim': 15}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_from_env_with_DEFAULT(self, environ):
 | 
					    def test_from_env_with_DEFAULT(self, environ):
 | 
				
			||||||
        environ.update({'THEFUCK_RULES': 'DEFAULT_RULES:bash:lisp'})
 | 
					        environ.update({'THEFUCK_RULES': 'DEFAULT_RULES:bash:lisp'})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,34 +6,55 @@ from thefuck import main, conf, types
 | 
				
			|||||||
from tests.utils import Rule, Command
 | 
					from tests.utils import Rule, Command
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_load_rule(monkeypatch):
 | 
					def test_load_rule(mocker):
 | 
				
			||||||
    match = object()
 | 
					    match = object()
 | 
				
			||||||
    get_new_command = object()
 | 
					    get_new_command = object()
 | 
				
			||||||
    load_source = Mock()
 | 
					    load_source = mocker.patch(
 | 
				
			||||||
    load_source.return_value = Mock(match=match,
 | 
					        'thefuck.main.load_source',
 | 
				
			||||||
                                    get_new_command=get_new_command,
 | 
					        return_value=Mock(match=match,
 | 
				
			||||||
                                    enabled_by_default=True)
 | 
					                          get_new_command=get_new_command,
 | 
				
			||||||
    monkeypatch.setattr('thefuck.main.load_source', load_source)
 | 
					                          enabled_by_default=True,
 | 
				
			||||||
 | 
					                          priority=900))
 | 
				
			||||||
    assert main.load_rule(Path('/rules/bash.py')) \
 | 
					    assert main.load_rule(Path('/rules/bash.py')) \
 | 
				
			||||||
           == Rule('bash', match, get_new_command)
 | 
					           == Rule('bash', match, get_new_command, priority=900)
 | 
				
			||||||
    load_source.assert_called_once_with('bash', '/rules/bash.py')
 | 
					    load_source.assert_called_once_with('bash', '/rules/bash.py')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.parametrize('conf_rules, rules', [
 | 
					class TestGetRules(object):
 | 
				
			||||||
    (conf.DEFAULT_RULES, [Rule('bash', 'bash', 'bash'),
 | 
					    @pytest.fixture(autouse=True)
 | 
				
			||||||
                          Rule('lisp', 'lisp', 'lisp'),
 | 
					    def glob(self, mocker):
 | 
				
			||||||
                          Rule('bash', 'bash', 'bash'),
 | 
					        return mocker.patch('thefuck.main.Path.glob', return_value=[])
 | 
				
			||||||
                          Rule('lisp', 'lisp', 'lisp')]),
 | 
					
 | 
				
			||||||
    (types.RulesNamesList(['bash']), [Rule('bash', 'bash', 'bash'),
 | 
					    def _compare_names(self, rules, names):
 | 
				
			||||||
                                      Rule('bash', 'bash', 'bash')])])
 | 
					        return [r.name for r in rules] == names
 | 
				
			||||||
def test_get_rules(monkeypatch, conf_rules, rules):
 | 
					
 | 
				
			||||||
    monkeypatch.setattr(
 | 
					    @pytest.mark.parametrize('conf_rules, rules', [
 | 
				
			||||||
        'thefuck.main.Path.glob',
 | 
					        (conf.DEFAULT_RULES, ['bash', 'lisp', 'bash', 'lisp']),
 | 
				
			||||||
        lambda *_: [PosixPath('bash.py'), PosixPath('lisp.py')])
 | 
					        (types.RulesNamesList(['bash']), ['bash', 'bash'])])
 | 
				
			||||||
    monkeypatch.setattr('thefuck.main.load_source',
 | 
					    def test_get(self, monkeypatch, glob, conf_rules, rules):
 | 
				
			||||||
                        lambda x, _: Mock(match=x, get_new_command=x,
 | 
					        glob.return_value = [PosixPath('bash.py'), PosixPath('lisp.py')]
 | 
				
			||||||
                                          enabled_by_default=True))
 | 
					        monkeypatch.setattr('thefuck.main.load_source',
 | 
				
			||||||
    assert list(main.get_rules(Path('~'), Mock(rules=conf_rules))) == rules
 | 
					                            lambda x, _: Rule(x))
 | 
				
			||||||
 | 
					        assert self._compare_names(
 | 
				
			||||||
 | 
					            main.get_rules(Path('~'), Mock(rules=conf_rules, priority={})),
 | 
				
			||||||
 | 
					            rules)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @pytest.mark.parametrize('priority, unordered, ordered', [
 | 
				
			||||||
 | 
					        ({},
 | 
				
			||||||
 | 
					         [Rule('bash', priority=100), Rule('python', priority=5)],
 | 
				
			||||||
 | 
					         ['python', 'bash']),
 | 
				
			||||||
 | 
					        ({},
 | 
				
			||||||
 | 
					         [Rule('lisp', priority=9999), Rule('c', priority=conf.DEFAULT_PRIORITY)],
 | 
				
			||||||
 | 
					         ['c', 'lisp']),
 | 
				
			||||||
 | 
					        ({'python': 9999},
 | 
				
			||||||
 | 
					         [Rule('bash', priority=100), Rule('python', priority=5)],
 | 
				
			||||||
 | 
					         ['bash', 'python'])])
 | 
				
			||||||
 | 
					    def test_ordered_by_priority(self, monkeypatch, priority, unordered, ordered):
 | 
				
			||||||
 | 
					        monkeypatch.setattr('thefuck.main._get_loaded_rules',
 | 
				
			||||||
 | 
					                            lambda *_: unordered)
 | 
				
			||||||
 | 
					        assert self._compare_names(
 | 
				
			||||||
 | 
					            main.get_rules(Path('~'), Mock(priority=priority)),
 | 
				
			||||||
 | 
					            ordered)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestGetCommand(object):
 | 
					class TestGetCommand(object):
 | 
				
			||||||
@@ -64,6 +85,7 @@ class TestGetCommand(object):
 | 
				
			|||||||
                                      stdout=PIPE,
 | 
					                                      stdout=PIPE,
 | 
				
			||||||
                                      stderr=PIPE,
 | 
					                                      stderr=PIPE,
 | 
				
			||||||
                                      env={'LANG': 'C'})
 | 
					                                      env={'LANG': 'C'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @pytest.mark.parametrize('args, result', [
 | 
					    @pytest.mark.parametrize('args, result', [
 | 
				
			||||||
        (['thefuck', 'ls', '-la'], 'ls -la'),
 | 
					        (['thefuck', 'ls', '-la'], 'ls -la'),
 | 
				
			||||||
        (['thefuck', 'ls'], 'ls')])
 | 
					        (['thefuck', 'ls'], 'ls')])
 | 
				
			||||||
@@ -94,10 +116,8 @@ class TestGetMatchedRule(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class TestRunRule(object):
 | 
					class TestRunRule(object):
 | 
				
			||||||
    @pytest.fixture(autouse=True)
 | 
					    @pytest.fixture(autouse=True)
 | 
				
			||||||
    def confirm(self, monkeypatch):
 | 
					    def confirm(self, mocker):
 | 
				
			||||||
        mock = Mock(return_value=True)
 | 
					        return mocker.patch('thefuck.main.confirm', return_value=True)
 | 
				
			||||||
        monkeypatch.setattr('thefuck.main.confirm', mock)
 | 
					 | 
				
			||||||
        return mock
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_run_rule(self, capsys):
 | 
					    def test_run_rule(self, capsys):
 | 
				
			||||||
        main.run_rule(Rule(get_new_command=lambda *_: 'new-command'),
 | 
					        main.run_rule(Rule(get_new_command=lambda *_: 'new-command'),
 | 
				
			||||||
@@ -123,10 +143,8 @@ class TestRunRule(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class TestConfirm(object):
 | 
					class TestConfirm(object):
 | 
				
			||||||
    @pytest.fixture
 | 
					    @pytest.fixture
 | 
				
			||||||
    def stdin(self, monkeypatch):
 | 
					    def stdin(self, mocker):
 | 
				
			||||||
        mock = Mock(return_value='\n')
 | 
					        return mocker.patch('sys.stdin.read', return_value='\n')
 | 
				
			||||||
        monkeypatch.setattr('sys.stdin.read', mock)
 | 
					 | 
				
			||||||
        return mock
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_when_not_required(self, capsys):
 | 
					    def test_when_not_required(self, capsys):
 | 
				
			||||||
        assert main.confirm('command', None, Mock(require_confirmation=False))
 | 
					        assert main.confirm('command', None, Mock(require_confirmation=False))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,20 +1,15 @@
 | 
				
			|||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
from mock import Mock, MagicMock
 | 
					 | 
				
			||||||
from thefuck import shells
 | 
					from thefuck import shells
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.fixture
 | 
					@pytest.fixture
 | 
				
			||||||
def builtins_open(monkeypatch):
 | 
					def builtins_open(mocker):
 | 
				
			||||||
    mock = MagicMock()
 | 
					    return mocker.patch('six.moves.builtins.open')
 | 
				
			||||||
    monkeypatch.setattr('six.moves.builtins.open', mock)
 | 
					 | 
				
			||||||
    return mock
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.fixture
 | 
					@pytest.fixture
 | 
				
			||||||
def isfile(monkeypatch):
 | 
					def isfile(mocker):
 | 
				
			||||||
    mock = Mock(return_value=True)
 | 
					    return mocker.patch('os.path.isfile', return_value=True)
 | 
				
			||||||
    monkeypatch.setattr('os.path.isfile', mock)
 | 
					 | 
				
			||||||
    return mock
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestGeneric(object):
 | 
					class TestGeneric(object):
 | 
				
			||||||
@@ -32,9 +27,8 @@ class TestGeneric(object):
 | 
				
			|||||||
@pytest.mark.usefixtures('isfile')
 | 
					@pytest.mark.usefixtures('isfile')
 | 
				
			||||||
class TestBash(object):
 | 
					class TestBash(object):
 | 
				
			||||||
    @pytest.fixture(autouse=True)
 | 
					    @pytest.fixture(autouse=True)
 | 
				
			||||||
    def Popen(self, monkeypatch):
 | 
					    def Popen(self, mocker):
 | 
				
			||||||
        mock = Mock()
 | 
					        mock = mocker.patch('thefuck.shells.Popen')
 | 
				
			||||||
        monkeypatch.setattr('thefuck.shells.Popen', mock)
 | 
					 | 
				
			||||||
        mock.return_value.stdout.read.return_value = (
 | 
					        mock.return_value.stdout.read.return_value = (
 | 
				
			||||||
            b'alias l=\'ls -CF\'\n'
 | 
					            b'alias l=\'ls -CF\'\n'
 | 
				
			||||||
            b'alias la=\'ls -A\'\n'
 | 
					            b'alias la=\'ls -A\'\n'
 | 
				
			||||||
@@ -52,16 +46,15 @@ class TestBash(object):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def test_put_to_history(self, builtins_open):
 | 
					    def test_put_to_history(self, builtins_open):
 | 
				
			||||||
        shells.Bash().put_to_history('ls')
 | 
					        shells.Bash().put_to_history('ls')
 | 
				
			||||||
        builtins_open.return_value.__enter__.return_value.\
 | 
					        builtins_open.return_value.__enter__.return_value. \
 | 
				
			||||||
            write.assert_called_once_with('ls\n')
 | 
					            write.assert_called_once_with('ls\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.mark.usefixtures('isfile')
 | 
					@pytest.mark.usefixtures('isfile')
 | 
				
			||||||
class TestZsh(object):
 | 
					class TestZsh(object):
 | 
				
			||||||
    @pytest.fixture(autouse=True)
 | 
					    @pytest.fixture(autouse=True)
 | 
				
			||||||
    def Popen(self, monkeypatch):
 | 
					    def Popen(self, mocker):
 | 
				
			||||||
        mock = Mock()
 | 
					        mock = mocker.patch('thefuck.shells.Popen')
 | 
				
			||||||
        monkeypatch.setattr('thefuck.shells.Popen', mock)
 | 
					 | 
				
			||||||
        mock.return_value.stdout.read.return_value = (
 | 
					        mock.return_value.stdout.read.return_value = (
 | 
				
			||||||
            b'l=\'ls -CF\'\n'
 | 
					            b'l=\'ls -CF\'\n'
 | 
				
			||||||
            b'la=\'ls -A\'\n'
 | 
					            b'la=\'ls -A\'\n'
 | 
				
			||||||
@@ -77,9 +70,9 @@ class TestZsh(object):
 | 
				
			|||||||
    def test_to_shell(self):
 | 
					    def test_to_shell(self):
 | 
				
			||||||
        assert shells.Zsh().to_shell('pwd') == 'pwd'
 | 
					        assert shells.Zsh().to_shell('pwd') == 'pwd'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_put_to_history(self, builtins_open, monkeypatch):
 | 
					    def test_put_to_history(self, builtins_open, mocker):
 | 
				
			||||||
        monkeypatch.setattr('thefuck.shells.time',
 | 
					        mocker.patch('thefuck.shells.time',
 | 
				
			||||||
                            lambda: 1430707243.3517463)
 | 
					                     return_value=1430707243.3517463)
 | 
				
			||||||
        shells.Zsh().put_to_history('ls')
 | 
					        shells.Zsh().put_to_history('ls')
 | 
				
			||||||
        builtins_open.return_value.__enter__.return_value. \
 | 
					        builtins_open.return_value.__enter__.return_value. \
 | 
				
			||||||
            write.assert_called_once_with(': 1430707243:0;ls\n')
 | 
					            write.assert_called_once_with(': 1430707243:0;ls\n')
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
from thefuck import types
 | 
					from thefuck import types
 | 
				
			||||||
 | 
					from thefuck.conf import DEFAULT_PRIORITY
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def Command(script='', stdout='', stderr=''):
 | 
					def Command(script='', stdout='', stderr=''):
 | 
				
			||||||
@@ -8,6 +9,8 @@ def Command(script='', stdout='', stderr=''):
 | 
				
			|||||||
def Rule(name='', match=lambda *_: True,
 | 
					def Rule(name='', match=lambda *_: True,
 | 
				
			||||||
         get_new_command=lambda *_: '',
 | 
					         get_new_command=lambda *_: '',
 | 
				
			||||||
         enabled_by_default=True,
 | 
					         enabled_by_default=True,
 | 
				
			||||||
         side_effect=None):
 | 
					         side_effect=None,
 | 
				
			||||||
 | 
					         priority=DEFAULT_PRIORITY):
 | 
				
			||||||
    return types.Rule(name, match, get_new_command,
 | 
					    return types.Rule(name, match, get_new_command,
 | 
				
			||||||
                      enabled_by_default, side_effect)
 | 
					                      enabled_by_default, side_effect,
 | 
				
			||||||
 | 
					                      priority)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,17 +22,20 @@ class _DefaultRulesNames(types.RulesNamesList):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEFAULT_RULES = _DefaultRulesNames([])
 | 
					DEFAULT_RULES = _DefaultRulesNames([])
 | 
				
			||||||
 | 
					DEFAULT_PRIORITY = 1000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEFAULT_SETTINGS = {'rules': DEFAULT_RULES,
 | 
					DEFAULT_SETTINGS = {'rules': DEFAULT_RULES,
 | 
				
			||||||
                    'wait_command': 3,
 | 
					                    'wait_command': 3,
 | 
				
			||||||
                    'require_confirmation': False,
 | 
					                    'require_confirmation': False,
 | 
				
			||||||
                    'no_colors': False}
 | 
					                    'no_colors': False,
 | 
				
			||||||
 | 
					                    'priority': {}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
 | 
					ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
 | 
				
			||||||
               'THEFUCK_WAIT_COMMAND': 'wait_command',
 | 
					               'THEFUCK_WAIT_COMMAND': 'wait_command',
 | 
				
			||||||
               'THEFUCK_REQUIRE_CONFIRMATION': 'require_confirmation',
 | 
					               'THEFUCK_REQUIRE_CONFIRMATION': 'require_confirmation',
 | 
				
			||||||
               'THEFUCK_NO_COLORS': 'no_colors'}
 | 
					               'THEFUCK_NO_COLORS': 'no_colors',
 | 
				
			||||||
 | 
					               'THEFUCK_PRIORITY': 'priority'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SETTINGS_HEADER = u"""# ~/.thefuck/settings.py: The Fuck settings file
 | 
					SETTINGS_HEADER = u"""# ~/.thefuck/settings.py: The Fuck settings file
 | 
				
			||||||
@@ -65,16 +68,29 @@ def _rules_from_env(val):
 | 
				
			|||||||
    return val
 | 
					    return val
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _priority_from_env(val):
 | 
				
			||||||
 | 
					    """Gets priority pairs from env."""
 | 
				
			||||||
 | 
					    for part in val.split(':'):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            rule, priority = part.split('=')
 | 
				
			||||||
 | 
					            yield rule, int(priority)
 | 
				
			||||||
 | 
					        except ValueError:
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _val_from_env(env, attr):
 | 
					def _val_from_env(env, attr):
 | 
				
			||||||
    """Transforms env-strings to python."""
 | 
					    """Transforms env-strings to python."""
 | 
				
			||||||
    val = os.environ[env]
 | 
					    val = os.environ[env]
 | 
				
			||||||
    if attr == 'rules':
 | 
					    if attr == 'rules':
 | 
				
			||||||
        val = _rules_from_env(val)
 | 
					        return _rules_from_env(val)
 | 
				
			||||||
 | 
					    elif attr == 'priority':
 | 
				
			||||||
 | 
					        return dict(_priority_from_env(val))
 | 
				
			||||||
    elif attr == 'wait_command':
 | 
					    elif attr == 'wait_command':
 | 
				
			||||||
        val = int(val)
 | 
					        return int(val)
 | 
				
			||||||
    elif attr in ('require_confirmation', 'no_colors'):
 | 
					    elif attr in ('require_confirmation', 'no_colors'):
 | 
				
			||||||
        val = val.lower() == 'true'
 | 
					        return val.lower() == 'true'
 | 
				
			||||||
    return val
 | 
					    else:
 | 
				
			||||||
 | 
					        return val
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _settings_from_env():
 | 
					def _settings_from_env():
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,7 +26,17 @@ def load_rule(rule):
 | 
				
			|||||||
    return types.Rule(rule.name[:-3], rule_module.match,
 | 
					    return types.Rule(rule.name[:-3], rule_module.match,
 | 
				
			||||||
                      rule_module.get_new_command,
 | 
					                      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))
 | 
					                      getattr(rule_module, 'side_effect', None),
 | 
				
			||||||
 | 
					                      getattr(rule_module, 'priority', conf.DEFAULT_PRIORITY))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _get_loaded_rules(rules, settings):
 | 
				
			||||||
 | 
					    """Yields all available rules."""
 | 
				
			||||||
 | 
					    for rule in rules:
 | 
				
			||||||
 | 
					        if rule.name != '__init__.py':
 | 
				
			||||||
 | 
					            loaded_rule = load_rule(rule)
 | 
				
			||||||
 | 
					            if loaded_rule in settings.rules:
 | 
				
			||||||
 | 
					                yield loaded_rule
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_rules(user_dir, settings):
 | 
					def get_rules(user_dir, settings):
 | 
				
			||||||
@@ -35,11 +45,9 @@ def get_rules(user_dir, settings):
 | 
				
			|||||||
        .joinpath('rules') \
 | 
					        .joinpath('rules') \
 | 
				
			||||||
        .glob('*.py')
 | 
					        .glob('*.py')
 | 
				
			||||||
    user = user_dir.joinpath('rules').glob('*.py')
 | 
					    user = user_dir.joinpath('rules').glob('*.py')
 | 
				
			||||||
    for rule in sorted(list(bundled)) + list(user):
 | 
					    rules = _get_loaded_rules(sorted(bundled) + sorted(user), settings)
 | 
				
			||||||
        if rule.name != '__init__.py':
 | 
					    return sorted(rules, key=lambda rule: settings.priority.get(
 | 
				
			||||||
            loaded_rule = load_rule(rule)
 | 
					        rule.name, rule.priority))
 | 
				
			||||||
            if loaded_rule in settings.rules:
 | 
					 | 
				
			||||||
                yield loaded_rule
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def wait_output(settings, popen):
 | 
					def wait_output(settings, popen):
 | 
				
			||||||
@@ -113,10 +121,6 @@ def run_rule(rule, command, settings):
 | 
				
			|||||||
        print(new_command)
 | 
					        print(new_command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def alias():
 | 
					 | 
				
			||||||
    print(shells.app_alias())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def main():
 | 
					def main():
 | 
				
			||||||
    colorama.init()
 | 
					    colorama.init()
 | 
				
			||||||
    user_dir = setup_user_dir()
 | 
					    user_dir = setup_user_dir()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										15
									
								
								thefuck/rules/git_add.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								thefuck/rules/git_add.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					import re
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def match(command, settings):
 | 
				
			||||||
 | 
					    return ('git' in command.script
 | 
				
			||||||
 | 
					            and 'did not match any file(s) known to git.' in command.stderr
 | 
				
			||||||
 | 
					            and "Did you forget to 'git add'?" in command.stderr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_new_command(command, settings):
 | 
				
			||||||
 | 
					    missing_file = re.findall(
 | 
				
			||||||
 | 
					            r"error: pathspec '([^']*)' "
 | 
				
			||||||
 | 
					            "did not match any file\(s\) known to git.", command.stderr)[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 'git add -- {} && {}'.format(missing_file, command.script)
 | 
				
			||||||
@@ -31,3 +31,6 @@ def get_new_command(command, settings):
 | 
				
			|||||||
    new_command = get_close_matches(old_command,
 | 
					    new_command = get_close_matches(old_command,
 | 
				
			||||||
                                    _get_all_bins())[0]
 | 
					                                    _get_all_bins())[0]
 | 
				
			||||||
    return ' '.join([new_command] + command.script.split(' ')[1:])
 | 
					    return ' '.join([new_command] + command.script.split(' ')[1:])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					priority = 3000
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										43
									
								
								thefuck/rules/pacman.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								thefuck/rules/pacman.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					import subprocess
 | 
				
			||||||
 | 
					from thefuck.utils import DEVNULL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def __command_available(command):
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        subprocess.check_output([command], stderr=DEVNULL)
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					    except subprocess.CalledProcessError:
 | 
				
			||||||
 | 
					        # command exists but is not happy to be called without any argument
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					    except OSError:
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def __get_pkgfile(command):
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        return subprocess.check_output(
 | 
				
			||||||
 | 
					            ['pkgfile', '-b', '-v', command.script.split(" ")[0]],
 | 
				
			||||||
 | 
					            universal_newlines=True, stderr=subprocess.DEVNULL
 | 
				
			||||||
 | 
					        ).split()
 | 
				
			||||||
 | 
					    except subprocess.CalledProcessError:
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def match(command, settings):
 | 
				
			||||||
 | 
					    return 'not found' in command.stderr and __get_pkgfile(command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_new_command(command, settings):
 | 
				
			||||||
 | 
					    package = __get_pkgfile(command)[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return '{} -S {} && {}'.format(pacman, package, command.script)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if not __command_available('pkgfile'):
 | 
				
			||||||
 | 
					    enabled_by_default = False
 | 
				
			||||||
 | 
					elif __command_available('yaourt'):
 | 
				
			||||||
 | 
					    pacman = 'yaourt'
 | 
				
			||||||
 | 
					elif __command_available('pacman'):
 | 
				
			||||||
 | 
					    pacman = 'sudo pacman'
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					    enabled_by_default = False
 | 
				
			||||||
@@ -8,9 +8,7 @@ from subprocess import Popen, PIPE
 | 
				
			|||||||
from time import time
 | 
					from time import time
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
from psutil import Process
 | 
					from psutil import Process
 | 
				
			||||||
 | 
					from .utils import DEVNULL
 | 
				
			||||||
 | 
					 | 
				
			||||||
FNULL = open(os.devnull, 'w')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Generic(object):
 | 
					class Generic(object):
 | 
				
			||||||
@@ -58,7 +56,7 @@ class Bash(Generic):
 | 
				
			|||||||
        return name, value
 | 
					        return name, value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_aliases(self):
 | 
					    def _get_aliases(self):
 | 
				
			||||||
        proc = Popen('bash -ic alias', stdout=PIPE, stderr=FNULL, shell=True)
 | 
					        proc = Popen('bash -ic alias', stdout=PIPE, stderr=DEVNULL, shell=True)
 | 
				
			||||||
        return dict(
 | 
					        return dict(
 | 
				
			||||||
            self._parse_alias(alias)
 | 
					            self._parse_alias(alias)
 | 
				
			||||||
            for alias in proc.stdout.read().decode('utf-8').split('\n')
 | 
					            for alias in proc.stdout.read().decode('utf-8').split('\n')
 | 
				
			||||||
@@ -80,7 +78,7 @@ class Zsh(Generic):
 | 
				
			|||||||
        return name, value
 | 
					        return name, value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_aliases(self):
 | 
					    def _get_aliases(self):
 | 
				
			||||||
        proc = Popen('zsh -ic alias', stdout=PIPE, stderr=FNULL, shell=True)
 | 
					        proc = Popen('zsh -ic alias', stdout=PIPE, stderr=DEVNULL, shell=True)
 | 
				
			||||||
        return dict(
 | 
					        return dict(
 | 
				
			||||||
            self._parse_alias(alias)
 | 
					            self._parse_alias(alias)
 | 
				
			||||||
            for alias in proc.stdout.read().decode('utf-8').split('\n')
 | 
					            for alias in proc.stdout.read().decode('utf-8').split('\n')
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,8 @@ from collections import namedtuple
 | 
				
			|||||||
Command = namedtuple('Command', ('script', 'stdout', 'stderr'))
 | 
					Command = namedtuple('Command', ('script', 'stdout', 'stderr'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Rule = namedtuple('Rule', ('name', 'match', 'get_new_command',
 | 
					Rule = namedtuple('Rule', ('name', 'match', 'get_new_command',
 | 
				
			||||||
                           'enabled_by_default', 'side_effect'))
 | 
					                           'enabled_by_default', 'side_effect',
 | 
				
			||||||
 | 
					                           'priority'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RulesNamesList(list):
 | 
					class RulesNamesList(list):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,9 @@ import six
 | 
				
			|||||||
from .types import Command
 | 
					from .types import Command
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEVNULL = open(os.devnull, 'w')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def which(program):
 | 
					def which(program):
 | 
				
			||||||
    """Returns `program` path or `None`."""
 | 
					    """Returns `program` path or `None`."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user