mirror of
https://github.com/nvbn/thefuck.git
synced 2025-03-20 01:28:56 +00:00
Merge branch 'master' into rule_for_branch_dash_0
This commit is contained in:
commit
18227fda6d
13
README.md
13
README.md
@ -114,8 +114,7 @@ sudo pip3 install thefuck
|
|||||||
|
|
||||||
On FreeBSD, install *The Fuck* with the following commands:
|
On FreeBSD, install *The Fuck* with the following commands:
|
||||||
```bash
|
```bash
|
||||||
sudo portsnap fetch update
|
pkg install thefuck
|
||||||
cd /usr/ports/misc/thefuck && sudo make install clean
|
|
||||||
```
|
```
|
||||||
|
|
||||||
On ChromeOS, install *The Fuck* using [chromebrew](https://github.com/skycocker/chromebrew) with the following command:
|
On ChromeOS, install *The Fuck* using [chromebrew](https://github.com/skycocker/chromebrew) with the following command:
|
||||||
@ -190,7 +189,9 @@ following rules are enabled by default:
|
|||||||
* `dirty_unzip` – fixes `unzip` command that unzipped in the current directory;
|
* `dirty_unzip` – fixes `unzip` command that unzipped in the current directory;
|
||||||
* `django_south_ghost` – adds `--delete-ghost-migrations` to failed because ghosts django south migration;
|
* `django_south_ghost` – adds `--delete-ghost-migrations` to failed because ghosts django south migration;
|
||||||
* `django_south_merge` – adds `--merge` to inconsistent django south migration;
|
* `django_south_merge` – adds `--merge` to inconsistent django south migration;
|
||||||
|
* `docker_login` – executes a `docker login` and repeats the previous command;
|
||||||
* `docker_not_command` – fixes wrong docker commands like `docker tags`;
|
* `docker_not_command` – fixes wrong docker commands like `docker tags`;
|
||||||
|
* `docker_image_being_used_by_container` ‐ removes the container that is using the image before removing the image;
|
||||||
* `dry` – fixes repetitions like `git git push`;
|
* `dry` – fixes repetitions like `git git push`;
|
||||||
* `fab_command_not_found` – fix misspelled fabric commands;
|
* `fab_command_not_found` – fix misspelled fabric commands;
|
||||||
* `fix_alt_space` – replaces Alt+Space with Space character;
|
* `fix_alt_space` – replaces Alt+Space with Space character;
|
||||||
@ -266,10 +267,12 @@ following rules are enabled by default:
|
|||||||
* `no_command` – fixes wrong console commands, for example `vom/vim`;
|
* `no_command` – fixes wrong console commands, for example `vom/vim`;
|
||||||
* `no_such_file` – creates missing directories with `mv` and `cp` commands;
|
* `no_such_file` – creates missing directories with `mv` and `cp` commands;
|
||||||
* `open` – either prepends `http://` to address passed to `open` or create a new file or directory and passes it to `open`;
|
* `open` – either prepends `http://` to address passed to `open` or create a new file or directory and passes it to `open`;
|
||||||
|
* `pip_install` – fixes permission issues with `pip install` commands by adding `--user` or prepending `sudo` if necessary;
|
||||||
* `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`;
|
||||||
* `php_s` – replaces `-s` by `-S` when trying to run a local php server;
|
* `php_s` – replaces `-s` by `-S` when trying to run a local php server;
|
||||||
* `port_already_in_use` – kills process that bound port;
|
* `port_already_in_use` – kills process that bound port;
|
||||||
* `prove_recursively` – adds `-r` when called with directory;
|
* `prove_recursively` – adds `-r` when called with directory;
|
||||||
|
* `pyenv_no_such_command` – fixes wrong pyenv commands like `pyenv isntall` or `pyenv list`;
|
||||||
* `python_command` – prepends `python` when you try to run non-executable/without `./` python script;
|
* `python_command` – prepends `python` when you try to run non-executable/without `./` python script;
|
||||||
* `python_execute` – appends missing `.py` when executing Python files;
|
* `python_execute` – appends missing `.py` when executing Python files;
|
||||||
* `quotation_marks` – fixes uneven usage of `'` and `"` when containing args';
|
* `quotation_marks` – fixes uneven usage of `'` and `"` when containing args';
|
||||||
@ -285,6 +288,7 @@ following rules are enabled by default:
|
|||||||
* `sudo_command_from_user_path` – runs commands from users `$PATH` with `sudo`;
|
* `sudo_command_from_user_path` – runs commands from users `$PATH` with `sudo`;
|
||||||
* `switch_lang` – switches command from your local layout to en;
|
* `switch_lang` – switches command from your local layout to en;
|
||||||
* `systemctl` – correctly orders parameters of confusing `systemctl`;
|
* `systemctl` – correctly orders parameters of confusing `systemctl`;
|
||||||
|
* `terraform_init.py` – run `terraform init` before plan or apply;
|
||||||
* `test.py` – runs `py.test` instead of `test.py`;
|
* `test.py` – runs `py.test` instead of `test.py`;
|
||||||
* `touch` – creates missing directories before "touching";
|
* `touch` – creates missing directories before "touching";
|
||||||
* `tsuru_login` – runs `tsuru login` if not authenticated or session expired;
|
* `tsuru_login` – runs `tsuru login` if not authenticated or session expired;
|
||||||
@ -315,8 +319,9 @@ The following rules are enabled by default on specific platforms only:
|
|||||||
* `brew_unknown_command` – fixes wrong brew commands, for example `brew docto/brew doctor`;
|
* `brew_unknown_command` – fixes wrong brew commands, for example `brew docto/brew doctor`;
|
||||||
* `brew_update_formula` – turns `brew update <formula>` into `brew upgrade <formula>`;
|
* `brew_update_formula` – turns `brew update <formula>` into `brew upgrade <formula>`;
|
||||||
* `dnf_no_such_command` – fixes mistyped DNF commands;
|
* `dnf_no_such_command` – fixes mistyped DNF commands;
|
||||||
* `pacman` – installs app with `pacman` if it is not installed (uses `yaourt` if available);
|
* `nixos_cmd_not_found` – installs apps on NixOS;
|
||||||
* `pacman_not_found` – fixes package name with `pacman` or `yaourt`.
|
* `pacman` – installs app with `pacman` if it is not installed (uses `yay` or `yaourt` if available);
|
||||||
|
* `pacman_not_found` – fixes package name with `pacman`, `yay` or `yaourt`.
|
||||||
|
|
||||||
The following commands are bundled with *The Fuck*, but are not enabled by
|
The following commands are bundled with *The Fuck*, but are not enabled by
|
||||||
default:
|
default:
|
||||||
|
@ -32,6 +32,6 @@ call('git push --tags', shell=True)
|
|||||||
|
|
||||||
env = os.environ
|
env = os.environ
|
||||||
env['CONVERT_README'] = 'true'
|
env['CONVERT_README'] = 'true'
|
||||||
call('rm -rf dist/*')
|
call('rm -rf dist/*', shell=True, env=env)
|
||||||
call('python setup.py sdist bdist_wheel', shell=True, env=env)
|
call('python setup.py sdist bdist_wheel', shell=True, env=env)
|
||||||
call('twine upload dist/*', shell=True, env=env)
|
call('twine upload dist/*', shell=True, env=env)
|
||||||
|
2
setup.py
2
setup.py
@ -31,7 +31,7 @@ elif (3, 0) < version < (3, 4):
|
|||||||
' ({}.{} detected).'.format(*version))
|
' ({}.{} detected).'.format(*version))
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
VERSION = '3.28'
|
VERSION = '3.29'
|
||||||
|
|
||||||
install_requires = ['psutil', 'colorama', 'six', 'decorator', 'pyte']
|
install_requires = ['psutil', 'colorama', 'six', 'decorator', 'pyte']
|
||||||
extras_require = {':python_version<"3.4"': ['pathlib2'],
|
extras_require = {':python_version<"3.4"': ['pathlib2'],
|
||||||
|
27
tests/rules/test_docker_image_being_used_by_container.py
Normal file
27
tests/rules/test_docker_image_being_used_by_container.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from thefuck.rules.docker_image_being_used_by_container import match, get_new_command
|
||||||
|
from thefuck.types import Command
|
||||||
|
|
||||||
|
|
||||||
|
def test_match():
|
||||||
|
err_response = """Error response from daemon: conflict: unable to delete cd809b04b6ff (cannot be forced) - image is being used by running container e5e2591040d1"""
|
||||||
|
assert match(Command('docker image rm -f cd809b04b6ff', err_response))
|
||||||
|
|
||||||
|
|
||||||
|
def test_not_match():
|
||||||
|
err_response = 'bash: docker: command not found'
|
||||||
|
assert not match(Command('docker image rm -f cd809b04b6ff', err_response))
|
||||||
|
|
||||||
|
|
||||||
|
def test_not_docker_command():
|
||||||
|
err_response = """Error response from daemon: conflict: unable to delete cd809b04b6ff (cannot be forced) - image is being used by running container e5e2591040d1"""
|
||||||
|
assert not match(Command('git image rm -f cd809b04b6ff', err_response))
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_new_command():
|
||||||
|
err_response = """
|
||||||
|
Error response from daemon: conflict: unable to delete cd809b04b6ff (cannot be forced) - image
|
||||||
|
is being used by running container e5e2591040d1
|
||||||
|
"""
|
||||||
|
result = get_new_command(Command('docker image rm -f cd809b04b6ff', err_response))
|
||||||
|
expected = 'docker container rm -f e5e2591040d1 && docker image rm -f cd809b04b6ff'
|
||||||
|
assert result == expected
|
37
tests/rules/test_docker_login.py
Normal file
37
tests/rules/test_docker_login.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from thefuck.rules.docker_login import match, get_new_command
|
||||||
|
from thefuck.types import Command
|
||||||
|
|
||||||
|
|
||||||
|
def test_match():
|
||||||
|
err_response1 = """
|
||||||
|
Sending build context to Docker daemon 118.8kB
|
||||||
|
Step 1/6 : FROM foo/bar:fdb7c6d
|
||||||
|
pull access denied for foo/bar, repository does not exist or may require 'docker login'
|
||||||
|
"""
|
||||||
|
assert match(Command('docker build -t artifactory:9090/foo/bar:fdb7c6d .', err_response1))
|
||||||
|
|
||||||
|
err_response2 = """
|
||||||
|
The push refers to repository [artifactory:9090/foo/bar]
|
||||||
|
push access denied for foo/bar, repository does not exist or may require 'docker login'
|
||||||
|
"""
|
||||||
|
assert match(Command('docker push artifactory:9090/foo/bar:fdb7c6d', err_response2))
|
||||||
|
|
||||||
|
err_response3 = """
|
||||||
|
docker push artifactory:9090/foo/bar:fdb7c6d
|
||||||
|
The push refers to repository [artifactory:9090/foo/bar]
|
||||||
|
9c29c7ad209d: Preparing
|
||||||
|
71f3ad53dfe0: Preparing
|
||||||
|
f58ee068224c: Preparing
|
||||||
|
aeddc924d0f7: Preparing
|
||||||
|
c2040e5d6363: Preparing
|
||||||
|
4d42df4f350f: Preparing
|
||||||
|
35723dab26f9: Preparing
|
||||||
|
71f3ad53dfe0: Pushed
|
||||||
|
cb95fa0faeb1: Layer already exists
|
||||||
|
"""
|
||||||
|
assert not match(Command('docker push artifactory:9090/foo/bar:fdb7c6d', err_response3))
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_new_command():
|
||||||
|
assert get_new_command(Command('docker build -t artifactory:9090/foo/bar:fdb7c6d .', '')) == 'docker login && docker build -t artifactory:9090/foo/bar:fdb7c6d .'
|
||||||
|
assert get_new_command(Command('docker push artifactory:9090/foo/bar:fdb7c6d', '')) == 'docker login && docker push artifactory:9090/foo/bar:fdb7c6d'
|
25
tests/rules/test_nixos_cmd_not_found.py
Normal file
25
tests/rules/test_nixos_cmd_not_found.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import pytest
|
||||||
|
from thefuck.rules.nixos_cmd_not_found import match, get_new_command
|
||||||
|
from thefuck.types import Command
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('command', [
|
||||||
|
Command('vim', 'nix-env -iA nixos.vim')])
|
||||||
|
def test_match(mocker, command):
|
||||||
|
mocker.patch('thefuck.rules.nixos_cmd_not_found', return_value=None)
|
||||||
|
assert match(command)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('command', [
|
||||||
|
Command('vim', ''),
|
||||||
|
Command('', '')])
|
||||||
|
def test_not_match(mocker, command):
|
||||||
|
mocker.patch('thefuck.rules.nixos_cmd_not_found', return_value=None)
|
||||||
|
assert not match(command)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('command, new_command', [
|
||||||
|
(Command('vim', 'nix-env -iA nixos.vim'), 'nix-env -iA nixos.vim && vim'),
|
||||||
|
(Command('pacman', 'nix-env -iA nixos.pacman'), 'nix-env -iA nixos.pacman && pacman')])
|
||||||
|
def test_get_new_command(mocker, command, new_command):
|
||||||
|
assert get_new_command(command) == new_command
|
@ -6,7 +6,7 @@ from thefuck.types import Command
|
|||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def get_all_executables(mocker):
|
def get_all_executables(mocker):
|
||||||
mocker.patch('thefuck.rules.no_command.get_all_executables',
|
mocker.patch('thefuck.rules.no_command.get_all_executables',
|
||||||
return_value=['vim', 'fsck', 'git', 'go'])
|
return_value=['vim', 'fsck', 'git', 'go', 'python'])
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
@ -20,6 +20,7 @@ def history_without_current(mocker):
|
|||||||
@pytest.mark.parametrize('script, output', [
|
@pytest.mark.parametrize('script, output', [
|
||||||
('vom file.py', 'vom: not found'),
|
('vom file.py', 'vom: not found'),
|
||||||
('fucck', 'fucck: not found'),
|
('fucck', 'fucck: not found'),
|
||||||
|
('puthon', "'puthon' is not recognized as an internal or external command"),
|
||||||
('got commit', 'got: command not found')])
|
('got commit', 'got: command not found')])
|
||||||
def test_match(mocker, script, output):
|
def test_match(mocker, script, output):
|
||||||
mocker.patch('thefuck.rules.no_command.which', return_value=None)
|
mocker.patch('thefuck.rules.no_command.which', return_value=None)
|
||||||
|
@ -11,6 +11,7 @@ extra/llvm35 3.5.2-13/usr/bin/llc'''
|
|||||||
@pytest.mark.skipif(not getattr(pacman_not_found, 'enabled_by_default', True),
|
@pytest.mark.skipif(not getattr(pacman_not_found, 'enabled_by_default', True),
|
||||||
reason='Skip if pacman is not available')
|
reason='Skip if pacman is not available')
|
||||||
@pytest.mark.parametrize('command', [
|
@pytest.mark.parametrize('command', [
|
||||||
|
Command('yay -S llc', 'error: target not found: llc'),
|
||||||
Command('yaourt -S llc', 'error: target not found: llc'),
|
Command('yaourt -S llc', 'error: target not found: llc'),
|
||||||
Command('pacman llc', 'error: target not found: llc'),
|
Command('pacman llc', 'error: target not found: llc'),
|
||||||
Command('sudo pacman llc', 'error: target not found: llc')])
|
Command('sudo pacman llc', 'error: target not found: llc')])
|
||||||
@ -19,6 +20,7 @@ def test_match(command):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('command', [
|
@pytest.mark.parametrize('command', [
|
||||||
|
Command('yay -S llc', 'error: target not found: llc'),
|
||||||
Command('yaourt -S llc', 'error: target not found: llc'),
|
Command('yaourt -S llc', 'error: target not found: llc'),
|
||||||
Command('pacman llc', 'error: target not found: llc'),
|
Command('pacman llc', 'error: target not found: llc'),
|
||||||
Command('sudo pacman llc', 'error: target not found: llc')])
|
Command('sudo pacman llc', 'error: target not found: llc')])
|
||||||
@ -31,6 +33,7 @@ def test_match_mocked(subp_mock, command):
|
|||||||
@pytest.mark.skipif(not getattr(pacman_not_found, 'enabled_by_default', True),
|
@pytest.mark.skipif(not getattr(pacman_not_found, 'enabled_by_default', True),
|
||||||
reason='Skip if pacman is not available')
|
reason='Skip if pacman is not available')
|
||||||
@pytest.mark.parametrize('command, fixed', [
|
@pytest.mark.parametrize('command, fixed', [
|
||||||
|
(Command('yay -S llc', 'error: target not found: llc'), ['yay -S extra/llvm', 'yay -S extra/llvm35']),
|
||||||
(Command('yaourt -S llc', 'error: target not found: llc'), ['yaourt -S extra/llvm', 'yaourt -S extra/llvm35']),
|
(Command('yaourt -S llc', 'error: target not found: llc'), ['yaourt -S extra/llvm', 'yaourt -S extra/llvm35']),
|
||||||
(Command('pacman -S llc', 'error: target not found: llc'), ['pacman -S extra/llvm', 'pacman -S extra/llvm35']),
|
(Command('pacman -S llc', 'error: target not found: llc'), ['pacman -S extra/llvm', 'pacman -S extra/llvm35']),
|
||||||
(Command('sudo pacman -S llc', 'error: target not found: llc'), ['sudo pacman -S extra/llvm', 'sudo pacman -S extra/llvm35'])])
|
(Command('sudo pacman -S llc', 'error: target not found: llc'), ['sudo pacman -S extra/llvm', 'sudo pacman -S extra/llvm35'])])
|
||||||
@ -39,6 +42,7 @@ def test_get_new_command(command, fixed):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('command, fixed', [
|
@pytest.mark.parametrize('command, fixed', [
|
||||||
|
(Command('yay -S llc', 'error: target not found: llc'), ['yay -S extra/llvm', 'yay -S extra/llvm35']),
|
||||||
(Command('yaourt -S llc', 'error: target not found: llc'), ['yaourt -S extra/llvm', 'yaourt -S extra/llvm35']),
|
(Command('yaourt -S llc', 'error: target not found: llc'), ['yaourt -S extra/llvm', 'yaourt -S extra/llvm35']),
|
||||||
(Command('pacman -S llc', 'error: target not found: llc'), ['pacman -S extra/llvm', 'pacman -S extra/llvm35']),
|
(Command('pacman -S llc', 'error: target not found: llc'), ['pacman -S extra/llvm', 'pacman -S extra/llvm35']),
|
||||||
(Command('sudo pacman -S llc', 'error: target not found: llc'), ['sudo pacman -S extra/llvm', 'sudo pacman -S extra/llvm35'])])
|
(Command('sudo pacman -S llc', 'error: target not found: llc'), ['sudo pacman -S extra/llvm', 'sudo pacman -S extra/llvm35'])])
|
||||||
|
27
tests/rules/test_pip_install.py
Normal file
27
tests/rules/test_pip_install.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: UTF-8 -*-
|
||||||
|
from thefuck.rules.pip_install import match, get_new_command
|
||||||
|
from thefuck.types import Command
|
||||||
|
|
||||||
|
|
||||||
|
def test_match():
|
||||||
|
response1 = """
|
||||||
|
Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: '/Library/Python/2.7/site-packages/entrypoints.pyc'
|
||||||
|
Consider using the `--user` option or check the permissions.
|
||||||
|
"""
|
||||||
|
assert match(Command('pip install -r requirements.txt', response1))
|
||||||
|
|
||||||
|
response2 = """
|
||||||
|
Collecting bacon
|
||||||
|
Downloading https://files.pythonhosted.org/packages/b2/81/19fb79139ee71c8bc4e5a444546f318e2b87253b8939ec8a7e10d63b7341/bacon-0.3.1.zip (11.0MB)
|
||||||
|
100% |████████████████████████████████| 11.0MB 3.0MB/s
|
||||||
|
Installing collected packages: bacon
|
||||||
|
Running setup.py install for bacon ... done
|
||||||
|
Successfully installed bacon-0.3.1
|
||||||
|
"""
|
||||||
|
assert not match(Command('pip install bacon', response2))
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_new_command():
|
||||||
|
assert get_new_command(Command('pip install -r requirements.txt', '')) == 'pip install --user -r requirements.txt'
|
||||||
|
assert get_new_command(Command('pip install bacon', '')) == 'pip install --user bacon'
|
||||||
|
assert get_new_command(Command('pip install --user -r requirements.txt', '')) == 'sudo pip install -r requirements.txt'
|
52
tests/rules/test_pyenv_no_such_command.py
Normal file
52
tests/rules/test_pyenv_no_such_command.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from thefuck.rules.pyenv_no_such_command import get_new_command, match
|
||||||
|
from thefuck.types import Command
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def output(pyenv_cmd):
|
||||||
|
return "pyenv: no such command `{}'".format(pyenv_cmd)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def Popen(mocker):
|
||||||
|
mock = mocker.patch('thefuck.rules.pyenv_no_such_command.Popen')
|
||||||
|
mock.return_value.stdout.readlines.return_value = (
|
||||||
|
b'--version\nactivate\ncommands\ncompletions\ndeactivate\nexec_\n'
|
||||||
|
b'global\nhelp\nhooks\ninit\ninstall\nlocal\nprefix_\n'
|
||||||
|
b'realpath.dylib\nrehash\nroot\nshell\nshims\nuninstall\nversion_\n'
|
||||||
|
b'version-file\nversion-file-read\nversion-file-write\nversion-name_\n'
|
||||||
|
b'version-origin\nversions\nvirtualenv\nvirtualenv-delete_\n'
|
||||||
|
b'virtualenv-init\nvirtualenv-prefix\nvirtualenvs_\n'
|
||||||
|
b'virtualenvwrapper\nvirtualenvwrapper_lazy\nwhence\nwhich_\n'
|
||||||
|
).split()
|
||||||
|
return mock
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('script, pyenv_cmd', [
|
||||||
|
('pyenv globe', 'globe'),
|
||||||
|
('pyenv intall 3.8.0', 'intall'),
|
||||||
|
('pyenv list', 'list'),
|
||||||
|
])
|
||||||
|
def test_match(script, pyenv_cmd, output):
|
||||||
|
assert match(Command(script, output=output))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('script, output', [
|
||||||
|
('pyenv global', 'system'),
|
||||||
|
('pyenv versions', ' 3.7.0\n 3.7.1\n* 3.7.2\n'),
|
||||||
|
('pyenv install --list', ' 3.7.0\n 3.7.1\n 3.7.2\n'),
|
||||||
|
])
|
||||||
|
def test_not_match(script, output):
|
||||||
|
assert not match(Command(script, output=output))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('script, pyenv_cmd, result', [
|
||||||
|
('pyenv globe', 'globe', 'pyenv global'),
|
||||||
|
('pyenv intall 3.8.0', 'intall', 'pyenv install 3.8.0'),
|
||||||
|
('pyenv list', 'list', 'pyenv install --list'),
|
||||||
|
('pyenv remove 3.8.0', 'remove', 'pyenv uninstall 3.8.0'),
|
||||||
|
])
|
||||||
|
def test_get_new_command(script, pyenv_cmd, output, result):
|
||||||
|
assert result in get_new_command(Command(script, output))
|
33
tests/rules/test_terraform_init.py
Normal file
33
tests/rules/test_terraform_init.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import pytest
|
||||||
|
from thefuck.rules.terraform_init import match, get_new_command
|
||||||
|
from thefuck.types import Command
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('script, output', [
|
||||||
|
('terraform plan', 'Error: Initialization required. '
|
||||||
|
'Please see the error message above.'),
|
||||||
|
('terraform plan', 'This module is not yet installed. Run "terraform init" '
|
||||||
|
'to install all modules required by this configuration.'),
|
||||||
|
('terraform apply', 'Error: Initialization required. '
|
||||||
|
'Please see the error message above.'),
|
||||||
|
('terraform apply', 'This module is not yet installed. Run "terraform init" '
|
||||||
|
'to install all modules required by this configuration.')])
|
||||||
|
def test_match(script, output):
|
||||||
|
assert match(Command(script, output))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('script, output', [
|
||||||
|
('terraform --version', 'Terraform v0.12.2'),
|
||||||
|
('terraform plan', 'No changes. Infrastructure is up-to-date.'),
|
||||||
|
('terraform apply', 'Apply complete! Resources: 0 added, 0 changed, 0 destroyed.'),
|
||||||
|
])
|
||||||
|
def test_not_match(script, output):
|
||||||
|
assert not match(Command(script, output=output))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('command, new_command', [
|
||||||
|
(Command('terraform plan', ''), 'terraform init && terraform plan'),
|
||||||
|
(Command('terraform apply', ''), 'terraform init && terraform apply'),
|
||||||
|
])
|
||||||
|
def test_get_new_command(command, new_command):
|
||||||
|
assert get_new_command(command) == new_command
|
@ -11,6 +11,11 @@ class TestBash(object):
|
|||||||
def shell(self):
|
def shell(self):
|
||||||
return Bash()
|
return Bash()
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def Popen(self, mocker):
|
||||||
|
mock = mocker.patch('thefuck.shells.bash.Popen')
|
||||||
|
return mock
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def shell_aliases(self):
|
def shell_aliases(self):
|
||||||
os.environ['TF_SHELL_ALIASES'] = (
|
os.environ['TF_SHELL_ALIASES'] = (
|
||||||
@ -74,7 +79,12 @@ class TestBash(object):
|
|||||||
config_exists.return_value = False
|
config_exists.return_value = False
|
||||||
assert not shell.how_to_configure().can_configure_automatically
|
assert not shell.how_to_configure().can_configure_automatically
|
||||||
|
|
||||||
def test_info(self, shell, mocker):
|
def test_info(self, shell, Popen):
|
||||||
patch = mocker.patch('thefuck.shells.bash.Popen')
|
Popen.return_value.stdout.read.side_effect = [b'3.5.9']
|
||||||
patch.return_value.stdout.read.side_effect = [b'3.5.9']
|
|
||||||
assert shell.info() == 'Bash 3.5.9'
|
assert shell.info() == 'Bash 3.5.9'
|
||||||
|
|
||||||
|
def test_get_version_error(self, shell, Popen):
|
||||||
|
Popen.return_value.stdout.read.side_effect = OSError
|
||||||
|
with pytest.raises(OSError):
|
||||||
|
shell._get_version()
|
||||||
|
assert Popen.call_args[0][0] == ['bash', '-c', 'echo $BASH_VERSION']
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from thefuck.const import ARGUMENT_PLACEHOLDER
|
||||||
from thefuck.shells import Fish
|
from thefuck.shells import Fish
|
||||||
|
|
||||||
|
|
||||||
@ -82,6 +83,7 @@ class TestFish(object):
|
|||||||
assert 'TF_SHELL=fish' in shell.app_alias('fuck')
|
assert 'TF_SHELL=fish' in shell.app_alias('fuck')
|
||||||
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
|
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
|
||||||
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
|
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
|
||||||
|
assert ARGUMENT_PLACEHOLDER in shell.app_alias('fuck')
|
||||||
|
|
||||||
def test_app_alias_alter_history(self, settings, shell):
|
def test_app_alias_alter_history(self, settings, shell):
|
||||||
settings.alter_history = True
|
settings.alter_history = True
|
||||||
@ -114,7 +116,17 @@ class TestFish(object):
|
|||||||
config_exists.return_value = False
|
config_exists.return_value = False
|
||||||
assert not shell.how_to_configure().can_configure_automatically
|
assert not shell.how_to_configure().can_configure_automatically
|
||||||
|
|
||||||
def test_info(self, shell, Popen):
|
def test_get_version(self, shell, Popen):
|
||||||
Popen.return_value.stdout.read.side_effect = [b'fish, version 3.5.9\n']
|
Popen.return_value.stdout.read.side_effect = [b'fish, version 3.5.9\n']
|
||||||
assert shell.info() == 'Fish Shell 3.5.9'
|
assert shell._get_version() == '3.5.9'
|
||||||
|
assert Popen.call_args[0][0] == ['fish', '--version']
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('side_effect, exception', [
|
||||||
|
([b'\n'], IndexError),
|
||||||
|
(OSError('file not found'), OSError),
|
||||||
|
])
|
||||||
|
def test_get_version_error(self, side_effect, exception, shell, Popen):
|
||||||
|
Popen.return_value.stdout.read.side_effect = side_effect
|
||||||
|
with pytest.raises(exception):
|
||||||
|
shell._get_version()
|
||||||
assert Popen.call_args[0][0] == ['fish', '--version']
|
assert Popen.call_args[0][0] == ['fish', '--version']
|
||||||
|
@ -43,3 +43,14 @@ class TestGeneric(object):
|
|||||||
|
|
||||||
def test_how_to_configure(self, shell):
|
def test_how_to_configure(self, shell):
|
||||||
assert shell.how_to_configure() is None
|
assert shell.how_to_configure() is None
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('side_effect, expected_info, warn', [
|
||||||
|
([u'3.5.9'], u'Generic Shell 3.5.9', False),
|
||||||
|
([OSError], u'Generic Shell', True),
|
||||||
|
])
|
||||||
|
def test_info(self, side_effect, expected_info, warn, shell, mocker):
|
||||||
|
warn_mock = mocker.patch('thefuck.shells.generic.warn')
|
||||||
|
shell._get_version = mocker.Mock(side_effect=side_effect)
|
||||||
|
assert shell.info() == expected_info
|
||||||
|
assert warn_mock.called is warn
|
||||||
|
assert shell._get_version.called
|
||||||
|
@ -10,6 +10,11 @@ class TestPowershell(object):
|
|||||||
def shell(self):
|
def shell(self):
|
||||||
return Powershell()
|
return Powershell()
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def Popen(self, mocker):
|
||||||
|
mock = mocker.patch('thefuck.shells.powershell.Popen')
|
||||||
|
return mock
|
||||||
|
|
||||||
def test_and_(self, shell):
|
def test_and_(self, shell):
|
||||||
assert shell.and_('ls', 'cd') == '(ls) -and (cd)'
|
assert shell.and_('ls', 'cd') == '(ls) -and (cd)'
|
||||||
|
|
||||||
@ -20,3 +25,20 @@ class TestPowershell(object):
|
|||||||
|
|
||||||
def test_how_to_configure(self, shell):
|
def test_how_to_configure(self, shell):
|
||||||
assert not shell.how_to_configure().can_configure_automatically
|
assert not shell.how_to_configure().can_configure_automatically
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('side_effect, expected_version, call_args', [
|
||||||
|
([b'''Major Minor Build Revision
|
||||||
|
----- ----- ----- --------
|
||||||
|
5 1 17763 316 \n'''], 'PowerShell 5.1.17763.316', ['powershell.exe']),
|
||||||
|
([IOError, b'PowerShell 6.1.2\n'], 'PowerShell 6.1.2', ['powershell.exe', 'pwsh'])])
|
||||||
|
def test_info(self, side_effect, expected_version, call_args, shell, Popen):
|
||||||
|
Popen.return_value.stdout.read.side_effect = side_effect
|
||||||
|
assert shell.info() == expected_version
|
||||||
|
assert Popen.call_count == len(call_args)
|
||||||
|
assert all([Popen.call_args_list[i][0][0][0] == call_arg for i, call_arg in enumerate(call_args)])
|
||||||
|
|
||||||
|
def test_get_version_error(self, shell, Popen):
|
||||||
|
Popen.return_value.stdout.read.side_effect = RuntimeError
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
shell._get_version()
|
||||||
|
assert Popen.call_args[0][0] == ['powershell.exe', '$PSVersionTable.PSVersion']
|
||||||
|
@ -61,3 +61,17 @@ class TestTcsh(object):
|
|||||||
config_exists):
|
config_exists):
|
||||||
config_exists.return_value = False
|
config_exists.return_value = False
|
||||||
assert not shell.how_to_configure().can_configure_automatically
|
assert not shell.how_to_configure().can_configure_automatically
|
||||||
|
|
||||||
|
def test_info(self, shell, Popen):
|
||||||
|
Popen.return_value.stdout.read.side_effect = [
|
||||||
|
b'tcsh 6.20.00 (Astron) 2016-11-24 (unknown-unknown-bsd44) \n']
|
||||||
|
assert shell.info() == 'Tcsh 6.20.00'
|
||||||
|
assert Popen.call_args[0][0] == ['tcsh', '--version']
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('side_effect, exception', [
|
||||||
|
([b'\n'], IndexError), (OSError, OSError)])
|
||||||
|
def test_get_version_error(self, side_effect, exception, shell, Popen):
|
||||||
|
Popen.return_value.stdout.read.side_effect = side_effect
|
||||||
|
with pytest.raises(exception):
|
||||||
|
shell._get_version()
|
||||||
|
assert Popen.call_args[0][0] == ['tcsh', '--version']
|
||||||
|
@ -11,6 +11,11 @@ class TestZsh(object):
|
|||||||
def shell(self):
|
def shell(self):
|
||||||
return Zsh()
|
return Zsh()
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def Popen(self, mocker):
|
||||||
|
mock = mocker.patch('thefuck.shells.zsh.Popen')
|
||||||
|
return mock
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def shell_aliases(self):
|
def shell_aliases(self):
|
||||||
os.environ['TF_SHELL_ALIASES'] = (
|
os.environ['TF_SHELL_ALIASES'] = (
|
||||||
@ -69,7 +74,12 @@ class TestZsh(object):
|
|||||||
config_exists.return_value = False
|
config_exists.return_value = False
|
||||||
assert not shell.how_to_configure().can_configure_automatically
|
assert not shell.how_to_configure().can_configure_automatically
|
||||||
|
|
||||||
def test_info(self, shell, mocker):
|
def test_info(self, shell, Popen):
|
||||||
patch = mocker.patch('thefuck.shells.zsh.Popen')
|
Popen.return_value.stdout.read.side_effect = [b'3.5.9']
|
||||||
patch.return_value.stdout.read.side_effect = [b'3.5.9']
|
|
||||||
assert shell.info() == 'ZSH 3.5.9'
|
assert shell.info() == 'ZSH 3.5.9'
|
||||||
|
|
||||||
|
def test_get_version_error(self, shell, Popen):
|
||||||
|
Popen.return_value.stdout.read.side_effect = OSError
|
||||||
|
with pytest.raises(OSError):
|
||||||
|
shell._get_version()
|
||||||
|
assert Popen.call_args[0][0] == ['zsh', '-c', 'echo $ZSH_VERSION']
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import warnings
|
import warnings
|
||||||
from mock import Mock, patch
|
from mock import Mock, call, patch
|
||||||
from thefuck.utils import default_settings, \
|
from thefuck.utils import default_settings, \
|
||||||
memoize, get_closest, get_all_executables, replace_argument, \
|
memoize, get_closest, get_all_executables, replace_argument, \
|
||||||
get_all_matched_commands, is_app, for_app, cache, \
|
get_all_matched_commands, is_app, for_app, cache, \
|
||||||
@ -76,6 +76,24 @@ def test_get_all_executables():
|
|||||||
assert 'fuck' not in all_callables
|
assert 'fuck' not in all_callables
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def os_environ_pathsep(monkeypatch, path, pathsep):
|
||||||
|
env = {'PATH': path}
|
||||||
|
monkeypatch.setattr('os.environ', env)
|
||||||
|
monkeypatch.setattr('os.pathsep', pathsep)
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('no_memoize', 'os_environ_pathsep')
|
||||||
|
@pytest.mark.parametrize('path, pathsep', [
|
||||||
|
('/foo:/bar:/baz:/foo/bar', ':'),
|
||||||
|
(r'C:\\foo;C:\\bar;C:\\baz;C:\\foo\\bar', ';')])
|
||||||
|
def test_get_all_executables_pathsep(path, pathsep):
|
||||||
|
with patch('thefuck.utils.Path') as Path_mock:
|
||||||
|
get_all_executables()
|
||||||
|
Path_mock.assert_has_calls([call(p) for p in path.split(pathsep)], True)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('args, result', [
|
@pytest.mark.parametrize('args, result', [
|
||||||
(('apt-get instol vim', 'instol', 'install'), 'apt-get install vim'),
|
(('apt-get instol vim', 'instol', 'install'), 'apt-get install vim'),
|
||||||
(('git brnch', 'brnch', 'branch'), 'git branch')])
|
(('git brnch', 'brnch', 'branch'), 'git branch')])
|
||||||
|
@ -22,10 +22,13 @@ def main():
|
|||||||
elif known_args.version:
|
elif known_args.version:
|
||||||
logs.version(get_installation_info().version,
|
logs.version(get_installation_info().version,
|
||||||
sys.version.split()[0], shell.info())
|
sys.version.split()[0], shell.info())
|
||||||
elif known_args.command or 'TF_HISTORY' in os.environ:
|
# It's important to check if an alias is being requested before checking if
|
||||||
fix_command(known_args)
|
# `TF_HISTORY` is in `os.environ`, otherwise it might mess with subshells.
|
||||||
|
# Check https://github.com/nvbn/thefuck/issues/921 for reference
|
||||||
elif known_args.alias:
|
elif known_args.alias:
|
||||||
print_alias(known_args)
|
print_alias(known_args)
|
||||||
|
elif known_args.command or 'TF_HISTORY' in os.environ:
|
||||||
|
fix_command(known_args)
|
||||||
elif known_args.shell_logger:
|
elif known_args.shell_logger:
|
||||||
try:
|
try:
|
||||||
from .shell_logger import shell_logger # noqa: E402
|
from .shell_logger import shell_logger # noqa: E402
|
||||||
|
20
thefuck/rules/docker_image_being_used_by_container.py
Normal file
20
thefuck/rules/docker_image_being_used_by_container.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
from thefuck.utils import for_app
|
||||||
|
from thefuck.shells import shell
|
||||||
|
|
||||||
|
|
||||||
|
@for_app('docker')
|
||||||
|
def match(command):
|
||||||
|
'''
|
||||||
|
Matches a command's output with docker's output
|
||||||
|
warning you that you need to remove a container before removing an image.
|
||||||
|
'''
|
||||||
|
return 'image is being used by running container' in command.output
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_command(command):
|
||||||
|
'''
|
||||||
|
Prepends docker container rm -f {container ID} to
|
||||||
|
the previous docker image rm {image ID} command
|
||||||
|
'''
|
||||||
|
container_id = command.output.strip().split(' ')
|
||||||
|
return shell.and_('docker container rm -f {}', '{}').format(container_id[-1], command.script)
|
12
thefuck/rules/docker_login.py
Normal file
12
thefuck/rules/docker_login.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from thefuck.utils import for_app
|
||||||
|
|
||||||
|
|
||||||
|
@for_app('docker')
|
||||||
|
def match(command):
|
||||||
|
return ('docker' in command.script
|
||||||
|
and "access denied" in command.output
|
||||||
|
and "may require 'docker login'" in command.output)
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_command(command):
|
||||||
|
return 'docker login && {}'.format(command.script)
|
15
thefuck/rules/nixos_cmd_not_found.py
Normal file
15
thefuck/rules/nixos_cmd_not_found.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import re
|
||||||
|
from thefuck.specific.nix import nix_available
|
||||||
|
from thefuck.shells import shell
|
||||||
|
|
||||||
|
regex = re.compile(r'nix-env -iA ([^\s]*)')
|
||||||
|
enabled_by_default = nix_available
|
||||||
|
|
||||||
|
|
||||||
|
def match(command):
|
||||||
|
return regex.findall(command.output)
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_command(command):
|
||||||
|
name = regex.findall(command.output)[0]
|
||||||
|
return shell.and_('nix-env -iA {}'.format(name), command.script)
|
@ -6,7 +6,8 @@ from thefuck.specific.sudo import sudo_support
|
|||||||
@sudo_support
|
@sudo_support
|
||||||
def match(command):
|
def match(command):
|
||||||
return (not which(command.script_parts[0])
|
return (not which(command.script_parts[0])
|
||||||
and 'not found' in command.output
|
and ('not found' in command.output
|
||||||
|
or 'is not recognized as' in command.output)
|
||||||
and bool(get_close_matches(command.script_parts[0],
|
and bool(get_close_matches(command.script_parts[0],
|
||||||
get_all_executables())))
|
get_all_executables())))
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
""" Fixes wrong package names with pacman or yaourt.
|
""" Fixes wrong package names with pacman or yaourt.
|
||||||
|
|
||||||
For example the `llc` program is in package `llvm` so this:
|
For example the `llc` program is in package `llvm` so this:
|
||||||
yaourt -S llc
|
yay -S llc
|
||||||
should be:
|
should be:
|
||||||
yaourt -S llvm
|
yay -S llvm
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from thefuck.utils import replace_command
|
from thefuck.utils import replace_command
|
||||||
@ -12,7 +12,7 @@ from thefuck.specific.archlinux import get_pkgfile, archlinux_env
|
|||||||
|
|
||||||
def match(command):
|
def match(command):
|
||||||
return (command.script_parts
|
return (command.script_parts
|
||||||
and (command.script_parts[0] in ('pacman', 'yaourt')
|
and (command.script_parts[0] in ('pacman', 'yay', 'yaourt')
|
||||||
or command.script_parts[0:2] == ['sudo', 'pacman'])
|
or command.script_parts[0:2] == ['sudo', 'pacman'])
|
||||||
and 'error: target not found:' in command.output)
|
and 'error: target not found:' in command.output)
|
||||||
|
|
||||||
|
15
thefuck/rules/pip_install.py
Normal file
15
thefuck/rules/pip_install.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from thefuck.utils import for_app
|
||||||
|
from thefuck.specific.sudo import sudo_support
|
||||||
|
|
||||||
|
|
||||||
|
@sudo_support
|
||||||
|
@for_app('pip')
|
||||||
|
def match(command):
|
||||||
|
return ('pip install' in command.script and 'Permission denied' in command.output)
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_command(command):
|
||||||
|
if '--user' not in command.script: # add --user (attempt 1)
|
||||||
|
return command.script.replace(' install ', ' install --user ')
|
||||||
|
|
||||||
|
return 'sudo {}'.format(command.script.replace(' --user', '')) # since --user didn't fix things, let's try sudo (attempt 2)
|
33
thefuck/rules/pyenv_no_such_command.py
Normal file
33
thefuck/rules/pyenv_no_such_command.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import re
|
||||||
|
from subprocess import PIPE, Popen
|
||||||
|
|
||||||
|
from thefuck.utils import (cache, for_app, replace_argument, replace_command,
|
||||||
|
which)
|
||||||
|
|
||||||
|
COMMON_TYPOS = {
|
||||||
|
'list': ['versions', 'install --list'],
|
||||||
|
'remove': ['uninstall'],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@for_app('pyenv')
|
||||||
|
def match(command):
|
||||||
|
return 'pyenv: no such command' in command.output
|
||||||
|
|
||||||
|
|
||||||
|
def get_pyenv_commands():
|
||||||
|
proc = Popen(['pyenv', 'commands'], stdout=PIPE)
|
||||||
|
return [line.decode('utf-8').strip() for line in proc.stdout.readlines()]
|
||||||
|
|
||||||
|
|
||||||
|
if which('pyenv'):
|
||||||
|
get_pyenv_commands = cache(which('pyenv'))(get_pyenv_commands)
|
||||||
|
|
||||||
|
|
||||||
|
@for_app('pyenv')
|
||||||
|
def get_new_command(command):
|
||||||
|
broken = re.findall(r"pyenv: no such command `([^']*)'", command.output)[0]
|
||||||
|
matched = [replace_argument(command.script, broken, common_typo)
|
||||||
|
for common_typo in COMMON_TYPOS.get(broken, [])]
|
||||||
|
matched.extend(replace_command(command, broken, get_pyenv_commands()))
|
||||||
|
return matched
|
13
thefuck/rules/terraform_init.py
Normal file
13
thefuck/rules/terraform_init.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from thefuck.shells import shell
|
||||||
|
from thefuck.utils import for_app
|
||||||
|
|
||||||
|
|
||||||
|
@for_app('terraform')
|
||||||
|
def match(command):
|
||||||
|
return ('this module is not yet installed' in command.output.lower() or
|
||||||
|
'initialization required' in command.output.lower()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_command(command):
|
||||||
|
return shell.and_('terraform init', command.script)
|
@ -9,6 +9,8 @@ from .generic import Generic
|
|||||||
|
|
||||||
|
|
||||||
class Bash(Generic):
|
class Bash(Generic):
|
||||||
|
friendly_name = 'Bash'
|
||||||
|
|
||||||
def app_alias(self, alias_name):
|
def app_alias(self, alias_name):
|
||||||
# It is VERY important to have the variables declared WITHIN the function
|
# It is VERY important to have the variables declared WITHIN the function
|
||||||
return '''
|
return '''
|
||||||
@ -20,8 +22,8 @@ class Bash(Generic):
|
|||||||
export TF_HISTORY=$(fc -ln -10);
|
export TF_HISTORY=$(fc -ln -10);
|
||||||
export PYTHONIOENCODING=utf-8;
|
export PYTHONIOENCODING=utf-8;
|
||||||
TF_CMD=$(
|
TF_CMD=$(
|
||||||
thefuck {argument_placeholder} $@
|
thefuck {argument_placeholder} "$@"
|
||||||
) && eval $TF_CMD;
|
) && eval "$TF_CMD";
|
||||||
unset TF_HISTORY;
|
unset TF_HISTORY;
|
||||||
export PYTHONIOENCODING=$TF_PYTHONIOENCODING;
|
export PYTHONIOENCODING=$TF_PYTHONIOENCODING;
|
||||||
{alter_history}
|
{alter_history}
|
||||||
@ -79,13 +81,12 @@ class Bash(Generic):
|
|||||||
config = 'bash config'
|
config = 'bash config'
|
||||||
|
|
||||||
return self._create_shell_configuration(
|
return self._create_shell_configuration(
|
||||||
content=u'eval $(thefuck --alias)',
|
content=u'eval "$(thefuck --alias)"',
|
||||||
path=config,
|
path=config,
|
||||||
reload=u'source {}'.format(config))
|
reload=u'source {}'.format(config))
|
||||||
|
|
||||||
def info(self):
|
def _get_version(self):
|
||||||
"""Returns the name and version of the current shell"""
|
"""Returns the version of the current shell"""
|
||||||
proc = Popen(['bash', '-c', 'echo $BASH_VERSION'],
|
proc = Popen(['bash', '-c', 'echo $BASH_VERSION'],
|
||||||
stdout=PIPE, stderr=DEVNULL)
|
stdout=PIPE, stderr=DEVNULL)
|
||||||
version = proc.stdout.read().decode('utf-8').strip()
|
return proc.stdout.read().decode('utf-8').strip()
|
||||||
return u'Bash {}'.format(version)
|
|
||||||
|
@ -5,6 +5,7 @@ import sys
|
|||||||
import six
|
import six
|
||||||
from .. import logs
|
from .. import logs
|
||||||
from ..conf import settings
|
from ..conf import settings
|
||||||
|
from ..const import ARGUMENT_PLACEHOLDER
|
||||||
from ..utils import DEVNULL, cache
|
from ..utils import DEVNULL, cache
|
||||||
from .generic import Generic
|
from .generic import Generic
|
||||||
|
|
||||||
@ -37,6 +38,8 @@ def _get_aliases(overridden):
|
|||||||
|
|
||||||
|
|
||||||
class Fish(Generic):
|
class Fish(Generic):
|
||||||
|
friendly_name = 'Fish Shell'
|
||||||
|
|
||||||
def _get_overridden_aliases(self):
|
def _get_overridden_aliases(self):
|
||||||
overridden = os.environ.get('THEFUCK_OVERRIDDEN_ALIASES',
|
overridden = os.environ.get('THEFUCK_OVERRIDDEN_ALIASES',
|
||||||
os.environ.get('TF_OVERRIDDEN_ALIASES', ''))
|
os.environ.get('TF_OVERRIDDEN_ALIASES', ''))
|
||||||
@ -56,11 +59,11 @@ class Fish(Generic):
|
|||||||
return ('function {0} -d "Correct your previous console command"\n'
|
return ('function {0} -d "Correct your previous console command"\n'
|
||||||
' set -l fucked_up_command $history[1]\n'
|
' set -l fucked_up_command $history[1]\n'
|
||||||
' env TF_SHELL=fish TF_ALIAS={0} PYTHONIOENCODING=utf-8'
|
' env TF_SHELL=fish TF_ALIAS={0} PYTHONIOENCODING=utf-8'
|
||||||
' thefuck $fucked_up_command | read -l unfucked_command\n'
|
' thefuck $fucked_up_command {2} $argv | read -l unfucked_command\n'
|
||||||
' if [ "$unfucked_command" != "" ]\n'
|
' if [ "$unfucked_command" != "" ]\n'
|
||||||
' eval $unfucked_command\n{1}'
|
' eval $unfucked_command\n{1}'
|
||||||
' end\n'
|
' end\n'
|
||||||
'end').format(alias_name, alter_history)
|
'end').format(alias_name, alter_history, ARGUMENT_PLACEHOLDER)
|
||||||
|
|
||||||
def get_aliases(self):
|
def get_aliases(self):
|
||||||
overridden = self._get_overridden_aliases()
|
overridden = self._get_overridden_aliases()
|
||||||
@ -103,12 +106,10 @@ class Fish(Generic):
|
|||||||
path='~/.config/fish/config.fish',
|
path='~/.config/fish/config.fish',
|
||||||
reload='fish')
|
reload='fish')
|
||||||
|
|
||||||
def info(self):
|
def _get_version(self):
|
||||||
"""Returns the name and version of the current shell"""
|
"""Returns the version of the current shell"""
|
||||||
proc = Popen(['fish', '--version'],
|
proc = Popen(['fish', '--version'], stdout=PIPE, stderr=DEVNULL)
|
||||||
stdout=PIPE, stderr=DEVNULL)
|
return proc.stdout.read().decode('utf-8').split()[-1]
|
||||||
version = proc.stdout.read().decode('utf-8').split()[-1]
|
|
||||||
return u'Fish Shell {}'.format(version)
|
|
||||||
|
|
||||||
def put_to_history(self, command):
|
def put_to_history(self, command):
|
||||||
try:
|
try:
|
||||||
|
@ -14,6 +14,8 @@ ShellConfiguration = namedtuple('ShellConfiguration', (
|
|||||||
|
|
||||||
|
|
||||||
class Generic(object):
|
class Generic(object):
|
||||||
|
friendly_name = 'Generic Shell'
|
||||||
|
|
||||||
def get_aliases(self):
|
def get_aliases(self):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@ -34,8 +36,8 @@ class Generic(object):
|
|||||||
return command_script
|
return command_script
|
||||||
|
|
||||||
def app_alias(self, alias_name):
|
def app_alias(self, alias_name):
|
||||||
return "alias {0}='eval $(TF_ALIAS={0} PYTHONIOENCODING=utf-8 " \
|
return """alias {0}='eval "$(TF_ALIAS={0} PYTHONIOENCODING=utf-8 """ \
|
||||||
"thefuck $(fc -ln -1))'".format(alias_name)
|
"""thefuck "$(fc -ln -1)")"'""".format(alias_name)
|
||||||
|
|
||||||
def instant_mode_alias(self, alias_name):
|
def instant_mode_alias(self, alias_name):
|
||||||
warn("Instant mode not supported by your shell")
|
warn("Instant mode not supported by your shell")
|
||||||
@ -131,9 +133,18 @@ class Generic(object):
|
|||||||
'type', 'typeset', 'ulimit', 'umask', 'unalias', 'unset',
|
'type', 'typeset', 'ulimit', 'umask', 'unalias', 'unset',
|
||||||
'until', 'wait', 'while']
|
'until', 'wait', 'while']
|
||||||
|
|
||||||
|
def _get_version(self):
|
||||||
|
"""Returns the version of the current shell"""
|
||||||
|
return ''
|
||||||
|
|
||||||
def info(self):
|
def info(self):
|
||||||
"""Returns the name and version of the current shell"""
|
"""Returns the name and version of the current shell"""
|
||||||
return 'Generic Shell'
|
try:
|
||||||
|
version = self._get_version()
|
||||||
|
except Exception as e:
|
||||||
|
warn(u'Could not determine shell version: {}'.format(e))
|
||||||
|
version = ''
|
||||||
|
return u'{} {}'.format(self.friendly_name, version).rstrip()
|
||||||
|
|
||||||
def _create_shell_configuration(self, content, path, reload):
|
def _create_shell_configuration(self, content, path, reload):
|
||||||
return ShellConfiguration(
|
return ShellConfiguration(
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
|
from subprocess import Popen, PIPE
|
||||||
|
from ..utils import DEVNULL
|
||||||
from .generic import Generic, ShellConfiguration
|
from .generic import Generic, ShellConfiguration
|
||||||
|
|
||||||
|
|
||||||
class Powershell(Generic):
|
class Powershell(Generic):
|
||||||
|
friendly_name = 'PowerShell'
|
||||||
|
|
||||||
def app_alias(self, alias_name):
|
def app_alias(self, alias_name):
|
||||||
return 'function ' + alias_name + ' {\n' \
|
return 'function ' + alias_name + ' {\n' \
|
||||||
' $history = (Get-History -Count 1).CommandLine;\n' \
|
' $history = (Get-History -Count 1).CommandLine;\n' \
|
||||||
@ -24,3 +28,16 @@ class Powershell(Generic):
|
|||||||
path='$profile',
|
path='$profile',
|
||||||
reload='& $profile',
|
reload='& $profile',
|
||||||
can_configure_automatically=False)
|
can_configure_automatically=False)
|
||||||
|
|
||||||
|
def _get_version(self):
|
||||||
|
"""Returns the version of the current shell"""
|
||||||
|
try:
|
||||||
|
proc = Popen(
|
||||||
|
['powershell.exe', '$PSVersionTable.PSVersion'],
|
||||||
|
stdout=PIPE,
|
||||||
|
stderr=DEVNULL)
|
||||||
|
version = proc.stdout.read().decode('utf-8').rstrip().split('\n')
|
||||||
|
return '.'.join(version[-1].split())
|
||||||
|
except IOError:
|
||||||
|
proc = Popen(['pwsh', '--version'], stdout=PIPE, stderr=DEVNULL)
|
||||||
|
return proc.stdout.read().decode('utf-8').split()[-1]
|
||||||
|
@ -6,6 +6,8 @@ from .generic import Generic
|
|||||||
|
|
||||||
|
|
||||||
class Tcsh(Generic):
|
class Tcsh(Generic):
|
||||||
|
friendly_name = 'Tcsh'
|
||||||
|
|
||||||
def app_alias(self, alias_name):
|
def app_alias(self, alias_name):
|
||||||
return ("alias {0} 'setenv TF_SHELL tcsh && setenv TF_ALIAS {0} && "
|
return ("alias {0} 'setenv TF_SHELL tcsh && setenv TF_ALIAS {0} && "
|
||||||
"set fucked_cmd=`history -h 2 | head -n 1` && "
|
"set fucked_cmd=`history -h 2 | head -n 1` && "
|
||||||
@ -35,3 +37,8 @@ class Tcsh(Generic):
|
|||||||
content=u'eval `thefuck --alias`',
|
content=u'eval `thefuck --alias`',
|
||||||
path='~/.tcshrc',
|
path='~/.tcshrc',
|
||||||
reload='tcsh')
|
reload='tcsh')
|
||||||
|
|
||||||
|
def _get_version(self):
|
||||||
|
"""Returns the version of the current shell"""
|
||||||
|
proc = Popen(['tcsh', '--version'], stdout=PIPE, stderr=DEVNULL)
|
||||||
|
return proc.stdout.read().decode('utf-8').split()[1]
|
||||||
|
@ -10,6 +10,8 @@ from .generic import Generic
|
|||||||
|
|
||||||
|
|
||||||
class Zsh(Generic):
|
class Zsh(Generic):
|
||||||
|
friendly_name = 'ZSH'
|
||||||
|
|
||||||
def app_alias(self, alias_name):
|
def app_alias(self, alias_name):
|
||||||
# It is VERY important to have the variables declared WITHIN the function
|
# It is VERY important to have the variables declared WITHIN the function
|
||||||
return '''
|
return '''
|
||||||
@ -87,9 +89,8 @@ class Zsh(Generic):
|
|||||||
path='~/.zshrc',
|
path='~/.zshrc',
|
||||||
reload='source ~/.zshrc')
|
reload='source ~/.zshrc')
|
||||||
|
|
||||||
def info(self):
|
def _get_version(self):
|
||||||
"""Returns the name and version of the current shell"""
|
"""Returns the version of the current shell"""
|
||||||
proc = Popen(['zsh', '-c', 'echo $ZSH_VERSION'],
|
proc = Popen(['zsh', '-c', 'echo $ZSH_VERSION'],
|
||||||
stdout=PIPE, stderr=DEVNULL)
|
stdout=PIPE, stderr=DEVNULL)
|
||||||
version = proc.stdout.read().decode('utf-8').strip()
|
return proc.stdout.read().decode('utf-8').strip()
|
||||||
return u'ZSH {}'.format(version)
|
|
||||||
|
@ -32,7 +32,9 @@ def get_pkgfile(command):
|
|||||||
|
|
||||||
|
|
||||||
def archlinux_env():
|
def archlinux_env():
|
||||||
if utils.which('yaourt'):
|
if utils.which('yay'):
|
||||||
|
pacman = 'yay'
|
||||||
|
elif utils.which('yaourt'):
|
||||||
pacman = 'yaourt'
|
pacman = 'yaourt'
|
||||||
elif utils.which('pacman'):
|
elif utils.which('pacman'):
|
||||||
pacman = 'sudo pacman'
|
pacman = 'sudo pacman'
|
||||||
|
3
thefuck/specific/nix.py
Normal file
3
thefuck/specific/nix.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from thefuck.utils import which
|
||||||
|
|
||||||
|
nix_available = bool(which('nix'))
|
@ -118,7 +118,7 @@ def get_all_executables():
|
|||||||
tf_entry_points = ['thefuck', 'fuck']
|
tf_entry_points = ['thefuck', 'fuck']
|
||||||
|
|
||||||
bins = [exe.name.decode('utf8') if six.PY2 else exe.name
|
bins = [exe.name.decode('utf8') if six.PY2 else exe.name
|
||||||
for path in os.environ.get('PATH', '').split(':')
|
for path in os.environ.get('PATH', '').split(os.pathsep)
|
||||||
for exe in _safe(lambda: list(Path(path).iterdir()), [])
|
for exe in _safe(lambda: list(Path(path).iterdir()), [])
|
||||||
if not _safe(exe.is_dir, True)
|
if not _safe(exe.is_dir, True)
|
||||||
and exe.name not in tf_entry_points]
|
and exe.name not in tf_entry_points]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user