1
0
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:
nvbn 2015-04-17 17:01:30 +02:00
parent 1503dcf294
commit 1de9c5f77b
5 changed files with 82 additions and 16 deletions

View File

@ -61,6 +61,40 @@ And add to `.bashrc` or `.zshrc`:
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
Install `The Fuck` for development:

View File

@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(name='thefuck',
version=1.3,
version=1.4,
description="Magnificent app which corrects your previous console command",
author='Vladimir Iakovlev',
author_email='nvbn.rm@gmail.com',

View File

@ -1,7 +1,7 @@
from subprocess import PIPE
from mock import patch, Mock
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
@ -21,23 +21,30 @@ vom: command not found
@pytest.fixture
def bins_exists(request):
p = patch('thefuck.rules.no_command_apt.which',
p = patch('thefuck.rules.no_command.which',
return_value=True)
p.start()
request.addfinalizer(p.stop)
@pytest.fixture
def settings():
class _Settings(object):
pass
return _Settings
@pytest.mark.usefixtures('bins_exists')
def test_match(command_found, command_not_found):
with patch('thefuck.rules.no_command_apt.Popen') as Popen:
def test_match(command_found, command_not_found, settings):
with patch('thefuck.rules.no_command.Popen') as Popen:
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',
shell=True, stderr=PIPE)
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
assert match(Command('sudo aptget install vim', '', ''),
Mock(command_not_found='test'))
@ -47,9 +54,9 @@ def test_match(command_found, command_not_found):
@pytest.mark.usefixtures('bins_exists')
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()):
assert get_new_command(Command('aptget install vim', '', ''), None)\
assert get_new_command(Command('aptget install vim', '', ''), settings)\
== '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'

View File

@ -1,25 +1,26 @@
from subprocess import Popen, PIPE
import re
from thefuck.utils import which
from thefuck.utils import which, wrap_settings
def _get_bin(settings):
return getattr(settings, 'command_not_found', '/usr/lib/command-not-found')
local_settings = {'command_not_found': '/usr/lib/command-not-found'}
def _get_output(command, settings):
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)
return result.stderr.read().decode()
@wrap_settings(local_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)
return "No command" in output and "from package" in output
@wrap_settings(local_settings)
def get_new_command(command, settings):
output = _get_output(command, settings)
broken_name = re.findall(r"No command '([^']*)' found",

View File

@ -1,7 +1,10 @@
from functools import wraps
import os
def which(program):
"""Returns `program` path or `None`."""
def is_exe(fpath):
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
@ -17,3 +20,24 @@ def which(program):
return exe_file
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