mirror of
https://github.com/nvbn/thefuck.git
synced 2025-11-01 15:42:06 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b80f3ea6e4 | ||
|
|
99d9c9aff7 | ||
|
|
4be60c78fa | ||
|
|
847b9e2cec | ||
|
|
c463fea8a0 | ||
|
|
7652884df6 | ||
|
|
e1ca120eb8 | ||
|
|
d3295e6a4e | ||
|
|
bc2c87e8fe | ||
|
|
a2ac15da56 | ||
|
|
4cf631fa47 | ||
|
|
cbf6507e1e | ||
|
|
49ed98c8a4 | ||
|
|
52bf3907a1 | ||
|
|
077de17d6c | ||
|
|
419878f526 | ||
|
|
f610cf2256 | ||
|
|
2f04a953ba | ||
|
|
698451f65d | ||
|
|
b7cb407637 | ||
|
|
bb6b9a638c | ||
|
|
0009fb0588 | ||
|
|
a9d3456e29 | ||
|
|
1e28671934 | ||
|
|
3134a60e27 | ||
|
|
03dd7eda04 | ||
|
|
d12a8bcdd8 | ||
|
|
58069f0a3e | ||
|
|
d0e02bc20c | ||
|
|
e554238996 | ||
|
|
fa465620ba | ||
|
|
294ba07ce1 | ||
|
|
e21befbcc4 | ||
|
|
a454438939 | ||
|
|
1fa7827f1a | ||
|
|
93b6a623e1 |
54
README.md
54
README.md
@@ -88,8 +88,8 @@ Reading package lists... Done
|
||||
|
||||
## Requirements
|
||||
|
||||
- python (2.7+ or 3.3+)
|
||||
- pip
|
||||
- python
|
||||
- python-dev
|
||||
|
||||
## Installation
|
||||
@@ -100,17 +100,7 @@ Install `The Fuck` with `pip`:
|
||||
sudo pip install thefuck
|
||||
```
|
||||
|
||||
If it fails try to use `easy_install`:
|
||||
|
||||
```bash
|
||||
sudo easy_install thefuck
|
||||
```
|
||||
|
||||
Or using an OS package manager (currently supported in OSX via [brew](http://brew.sh)):
|
||||
|
||||
```bash
|
||||
brew install thefuck
|
||||
```
|
||||
[Or using an OS package manager (OS X, Ubuntu, Arch).](https://github.com/nvbn/thefuck/wiki/Installation)
|
||||
|
||||
And add to `.bashrc` or `.zshrc` or `.bash_profile`(for OSX):
|
||||
|
||||
@@ -120,25 +110,7 @@ alias fuck='eval $(thefuck $(fc -ln -1))'
|
||||
alias FUCK='fuck'
|
||||
```
|
||||
|
||||
Or in `config.fish`:
|
||||
|
||||
```fish
|
||||
function fuck
|
||||
eval (thefuck $history[1])
|
||||
end
|
||||
```
|
||||
|
||||
Or in your Powershell `$PROFILE` on Windows:
|
||||
|
||||
```powershell
|
||||
function fuck {
|
||||
$fuck = $(thefuck (get-history -count 1).commandline)
|
||||
if($fuck.startswith("echo")) {
|
||||
$fuck.substring(5)
|
||||
}
|
||||
else { iex "$fuck" }
|
||||
}
|
||||
```
|
||||
[On in your shell config (Bash, Zsh, Fish, Powershell).](https://github.com/nvbn/thefuck/wiki/Shell-aliases)
|
||||
|
||||
Changes will be available only in a new shell session.
|
||||
|
||||
@@ -156,6 +128,7 @@ using matched rule and run it. Rules enabled by default:
|
||||
|
||||
* `brew_unknown_command` – fixes wrong brew commands, for example `brew docto/brew doctor`;
|
||||
* `cd_parent` – changes `cd..` to `cd ..`;
|
||||
* `cd_mkdir` – creates directories before cd'ing into them;
|
||||
* `cp_omitting_directory` – adds `-a` when you `cp` directory;
|
||||
* `fix_alt_space` – replaces Alt+Space with Space character;
|
||||
* `git_no_command` – fixes wrong git commands like `git brnch`;
|
||||
@@ -174,6 +147,7 @@ using matched rule and run it. Rules enabled by default:
|
||||
|
||||
Bundled, but not enabled by default:
|
||||
|
||||
* `ls_lah` – adds -lah to ls;
|
||||
* `rm_root` – adds `--no-preserve-root` to `rm -rf /` command.
|
||||
|
||||
## Creating your own rules
|
||||
@@ -211,12 +185,30 @@ The Fuck has a few settings parameters, they can be changed in `~/.thefuck/setti
|
||||
* `wait_command` – max amount of time in seconds for getting previous command output;
|
||||
* `no_colors` – disable colored output.
|
||||
|
||||
Example of `settings.py`:
|
||||
|
||||
```python
|
||||
rules = ['sudo', 'no_command']
|
||||
require_confirmation = True
|
||||
wait_command = 10
|
||||
no_colors = False
|
||||
```
|
||||
|
||||
Or via environment variables:
|
||||
|
||||
* `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_WAIT_COMMAND` – max amount of time in seconds for getting previous command output;
|
||||
* `THEFUCK_NO_COLORS` – disable colored output, `true/false`.
|
||||
|
||||
For example:
|
||||
|
||||
```bash
|
||||
export THEFUCK_RULES='sudo:no_command'
|
||||
export THEFUCK_REQUIRE_CONFIRMATION='true'
|
||||
export THEFUCK_WAIT_COMMAND=10
|
||||
export THEFUCK_NO_COLORS='false'
|
||||
```
|
||||
|
||||
## Developing
|
||||
|
||||
|
||||
2
setup.py
2
setup.py
@@ -1,7 +1,7 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
|
||||
VERSION = '1.29'
|
||||
VERSION = '1.31'
|
||||
|
||||
|
||||
setup(name='thefuck',
|
||||
|
||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
@@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
from thefuck.types import Command
|
||||
from thefuck.rules.brew_install import match, get_new_command
|
||||
from thefuck.rules.brew_install import brew_formulas
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -29,21 +29,21 @@ def _is_not_okay_to_test():
|
||||
reason='No need to run if there\'s no formula')
|
||||
def test_match(brew_no_available_formula, brew_already_installed,
|
||||
brew_install_no_argument):
|
||||
assert match(Command('brew install elsticsearch', '',
|
||||
brew_no_available_formula), None)
|
||||
assert not match(Command('brew install git', '',
|
||||
brew_already_installed), None)
|
||||
assert not match(Command('brew install', '', brew_install_no_argument),
|
||||
assert match(Command('brew install elsticsearch',
|
||||
stderr=brew_no_available_formula), None)
|
||||
assert not match(Command('brew install git',
|
||||
stderr=brew_already_installed), None)
|
||||
assert not match(Command('brew install', stderr=brew_install_no_argument),
|
||||
None)
|
||||
|
||||
|
||||
@pytest.mark.skipif(_is_not_okay_to_test(),
|
||||
reason='No need to run if there\'s no formula')
|
||||
def test_get_new_command(brew_no_available_formula):
|
||||
assert get_new_command(Command('brew install elsticsearch', '',
|
||||
brew_no_available_formula), None)\
|
||||
assert get_new_command(Command('brew install elsticsearch',
|
||||
stderr=brew_no_available_formula), None)\
|
||||
== 'brew install elasticsearch'
|
||||
|
||||
assert get_new_command(Command('brew install aa', '',
|
||||
brew_no_available_formula),
|
||||
assert get_new_command(Command('brew install aa',
|
||||
stderr=brew_no_available_formula),
|
||||
None) != 'brew install aha'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
from thefuck.types import Command
|
||||
from thefuck.rules.brew_unknown_command import match, get_new_command
|
||||
from thefuck.rules.brew_unknown_command import brew_commands
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -15,14 +15,14 @@ def brew_unknown_cmd_instaa():
|
||||
|
||||
|
||||
def test_match(brew_unknown_cmd):
|
||||
assert match(Command('brew inst', '', brew_unknown_cmd), None)
|
||||
assert match(Command('brew inst', stderr=brew_unknown_cmd), None)
|
||||
for command in brew_commands:
|
||||
assert not match(Command('brew ' + command, '', ''), None)
|
||||
assert not match(Command('brew ' + command), None)
|
||||
|
||||
|
||||
def test_get_new_command(brew_unknown_cmd, brew_unknown_cmd_instaa):
|
||||
assert get_new_command(Command('brew inst', '', brew_unknown_cmd), None)\
|
||||
assert get_new_command(Command('brew inst', stderr=brew_unknown_cmd), None)\
|
||||
== 'brew list'
|
||||
|
||||
assert get_new_command(Command('brew instaa', '', brew_unknown_cmd_instaa),
|
||||
assert get_new_command(Command('brew instaa', stderr=brew_unknown_cmd_instaa),
|
||||
None) == 'brew install'
|
||||
|
||||
25
tests/rules/test_cd_mkdir.py
Normal file
25
tests/rules/test_cd_mkdir.py
Normal file
@@ -0,0 +1,25 @@
|
||||
import pytest
|
||||
from thefuck.rules.cd_mkdir import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command(script='cd foo', stderr='cd: foo: No such file or directory'),
|
||||
Command(script='cd foo/bar/baz',
|
||||
stderr='cd: foo: No such file or directory'),
|
||||
Command(script='cd foo/bar/baz', stderr='cd: can\'t cd to foo/bar/baz')])
|
||||
def test_match(command):
|
||||
assert match(command, None)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command(script='cd foo', stderr=''), Command()])
|
||||
def test_not_match(command):
|
||||
assert not match(command, None)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, new_command', [
|
||||
(Command('cd foo'), 'mkdir -p foo && cd foo'),
|
||||
(Command('cd foo/bar/baz'), 'mkdir -p foo/bar/baz && cd foo/bar/baz')])
|
||||
def test_get_new_command(command, new_command):
|
||||
assert get_new_command(command, None) == new_command
|
||||
@@ -1,12 +1,12 @@
|
||||
from thefuck.types import Command
|
||||
from thefuck.rules.cd_parent import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
def test_match():
|
||||
assert match(Command('cd..', '', 'cd..: command not found'), None)
|
||||
assert not match(Command('', '', ''), None)
|
||||
assert match(Command('cd..', stderr='cd..: command not found'), None)
|
||||
assert not match(Command(), None)
|
||||
|
||||
|
||||
def test_get_new_command():
|
||||
assert get_new_command(
|
||||
Command('cd..', '', ''), None) == 'cd ..'
|
||||
Command('cd..'), None) == 'cd ..'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
from thefuck.types import Command
|
||||
from thefuck.rules.composer_not_command import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -36,13 +36,18 @@ def composer_not_command_one_of_this():
|
||||
|
||||
|
||||
def test_match(composer_not_command, composer_not_command_one_of_this):
|
||||
assert match(Command('composer udpate', '', composer_not_command), None)
|
||||
assert match(Command('composer pdate', '', composer_not_command_one_of_this), None)
|
||||
assert not match(Command('ls update', '', composer_not_command), None)
|
||||
assert match(Command('composer udpate',
|
||||
stderr=composer_not_command), None)
|
||||
assert match(Command('composer pdate',
|
||||
stderr=composer_not_command_one_of_this), None)
|
||||
assert not match(Command('ls update', stderr=composer_not_command),
|
||||
None)
|
||||
|
||||
|
||||
def test_get_new_command(composer_not_command, composer_not_command_one_of_this):
|
||||
assert get_new_command(Command('composer udpate', '', composer_not_command), None) \
|
||||
assert get_new_command(Command('composer udpate',
|
||||
stderr=composer_not_command), None) \
|
||||
== 'composer update'
|
||||
assert get_new_command(
|
||||
Command('composer pdate', '', composer_not_command_one_of_this), None) == 'composer selfupdate'
|
||||
Command('composer pdate', stderr=composer_not_command_one_of_this),
|
||||
None) == 'composer selfupdate'
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
|
||||
from thefuck.types import Command
|
||||
from thefuck.rules.fix_alt_space import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
def test_match():
|
||||
""" The character before 'grep' is Alt+Space, which happens frequently on the Mac when typing
|
||||
the pipe character (Alt+7), and holding the Alt key pressed for longer than necessary. """
|
||||
assert match(Command(u'ps -ef | grep foo', '', u'-bash: grep: command not found'), None)
|
||||
assert not match(Command('ps -ef | grep foo', '', ''), None)
|
||||
assert not match(Command('', '', ''), None)
|
||||
"""The character before 'grep' is Alt+Space, which happens frequently
|
||||
on the Mac when typing the pipe character (Alt+7), and holding the Alt
|
||||
key pressed for longer than necessary.
|
||||
|
||||
"""
|
||||
assert match(Command(u'ps -ef | grep foo',
|
||||
stderr=u'-bash: grep: command not found'), None)
|
||||
assert not match(Command('ps -ef | grep foo'), None)
|
||||
assert not match(Command(), None)
|
||||
|
||||
|
||||
def test_get_new_command():
|
||||
""" Replace the Alt+Space character by a simple space """
|
||||
assert get_new_command(Command(u'ps -ef | grep foo', '', ''), None) == 'ps -ef | grep foo'
|
||||
assert get_new_command(Command(u'ps -ef | grep foo'), None)\
|
||||
== 'ps -ef | grep foo'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
from thefuck.types import Command
|
||||
from thefuck.rules.git_not_command import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -31,14 +31,14 @@ def git_command():
|
||||
|
||||
|
||||
def test_match(git_not_command, git_command, git_not_command_one_of_this):
|
||||
assert match(Command('git brnch', '', git_not_command), None)
|
||||
assert match(Command('git st', '', git_not_command_one_of_this), None)
|
||||
assert not match(Command('ls brnch', '', git_not_command), None)
|
||||
assert not match(Command('git branch', '', git_command), None)
|
||||
assert match(Command('git brnch', stderr=git_not_command), None)
|
||||
assert match(Command('git st', stderr=git_not_command_one_of_this), None)
|
||||
assert not match(Command('ls brnch', stderr=git_not_command), None)
|
||||
assert not match(Command('git branch', stderr=git_command), None)
|
||||
|
||||
|
||||
def test_get_new_command(git_not_command, git_not_command_one_of_this):
|
||||
assert get_new_command(Command('git brnch', '', git_not_command), None)\
|
||||
assert get_new_command(Command('git brnch', stderr=git_not_command), None)\
|
||||
== 'git branch'
|
||||
assert get_new_command(
|
||||
Command('git st', '', git_not_command_one_of_this), None) == 'git status'
|
||||
assert get_new_command(Command('git st', stderr=git_not_command_one_of_this),
|
||||
None) == 'git status'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
from thefuck.types import Command
|
||||
from thefuck.rules.git_push import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -14,11 +14,11 @@ To push the current branch and set the remote as upstream, use
|
||||
|
||||
|
||||
def test_match(stderr):
|
||||
assert match(Command('git push master', '', stderr), None)
|
||||
assert not match(Command('git push master', '', ''), None)
|
||||
assert not match(Command('ls', '', stderr), None)
|
||||
assert match(Command('git push master', stderr=stderr), None)
|
||||
assert not match(Command('git push master'), None)
|
||||
assert not match(Command('ls', stderr=stderr), None)
|
||||
|
||||
|
||||
def test_get_new_command(stderr):
|
||||
assert get_new_command(Command('', '', stderr), None)\
|
||||
assert get_new_command(Command(stderr=stderr), None)\
|
||||
== "git push --set-upstream origin master"
|
||||
|
||||
13
tests/rules/test_ls_lah.py
Normal file
13
tests/rules/test_ls_lah.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from mock import patch, Mock
|
||||
from thefuck.rules.ls_lah import match, get_new_command
|
||||
|
||||
|
||||
def test_match():
|
||||
assert match(Mock(script='ls file.py'), None)
|
||||
assert match(Mock(script='ls /opt'), None)
|
||||
assert not match(Mock(script='ls -lah /opt'), None)
|
||||
|
||||
|
||||
def test_get_new_command():
|
||||
assert get_new_command(Mock(script='ls file.py'), None) == 'ls -lah file.py'
|
||||
assert get_new_command(Mock(script='ls'), None) == 'ls -lah'
|
||||
@@ -1,13 +1,22 @@
|
||||
from thefuck.types import Command
|
||||
import pytest
|
||||
from thefuck.rules.mkdir_p import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
def test_match():
|
||||
assert match(Command('mkdir foo/bar/baz', '', 'mkdir: foo/bar: No such file or directory'), None)
|
||||
assert not match(Command('mkdir foo/bar/baz', '', ''), None)
|
||||
assert not match(Command('mkdir foo/bar/baz', '', 'foo bar baz'), None)
|
||||
assert not match(Command('', '', ''), None)
|
||||
assert match(Command('mkdir foo/bar/baz',
|
||||
stderr='mkdir: foo/bar: No such file or directory'),
|
||||
None)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('mkdir foo/bar/baz'),
|
||||
Command('mkdir foo/bar/baz', stderr='foo bar baz'),
|
||||
Command()])
|
||||
def test_not_match(command):
|
||||
assert not match(command, None)
|
||||
|
||||
|
||||
def test_get_new_command():
|
||||
assert get_new_command(Command('mkdir foo/bar/baz', '', ''), None) == 'mkdir -p foo/bar/baz'
|
||||
assert get_new_command(Command('mkdir foo/bar/baz'), None)\
|
||||
== 'mkdir -p foo/bar/baz'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
from thefuck.types import Command
|
||||
from thefuck.rules.pip_unknown_command import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -14,11 +14,12 @@ def pip_unknown_cmd_without_recommend():
|
||||
|
||||
|
||||
def test_match(pip_unknown_cmd, pip_unknown_cmd_without_recommend):
|
||||
assert match(Command('pip instatl', '', pip_unknown_cmd), None)
|
||||
assert not match(Command('pip i', '', pip_unknown_cmd_without_recommend),
|
||||
assert match(Command('pip instatl', stderr=pip_unknown_cmd), None)
|
||||
assert not match(Command('pip i',
|
||||
stderr=pip_unknown_cmd_without_recommend),
|
||||
None)
|
||||
|
||||
|
||||
def test_get_new_command(pip_unknown_cmd):
|
||||
assert get_new_command(Command('pip instatl', '', pip_unknown_cmd), None)\
|
||||
== 'pip install'
|
||||
assert get_new_command(Command('pip instatl', stderr=pip_unknown_cmd),
|
||||
None) == 'pip install'
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
from thefuck.types import Command
|
||||
from thefuck.rules.python_command import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
def test_match():
|
||||
assert match(Command('temp.py', '', 'Permission denied'), None)
|
||||
assert not match(Command('', '', ''), None)
|
||||
assert match(Command('temp.py', stderr='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'
|
||||
assert get_new_command(Command('./test_sudo.py'), None)\
|
||||
== 'python ./test_sudo.py'
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
from thefuck.types import Command
|
||||
import pytest
|
||||
from thefuck.rules.rm_dir import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
def test_match():
|
||||
assert match(Command('rm foo', '', 'rm: foo: is a directory'), None)
|
||||
assert match(Command('rm foo', '', 'rm: foo: Is a directory'), None)
|
||||
assert not match(Command('rm foo', '', ''), None)
|
||||
assert not match(Command('rm foo', '', 'foo bar baz'), None)
|
||||
assert not match(Command('', '', ''), None)
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('rm foo', stderr='rm: foo: is a directory'),
|
||||
Command('rm foo', stderr='rm: foo: Is a directory')])
|
||||
def test_match(command):
|
||||
assert match(command, None)
|
||||
assert match(command, None)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('rm foo'), Command('rm foo'), Command()])
|
||||
def test_not_match(command):
|
||||
assert not match(command, None)
|
||||
|
||||
|
||||
def test_get_new_command():
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
from mock import Mock
|
||||
import pytest
|
||||
from thefuck.rules.rm_root import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
def test_match():
|
||||
assert match(Mock(script='rm -rf /',
|
||||
stderr='add --no-preserve-root'), None)
|
||||
assert not match(Mock(script='ls',
|
||||
stderr='add --no-preserve-root'), None)
|
||||
assert not match(Mock(script='rm --no-preserve-root /',
|
||||
stderr='add --no-preserve-root'), None)
|
||||
assert not match(Mock(script='rm -rf /',
|
||||
stderr=''), None)
|
||||
assert match(Command(script='rm -rf /',
|
||||
stderr='add --no-preserve-root'), None)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command(script='ls', stderr='add --no-preserve-root'),
|
||||
Command(script='rm --no-preserve-root /', stderr='add --no-preserve-root'),
|
||||
Command(script='rm -rf /', stderr='')])
|
||||
def test_not_match(command):
|
||||
assert not match(command, None)
|
||||
|
||||
|
||||
def test_get_new_command():
|
||||
assert get_new_command(Mock(script='rm -rf /'), None) \
|
||||
== 'rm -rf / --no-preserve-root'
|
||||
assert get_new_command(Command(script='rm -rf /'), None) \
|
||||
== 'rm -rf / --no-preserve-root'
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
|
||||
from thefuck.types import Command
|
||||
from thefuck.rules.sl_ls import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
def test_match():
|
||||
assert match(Command('sl', '', ''), None)
|
||||
assert not match(Command('ls', '', ''), None)
|
||||
assert match(Command('sl'), None)
|
||||
assert not match(Command('ls'), None)
|
||||
|
||||
|
||||
def test_get_new_command():
|
||||
assert get_new_command(Command('sl', '', ''), None) == 'ls'
|
||||
assert get_new_command(Command('sl'), None) == 'ls'
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import os
|
||||
import pytest
|
||||
from mock import Mock
|
||||
from thefuck.types import Command
|
||||
from thefuck.rules.ssh_known_hosts import match, get_new_command, remove_offending_keys
|
||||
from thefuck.rules.ssh_known_hosts import match, get_new_command,\
|
||||
remove_offending_keys
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -43,18 +44,18 @@ Host key verification failed.""".format(path, '98.765.432.321')
|
||||
|
||||
def test_match(ssh_error):
|
||||
errormsg, _, _, _ = ssh_error
|
||||
assert match(Command('ssh', '', errormsg), None)
|
||||
assert match(Command('ssh', '', errormsg), None)
|
||||
assert match(Command('scp something something', '', errormsg), None)
|
||||
assert match(Command('scp something something', '', errormsg), None)
|
||||
assert not match(Command('', '', errormsg), None)
|
||||
assert not match(Command('notssh', '', errormsg), None)
|
||||
assert not match(Command('ssh', '', ''), None)
|
||||
assert match(Command('ssh', stderr=errormsg), None)
|
||||
assert match(Command('ssh', stderr=errormsg), None)
|
||||
assert match(Command('scp something something', stderr=errormsg), None)
|
||||
assert match(Command('scp something something', stderr=errormsg), None)
|
||||
assert not match(Command(stderr=errormsg), None)
|
||||
assert not match(Command('notssh', stderr=errormsg), None)
|
||||
assert not match(Command('ssh'), None)
|
||||
|
||||
|
||||
def test_remove_offending_keys(ssh_error):
|
||||
errormsg, path, reset, known_hosts = ssh_error
|
||||
command = Command('ssh user@host', '', errormsg)
|
||||
command = Command('ssh user@host', stderr=errormsg)
|
||||
remove_offending_keys(command, None)
|
||||
expected = ['123.234.567.890 asdjkasjdakjsd\n', '111.222.333.444 qwepoiwqepoiss\n']
|
||||
assert known_hosts(path) == expected
|
||||
@@ -65,5 +66,5 @@ def test_get_new_command(ssh_error, monkeypatch):
|
||||
|
||||
method = Mock()
|
||||
monkeypatch.setattr('thefuck.rules.ssh_known_hosts.remove_offending_keys', method)
|
||||
assert get_new_command(Command('ssh user@host', '', errormsg), None) == 'ssh user@host'
|
||||
assert get_new_command(Command('ssh user@host', stderr=errormsg), None) == 'ssh user@host'
|
||||
assert method.call_count
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
from thefuck.types import Command
|
||||
import pytest
|
||||
from thefuck.rules.sudo import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
def test_match():
|
||||
assert match(Command('', '', 'Permission denied'), None)
|
||||
assert match(Command('', '', 'permission denied'), None)
|
||||
assert match(Command('', '', "npm ERR! Error: EACCES, unlink"), None)
|
||||
assert not match(Command('', '', ''), None)
|
||||
@pytest.mark.parametrize('stderr, stdout', [
|
||||
('Permission denied', ''),
|
||||
('permission denied', ''),
|
||||
("npm ERR! Error: EACCES, unlink", ''),
|
||||
('requested operation requires superuser privilege', ''),
|
||||
('', "error: [Errno 13] Permission denied: '/usr/local/lib/python2.7/dist-packages/ipaddr.py'")])
|
||||
def test_match(stderr, stdout):
|
||||
assert match(Command(stderr=stderr, stdout=stdout), None)
|
||||
|
||||
|
||||
def test_not_match():
|
||||
assert not match(Command(), None)
|
||||
|
||||
|
||||
def test_get_new_command():
|
||||
assert get_new_command(Command('ls', '', ''), None) == 'sudo ls'
|
||||
assert get_new_command(Command('ls'), None) == 'sudo ls'
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from mock import Mock
|
||||
import pytest
|
||||
from thefuck.rules import switch_lang
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
def test_match():
|
||||
assert switch_lang.match(Mock(stderr='command not found: фзе-пуе',
|
||||
script=u'фзе-пуе'), None)
|
||||
assert switch_lang.match(Mock(stderr='command not found: λσ',
|
||||
script=u'λσ'), None)
|
||||
|
||||
assert not switch_lang.match(Mock(stderr='command not found: pat-get',
|
||||
script=u'pat-get'), None)
|
||||
assert not switch_lang.match(Mock(stderr='command not found: ls',
|
||||
script=u'ls'), None)
|
||||
assert not switch_lang.match(Mock(stderr='some info',
|
||||
script=u'фзе-пуе'), None)
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command(stderr='command not found: фзе-пуе', script=u'фзе-пуе'),
|
||||
Command(stderr='command not found: λσ', script=u'λσ')])
|
||||
def test_match(command):
|
||||
assert switch_lang.match(command, None)
|
||||
|
||||
|
||||
def test_get_new_command():
|
||||
assert switch_lang.get_new_command(
|
||||
Mock(script=u'фзе-пуе штыефдд мшь'), None) == 'apt-get install vim'
|
||||
assert switch_lang.get_new_command(
|
||||
Mock(script=u'λσ -λα'), None) == 'ls -la'
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command(stderr='command not found: pat-get', script=u'pat-get'),
|
||||
Command(stderr='command not found: ls', script=u'ls'),
|
||||
Command(stderr='some info', script=u'фзе-пуе')])
|
||||
def test_not_match(command):
|
||||
assert not switch_lang.match(command, None)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, new_command', [
|
||||
(Command(u'фзе-пуе штыефдд мшь'), 'apt-get install vim'),
|
||||
(Command(u'λσ -λα'), 'ls -la')])
|
||||
def test_get_new_command(command, new_command):
|
||||
assert switch_lang.get_new_command(command, None) == new_command
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import six
|
||||
from mock import patch, Mock
|
||||
from thefuck.types import Rule
|
||||
from thefuck import conf
|
||||
from tests.utils import Rule
|
||||
|
||||
|
||||
def test_default():
|
||||
assert Rule('test', None, None, True) in conf.DEFAULT_RULES
|
||||
assert Rule('test', None, None, False) not in conf.DEFAULT_RULES
|
||||
assert Rule('test', None, None, False) in (conf.DEFAULT_RULES + ['test'])
|
||||
assert Rule('test', enabled_by_default=True) in conf.DEFAULT_RULES
|
||||
assert Rule('test', enabled_by_default=False) not in conf.DEFAULT_RULES
|
||||
assert Rule('test', enabled_by_default=False) in (conf.DEFAULT_RULES + ['test'])
|
||||
|
||||
|
||||
def test_settings_defaults():
|
||||
@@ -59,3 +60,30 @@ 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_initialize_settings_file_ignore_if_exists():
|
||||
settings_path_mock = Mock(is_file=Mock(return_value=True), open=Mock())
|
||||
user_dir_mock = Mock(joinpath=Mock(return_value=settings_path_mock))
|
||||
conf.initialize_settings_file(user_dir_mock)
|
||||
assert settings_path_mock.is_file.call_count == 1
|
||||
assert not settings_path_mock.open.called
|
||||
|
||||
|
||||
def test_initialize_settings_file_create_if_exists_not():
|
||||
settings_file = six.StringIO()
|
||||
settings_path_mock = Mock(
|
||||
is_file=Mock(return_value=False),
|
||||
open=Mock(return_value=Mock(
|
||||
__exit__=lambda *args: None, __enter__=lambda *args: settings_file
|
||||
)),
|
||||
)
|
||||
user_dir_mock = Mock(joinpath=Mock(return_value=settings_path_mock))
|
||||
conf.initialize_settings_file(user_dir_mock)
|
||||
settings_file_contents = settings_file.getvalue()
|
||||
assert settings_path_mock.is_file.call_count == 1
|
||||
assert settings_path_mock.open.call_count == 1
|
||||
assert conf.SETTINGS_HEADER in settings_file_contents
|
||||
for setting in conf.DEFAULT_SETTINGS.items():
|
||||
assert '# {} = {}\n'.format(*setting) in settings_file_contents
|
||||
settings_file.close()
|
||||
|
||||
@@ -2,6 +2,7 @@ from subprocess import PIPE
|
||||
from pathlib import PosixPath, Path
|
||||
from mock import patch, Mock
|
||||
from thefuck import main, conf, types
|
||||
from tests.utils import Rule, Command
|
||||
|
||||
|
||||
def test_load_rule():
|
||||
@@ -13,7 +14,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')) \
|
||||
== types.Rule('bash', match, get_new_command, True)
|
||||
== Rule('bash', match, get_new_command)
|
||||
load_source.assert_called_once_with('bash', '/rules/bash.py')
|
||||
|
||||
|
||||
@@ -26,15 +27,15 @@ def test_get_rules():
|
||||
assert list(main.get_rules(
|
||||
Path('~'),
|
||||
Mock(rules=conf.DEFAULT_RULES))) \
|
||||
== [types.Rule('bash', 'bash', 'bash', True),
|
||||
types.Rule('lisp', 'lisp', 'lisp', True),
|
||||
types.Rule('bash', 'bash', 'bash', True),
|
||||
types.Rule('lisp', 'lisp', 'lisp', True)]
|
||||
== [Rule('bash', 'bash', 'bash'),
|
||||
Rule('lisp', 'lisp', 'lisp'),
|
||||
Rule('bash', 'bash', 'bash'),
|
||||
Rule('lisp', 'lisp', 'lisp')]
|
||||
assert list(main.get_rules(
|
||||
Path('~'),
|
||||
Mock(rules=types.RulesNamesList(['bash'])))) \
|
||||
== [types.Rule('bash', 'bash', 'bash', True),
|
||||
types.Rule('bash', 'bash', 'bash', True)]
|
||||
== [Rule('bash', 'bash', 'bash'),
|
||||
Rule('bash', 'bash', 'bash')]
|
||||
|
||||
|
||||
def test_get_command():
|
||||
@@ -47,7 +48,7 @@ def test_get_command():
|
||||
Popen.return_value.stderr.read.return_value = b'stderr'
|
||||
assert main.get_command(Mock(), ['thefuck', 'apt-get',
|
||||
'search', 'vim']) \
|
||||
== types.Command('apt-get search vim', 'stdout', 'stderr')
|
||||
== Command('apt-get search vim', 'stdout', 'stderr')
|
||||
Popen.assert_called_once_with('apt-get search vim',
|
||||
shell=True,
|
||||
stdout=PIPE,
|
||||
@@ -57,12 +58,12 @@ def test_get_command():
|
||||
|
||||
|
||||
def test_get_matched_rule(capsys):
|
||||
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 = [Rule('', lambda x, _: x.script == 'cd ..'),
|
||||
Rule('', lambda *_: False),
|
||||
Rule('rule', Mock(side_effect=OSError('Denied')))]
|
||||
assert main.get_matched_rule(Command('ls'),
|
||||
rules, Mock(no_colors=True)) is None
|
||||
assert main.get_matched_rule(types.Command('cd ..', '', ''),
|
||||
assert main.get_matched_rule(Command('cd ..'),
|
||||
rules, Mock(no_colors=True)) == rules[0]
|
||||
assert capsys.readouterr()[1].split('\n')[0] \
|
||||
== '[WARN] Rule rule:'
|
||||
@@ -70,11 +71,11 @@ def test_get_matched_rule(capsys):
|
||||
|
||||
def test_run_rule(capsys):
|
||||
with patch('thefuck.main.confirm', return_value=True):
|
||||
main.run_rule(types.Rule('', None, lambda *_: 'new-command', True),
|
||||
main.run_rule(Rule(get_new_command=lambda *_: 'new-command'),
|
||||
None, None)
|
||||
assert capsys.readouterr() == ('new-command\n', '')
|
||||
with patch('thefuck.main.confirm', return_value=False):
|
||||
main.run_rule(types.Rule('', None, lambda *_: 'new-command', True),
|
||||
main.run_rule(Rule(get_new_command=lambda *_: 'new-command'),
|
||||
None, None)
|
||||
assert capsys.readouterr() == ('', '')
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from mock import Mock
|
||||
from thefuck.utils import sudo_support, wrap_settings
|
||||
from thefuck.types import Command, Settings
|
||||
from thefuck.types import Settings
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
def test_wrap_settings():
|
||||
@@ -13,13 +14,13 @@ def test_wrap_settings():
|
||||
|
||||
def test_sudo_support():
|
||||
fn = Mock(return_value=True, __name__='')
|
||||
assert sudo_support(fn)(Command('sudo ls', 'out', 'err'), None)
|
||||
fn.assert_called_once_with(Command('ls', 'out', 'err'), None)
|
||||
assert sudo_support(fn)(Command('sudo ls'), None)
|
||||
fn.assert_called_once_with(Command('ls'), None)
|
||||
|
||||
fn.return_value = False
|
||||
assert not sudo_support(fn)(Command('sudo ls', 'out', 'err'), None)
|
||||
assert not sudo_support(fn)(Command('sudo ls'), None)
|
||||
|
||||
fn.return_value = 'pwd'
|
||||
assert sudo_support(fn)(Command('sudo ls', 'out', 'err'), None) == 'sudo pwd'
|
||||
assert sudo_support(fn)(Command('sudo ls'), None) == 'sudo pwd'
|
||||
|
||||
assert sudo_support(fn)(Command('ls', 'out', 'err'), None) == 'pwd'
|
||||
assert sudo_support(fn)(Command('ls'), None) == 'pwd'
|
||||
|
||||
11
tests/utils.py
Normal file
11
tests/utils.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from thefuck import types
|
||||
|
||||
|
||||
def Command(script='', stdout='', stderr=''):
|
||||
return types.Command(script, stdout, stderr)
|
||||
|
||||
|
||||
def Rule(name='', match=lambda *_: True,
|
||||
get_new_command=lambda *_: '',
|
||||
enabled_by_default=True):
|
||||
return types.Rule(name, match, get_new_command, enabled_by_default)
|
||||
@@ -35,6 +35,19 @@ ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
|
||||
'THEFUCK_NO_COLORS': 'no_colors'}
|
||||
|
||||
|
||||
SETTINGS_HEADER = u"""# ~/.thefuck/settings.py: The Fuck settings file
|
||||
#
|
||||
# The rules are defined as in the example bellow:
|
||||
#
|
||||
# rules = ['cd_parent', 'git_push', 'python_command', 'sudo']
|
||||
#
|
||||
# The default values are as follows. Uncomment and change to fit your needs.
|
||||
# See https://github.com/nvbn/thefuck#settings for more information.
|
||||
#
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def _settings_from_file(user_dir):
|
||||
"""Loads settings from file."""
|
||||
settings = load_source('settings',
|
||||
@@ -92,3 +105,12 @@ def get_settings(user_dir):
|
||||
conf['rules'] = types.RulesNamesList(conf['rules'])
|
||||
|
||||
return types.Settings(conf)
|
||||
|
||||
|
||||
def initialize_settings_file(user_dir):
|
||||
settings_path = user_dir.joinpath('settings.py')
|
||||
if not settings_path.is_file():
|
||||
with settings_path.open(mode='w') as settings_file:
|
||||
settings_file.write(SETTINGS_HEADER)
|
||||
for setting in DEFAULT_SETTINGS.items():
|
||||
settings_file.write(u'# {} = {}\n'.format(*setting))
|
||||
|
||||
@@ -15,7 +15,7 @@ def setup_user_dir():
|
||||
rules_dir = user_dir.joinpath('rules')
|
||||
if not rules_dir.is_dir():
|
||||
rules_dir.mkdir(parents=True)
|
||||
user_dir.joinpath('settings.py').touch()
|
||||
conf.initialize_settings_file(user_dir)
|
||||
return user_dir
|
||||
|
||||
|
||||
|
||||
23
thefuck/rules/apt_get.py
Normal file
23
thefuck/rules/apt_get.py
Normal file
@@ -0,0 +1,23 @@
|
||||
try:
|
||||
import CommandNotFound
|
||||
except ImportError:
|
||||
enabled_by_default = False
|
||||
|
||||
|
||||
def match(command, settings):
|
||||
if 'not found' in command.stderr:
|
||||
try:
|
||||
c = CommandNotFound.CommandNotFound()
|
||||
pkgs = c.getPackages(command.script.split(" ")[0])
|
||||
name, _ = pkgs[0]
|
||||
return True
|
||||
except IndexError:
|
||||
# IndexError is thrown when no matching package is found
|
||||
return False
|
||||
|
||||
|
||||
def get_new_command(command, settings):
|
||||
c = CommandNotFound.CommandNotFound()
|
||||
pkgs = c.getPackages(command.script.split(" ")[0])
|
||||
name, _ = pkgs[0]
|
||||
return "sudo apt-get install {} && {}".format(name, command.script)
|
||||
14
thefuck/rules/cd_mkdir.py
Normal file
14
thefuck/rules/cd_mkdir.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import re
|
||||
from thefuck.utils import sudo_support
|
||||
|
||||
|
||||
@sudo_support
|
||||
def match(command, settings):
|
||||
return (command.script.startswith('cd ')
|
||||
and ('no such file or directory' in command.stderr.lower()
|
||||
or 'cd: can\'t cd to' in command.stderr.lower()))
|
||||
|
||||
|
||||
@sudo_support
|
||||
def get_new_command(command, settings):
|
||||
return re.sub(r'^cd (.*)', 'mkdir -p \\1 && cd \\1', command.script)
|
||||
11
thefuck/rules/ls_lah.py
Normal file
11
thefuck/rules/ls_lah.py
Normal file
@@ -0,0 +1,11 @@
|
||||
enabled_by_default = False
|
||||
|
||||
|
||||
def match(command, settings):
|
||||
return 'ls' in command.script and not ('ls -' in command.script)
|
||||
|
||||
|
||||
def get_new_command(command, settings):
|
||||
command = command.script.split(' ')
|
||||
command[0] = 'ls -lah'
|
||||
return ' '.join(command)
|
||||
@@ -7,12 +7,14 @@ patterns = ['permission denied',
|
||||
'root privilege',
|
||||
'This command has to be run under the root user.',
|
||||
'This operation requires root.',
|
||||
'You need to be root to perform this command.']
|
||||
'You need to be root to perform this command.',
|
||||
'requested operation requires superuser privilege']
|
||||
|
||||
|
||||
def match(command, settings):
|
||||
for pattern in patterns:
|
||||
if pattern.lower() in command.stderr.lower():
|
||||
if pattern.lower() in command.stderr.lower()\
|
||||
or pattern.lower() in command.stdout.lower():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user