mirror of
https://github.com/nvbn/thefuck.git
synced 2025-01-31 02:01:13 +00:00
Add information about writting yourself rules, revert no_command changes
This commit is contained in:
parent
1503dcf294
commit
1de9c5f77b
34
README.md
34
README.md
@ -61,6 +61,40 @@ And add to `.bashrc` or `.zshrc`:
|
|||||||
alias fuck='$(thefuck $(fc -ln -1))'
|
alias fuck='$(thefuck $(fc -ln -1))'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Creating your own rules
|
||||||
|
|
||||||
|
For adding your own rule you should create `your-rule-name.py`
|
||||||
|
in `~/.thefuck/rules`. Rule should contain two functions:
|
||||||
|
`match(command: Command, settings: Settings) -> bool`
|
||||||
|
and `get_new_command(command: Command, settings: Settings) -> str`.
|
||||||
|
|
||||||
|
`Command` have three attributes: `script`, `stdout` and `stderr`.
|
||||||
|
|
||||||
|
`Settings` is `~/.thefuck/settings.py`.
|
||||||
|
|
||||||
|
Simple example of the rule for running script with `sudo`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def match(command, settings):
|
||||||
|
return ('permission denied' in command.stderr.lower()
|
||||||
|
or 'EACCES' in command.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_command(command, settings):
|
||||||
|
return 'sudo {}'.format(command.script)
|
||||||
|
```
|
||||||
|
|
||||||
|
[More examples of rules](https://github.com/nvbn/thefuck/tree/master/thefuck/rules),
|
||||||
|
[utility functions for rules]((https://github.com/nvbn/thefuck/tree/master/thefuck/utils.py)).
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
The Fuck have a few settings parameters:
|
||||||
|
|
||||||
|
* `rules` – list of enabled rules, by default all;
|
||||||
|
* `command_not_found` – path to `command_not_found` binary,
|
||||||
|
by default `/usr/lib/command-not-found`.
|
||||||
|
|
||||||
## Developing
|
## Developing
|
||||||
|
|
||||||
Install `The Fuck` for development:
|
Install `The Fuck` for development:
|
||||||
|
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
|
|
||||||
setup(name='thefuck',
|
setup(name='thefuck',
|
||||||
version=1.3,
|
version=1.4,
|
||||||
description="Magnificent app which corrects your previous console command",
|
description="Magnificent app which corrects your previous console command",
|
||||||
author='Vladimir Iakovlev',
|
author='Vladimir Iakovlev',
|
||||||
author_email='nvbn.rm@gmail.com',
|
author_email='nvbn.rm@gmail.com',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from subprocess import PIPE
|
from subprocess import PIPE
|
||||||
from mock import patch, Mock
|
from mock import patch, Mock
|
||||||
import pytest
|
import pytest
|
||||||
from thefuck.rules.no_command_apt import match, get_new_command
|
from thefuck.rules.no_command import match, get_new_command
|
||||||
from thefuck.main import Command
|
from thefuck.main import Command
|
||||||
|
|
||||||
|
|
||||||
@ -21,23 +21,30 @@ vom: command not found
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def bins_exists(request):
|
def bins_exists(request):
|
||||||
p = patch('thefuck.rules.no_command_apt.which',
|
p = patch('thefuck.rules.no_command.which',
|
||||||
return_value=True)
|
return_value=True)
|
||||||
p.start()
|
p.start()
|
||||||
request.addfinalizer(p.stop)
|
request.addfinalizer(p.stop)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def settings():
|
||||||
|
class _Settings(object):
|
||||||
|
pass
|
||||||
|
return _Settings
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('bins_exists')
|
@pytest.mark.usefixtures('bins_exists')
|
||||||
def test_match(command_found, command_not_found):
|
def test_match(command_found, command_not_found, settings):
|
||||||
with patch('thefuck.rules.no_command_apt.Popen') as Popen:
|
with patch('thefuck.rules.no_command.Popen') as Popen:
|
||||||
Popen.return_value.stderr.read.return_value = command_found
|
Popen.return_value.stderr.read.return_value = command_found
|
||||||
assert match(Command('aptget install vim', '', ''), None)
|
assert match(Command('aptget install vim', '', ''), settings)
|
||||||
Popen.assert_called_once_with('/usr/lib/command-not-found aptget',
|
Popen.assert_called_once_with('/usr/lib/command-not-found aptget',
|
||||||
shell=True, stderr=PIPE)
|
shell=True, stderr=PIPE)
|
||||||
Popen.return_value.stderr.read.return_value = command_not_found
|
Popen.return_value.stderr.read.return_value = command_not_found
|
||||||
assert not match(Command('ls', '', ''), None)
|
assert not match(Command('ls', '', ''), settings)
|
||||||
|
|
||||||
with patch('thefuck.rules.no_command_apt.Popen') as Popen:
|
with patch('thefuck.rules.no_command.Popen') as Popen:
|
||||||
Popen.return_value.stderr.read.return_value = command_found
|
Popen.return_value.stderr.read.return_value = command_found
|
||||||
assert match(Command('sudo aptget install vim', '', ''),
|
assert match(Command('sudo aptget install vim', '', ''),
|
||||||
Mock(command_not_found='test'))
|
Mock(command_not_found='test'))
|
||||||
@ -47,9 +54,9 @@ def test_match(command_found, command_not_found):
|
|||||||
|
|
||||||
@pytest.mark.usefixtures('bins_exists')
|
@pytest.mark.usefixtures('bins_exists')
|
||||||
def test_get_new_command(command_found):
|
def test_get_new_command(command_found):
|
||||||
with patch('thefuck.rules.no_command_apt._get_output',
|
with patch('thefuck.rules.no_command._get_output',
|
||||||
return_value=command_found.decode()):
|
return_value=command_found.decode()):
|
||||||
assert get_new_command(Command('aptget install vim', '', ''), None)\
|
assert get_new_command(Command('aptget install vim', '', ''), settings)\
|
||||||
== 'apt-get install vim'
|
== 'apt-get install vim'
|
||||||
assert get_new_command(Command('sudo aptget install vim', '', ''), None) \
|
assert get_new_command(Command('sudo aptget install vim', '', ''), settings) \
|
||||||
== 'sudo apt-get install vim'
|
== 'sudo apt-get install vim'
|
@ -1,25 +1,26 @@
|
|||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
import re
|
import re
|
||||||
from thefuck.utils import which
|
from thefuck.utils import which, wrap_settings
|
||||||
|
|
||||||
|
|
||||||
def _get_bin(settings):
|
local_settings = {'command_not_found': '/usr/lib/command-not-found'}
|
||||||
return getattr(settings, 'command_not_found', '/usr/lib/command-not-found')
|
|
||||||
|
|
||||||
|
|
||||||
def _get_output(command, settings):
|
def _get_output(command, settings):
|
||||||
name = command.script.split(' ')[command.script.startswith('sudo')]
|
name = command.script.split(' ')[command.script.startswith('sudo')]
|
||||||
check_script = '{} {}'.format(_get_bin(settings), name)
|
check_script = '{} {}'.format(settings.command_not_found, name)
|
||||||
result = Popen(check_script, shell=True, stderr=PIPE)
|
result = Popen(check_script, shell=True, stderr=PIPE)
|
||||||
return result.stderr.read().decode()
|
return result.stderr.read().decode()
|
||||||
|
|
||||||
|
|
||||||
|
@wrap_settings(local_settings)
|
||||||
def match(command, settings):
|
def match(command, settings):
|
||||||
if which('apt-get') and which(_get_bin(settings)):
|
if which(settings.command_not_found):
|
||||||
output = _get_output(command, settings)
|
output = _get_output(command, settings)
|
||||||
return "No command" in output and "from package" in output
|
return "No command" in output and "from package" in output
|
||||||
|
|
||||||
|
|
||||||
|
@wrap_settings(local_settings)
|
||||||
def get_new_command(command, settings):
|
def get_new_command(command, settings):
|
||||||
output = _get_output(command, settings)
|
output = _get_output(command, settings)
|
||||||
broken_name = re.findall(r"No command '([^']*)' found",
|
broken_name = re.findall(r"No command '([^']*)' found",
|
@ -1,7 +1,10 @@
|
|||||||
|
from functools import wraps
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
def which(program):
|
def which(program):
|
||||||
|
"""Returns `program` path or `None`."""
|
||||||
|
|
||||||
def is_exe(fpath):
|
def is_exe(fpath):
|
||||||
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
|
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
|
||||||
|
|
||||||
@ -17,3 +20,24 @@ def which(program):
|
|||||||
return exe_file
|
return exe_file
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def wrap_settings(params):
|
||||||
|
"""Adds default values to settings if it not presented.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
@wrap_settings({'apt': '/usr/bin/apt'})
|
||||||
|
def match(command, settings):
|
||||||
|
print(settings.apt)
|
||||||
|
|
||||||
|
"""
|
||||||
|
def decorator(fn):
|
||||||
|
@wraps(fn)
|
||||||
|
def wrapper(command, settings):
|
||||||
|
for key, val in params.items():
|
||||||
|
if not hasattr(settings, key):
|
||||||
|
setattr(settings, key, val)
|
||||||
|
return fn(command, settings)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
Loading…
x
Reference in New Issue
Block a user