mirror of
https://github.com/nvbn/thefuck.git
synced 2025-11-01 07:32:09 +00:00
Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c63e0d1582 | ||
|
|
a3eb124033 | ||
|
|
a1f115eb19 | ||
|
|
56851e8d31 | ||
|
|
a8c3c2d728 | ||
|
|
844d3af8ae | ||
|
|
a2693bd737 | ||
|
|
ae3e231a5f | ||
|
|
21bb439d7c | ||
|
|
efcf7da7db | ||
|
|
5f79217e97 | ||
|
|
fdfbfc80c0 | ||
|
|
b09a4e394e | ||
|
|
34973fe97e | ||
|
|
379d2953c9 | ||
|
|
065b350ada | ||
|
|
ca787a1cba | ||
|
|
4c2fc490f2 | ||
|
|
0c2083485d | ||
|
|
bc78c83224 | ||
|
|
b2a5009116 | ||
|
|
087584a1e0 | ||
|
|
b6b15bf0d1 | ||
|
|
a169575b0f | ||
|
|
fbea803a9b | ||
|
|
51415a5cb1 | ||
|
|
237bc57999 | ||
|
|
2af65071d8 | ||
|
|
c93b547624 | ||
|
|
837ca73f50 | ||
|
|
a3b2e6872b | ||
|
|
29ed1800e1 | ||
|
|
965c05bfdf | ||
|
|
a7d1c725e4 | ||
|
|
99e828d15d | ||
|
|
ae2b767a4d | ||
|
|
3893e0cdca | ||
|
|
2988e4871f | ||
|
|
2c1666abc4 |
38
.travis.yml
38
.travis.yml
@@ -1,10 +1,25 @@
|
||||
language: python
|
||||
sudo: false
|
||||
python:
|
||||
- "3.5"
|
||||
- "3.4"
|
||||
- "3.3"
|
||||
- "2.7"
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: trusty
|
||||
python: "3.5"
|
||||
- os: linux
|
||||
dist: trusty
|
||||
python: "3.4"
|
||||
- os: linux
|
||||
dist: trusty
|
||||
python: "3.3"
|
||||
- os: linux
|
||||
dist: trusty
|
||||
python: "2.7"
|
||||
- os: osx
|
||||
env: FORMULA="python"
|
||||
language: generic
|
||||
- os: osx
|
||||
env: FORMULA="python3"
|
||||
language: generic
|
||||
services:
|
||||
- docker
|
||||
addons:
|
||||
@@ -12,16 +27,21 @@ addons:
|
||||
packages:
|
||||
- python-commandnotfound
|
||||
- python3-commandnotfound
|
||||
install:
|
||||
before_install:
|
||||
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew update ; fi
|
||||
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew install $FORMULA; fi
|
||||
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then virtualenv venv -p $FORMULA; fi
|
||||
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then source venv/bin/activate; fi
|
||||
- pip install -U pip
|
||||
- pip install -U coveralls
|
||||
install:
|
||||
- pip install -Ur requirements.txt
|
||||
- python setup.py develop
|
||||
- rm -rf build
|
||||
script:
|
||||
- export COVERAGE_PYTHON_VERSION=python-${TRAVIS_PYTHON_VERSION:0:1}
|
||||
- export RUN_TESTS="coverage run --source=thefuck,tests -m py.test -v --capture=sys"
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 3.5 ]]; then $RUN_TESTS --enable-functional; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != 3.5 ]]; then $RUN_TESTS; fi
|
||||
- export RUN_TESTS="coverage run --source=thefuck,tests -m py.test -v --capture=sys tests"
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 3.5 && $TRAVIS_OS_NAME != "osx" ]]; then $RUN_TESTS --enable-functional; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION != 3.5 || $TRAVIS_OS_NAME == "osx" ]]; then $RUN_TESTS; fi
|
||||
after_success:
|
||||
- coveralls
|
||||
|
||||
24
README.md
24
README.md
@@ -160,6 +160,7 @@ using the matched rule and runs it. Rules enabled by default are as follows:
|
||||
* `django_south_merge` – adds `--merge` to inconsistent django south migration;
|
||||
* `docker_not_command` – fixes wrong docker commands like `docker tags`;
|
||||
* `dry` – fixes repetitions like `git git push`;
|
||||
* `fab_command_not_found` – fix misspelled fabric commands;
|
||||
* `fix_alt_space` – replaces Alt+Space with Space character;
|
||||
* `fix_file` – opens a file with an error in your `$EDITOR`;
|
||||
* `git_add` – fixes *"pathspec 'foo' did not match any file(s) known to git."*;
|
||||
@@ -181,8 +182,11 @@ using the matched rule and runs it. Rules enabled by default are as follows:
|
||||
* `git_stash` – stashes you local modifications before rebasing or switching branch;
|
||||
* `git_two_dashes` – adds a missing dash to commands like `git commit -amend` or `git rebase -continue`;
|
||||
* `go_run` – appends `.go` extension when compiling/running Go programs;
|
||||
* `gradle_no_task` – fixes not found or ambiguous `gradle` task;
|
||||
* `gradle_wrapper` – replaces `gradle` with `./gradlew`;
|
||||
* `grep_arguments_order` – fixes grep arguments order for situations like `grep -lir . test`;
|
||||
* `grep_recursive` – adds `-r` when you trying to `grep` directory;
|
||||
* `grunt_task_not_found` – fixes misspelled `grunt` commands;
|
||||
* `gulp_not_task` – fixes misspelled `gulp` tasks;
|
||||
* `has_exists_script` – prepends `./` when script/binary exists;
|
||||
* `heroku_not_command` – fixes wrong `heroku` commands like `heroku log`;
|
||||
@@ -199,14 +203,18 @@ using the matched rule and runs it. Rules enabled by default are as follows:
|
||||
* `mkdir_p` – adds `-p` when you trying to create directory without parent;
|
||||
* `mvn_no_command` – adds `clean package` to `mvn`;
|
||||
* `mvn_unknown_lifecycle_phase` – fixes misspelled lifecycle phases with `mvn`;
|
||||
* `npm_missing_script` – fixes `npm` custom script name in `npm run-script <script>`;
|
||||
* `npm_run_script` – adds missing `run-script` for custom `npm` scripts;
|
||||
* `npm_wrong_command` – fixes wrong npm commands like `npm urgrade`;
|
||||
* `no_command` – fixes wrong console commands, for example `vom/vim`;
|
||||
* `no_such_file` – creates missing directories with `mv` and `cp` commands;
|
||||
* `open` – prepends `http` to address passed to `open`;
|
||||
* `open` – either prepends `http://` to address passed to `open` or create a new file or directory and passes it to `open`;
|
||||
* `pip_unknown_command` – fixes wrong `pip` commands, for example `pip instatl/pip install`;
|
||||
* `port_already_in_use` – kills process that bound port;
|
||||
* `python_command` – prepends `python` when you trying to run not executable/without `./` python script;
|
||||
* `python_execute` – appends missing `.py` when executing Python files;
|
||||
* `quotation_marks` – fixes uneven usage of `'` and `"` when containing args';
|
||||
* `react_native_command_unrecognized` – fixes unrecognized `react-native` commands;
|
||||
* `rm_dir` – adds `-rf` when you trying to remove directory;
|
||||
* `sed_unterminated_s` – adds missing '/' to `sed`'s `s` commands;
|
||||
* `sl_ls` – changes `sl` to `ls`;
|
||||
@@ -221,7 +229,8 @@ using the matched rule and runs it. Rules enabled by default are as follows:
|
||||
* `tmux` – fixes `tmux` commands;
|
||||
* `unknown_command` – fixes hadoop hdfs-style "unknown command", for example adds missing '-' to the command on `hdfs dfs ls`;
|
||||
* `vagrant_up` – starts up the vagrant instance;
|
||||
* `whois` – fixes `whois` command.
|
||||
* `whois` – fixes `whois` command;
|
||||
* `workon_doesnt_exists` – fixes `virtualenvwrapper` env name os suggests to create new.
|
||||
|
||||
Enabled by default only on specific platforms:
|
||||
|
||||
@@ -300,7 +309,9 @@ The Fuck has a few settings parameters which can be changed in `$XDG_CONFIG_HOME
|
||||
* `priority` – dict with rules priorities, rule with lower `priority` will be matched first;
|
||||
* `debug` – enables debug output, by default `False`;
|
||||
* `history_limit` – numeric value of how many history commands will be scanned, like `2000`;
|
||||
* `alter_history` – push fixed command to history, by default `True`.
|
||||
* `alter_history` – push fixed command to history, by default `True`;
|
||||
* `wait_slow_command` – max amount of time in seconds for getting previous command output if it in `slow_commands` list;
|
||||
* `slow_commands` – list of slow commands.
|
||||
|
||||
Example of `settings.py`:
|
||||
|
||||
@@ -312,6 +323,9 @@ wait_command = 10
|
||||
no_colors = False
|
||||
priority = {'sudo': 100, 'no_command': 9999}
|
||||
debug = False
|
||||
history_limit = 9999
|
||||
wait_slow_command = 20
|
||||
slow_commands = ['react-native', 'gradle']
|
||||
```
|
||||
|
||||
Or via environment variables:
|
||||
@@ -325,7 +339,9 @@ Or via environment variables:
|
||||
rule with lower `priority` will be matched first;
|
||||
* `THEFUCK_DEBUG` – enables debug output, `true/false`;
|
||||
* `THEFUCK_HISTORY_LIMIT` – how many history commands will be scanned, like `2000`;
|
||||
* `THEFUCK_ALTER_HISTORY` – push fixed command to history `true/false`.
|
||||
* `THEFUCK_ALTER_HISTORY` – push fixed command to history `true/false`;
|
||||
* `THEFUCK_WAIT_SLOW_COMMAND` – max amount of time in seconds for getting previous command output if it in `slow_commands` list;
|
||||
* `THEFUCK_SLOW_COMMANDS` – list of slow commands, like `lein:gradle`.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
15
setup.py
15
setup.py
@@ -1,13 +1,16 @@
|
||||
#!/usr/bin/env python
|
||||
from setuptools import setup, find_packages
|
||||
import pip
|
||||
import pkg_resources
|
||||
import sys
|
||||
import os
|
||||
|
||||
if int(pip.__version__.split('.')[0]) < 6:
|
||||
print('pip older than 6.0 not supported, please upgrade pip with:\n\n'
|
||||
' pip install -U pip')
|
||||
sys.exit(-1)
|
||||
try:
|
||||
if int(pkg_resources.get_distribution("pip").version.split('.')[0]) < 6:
|
||||
print('pip older than 6.0 not supported, please upgrade pip with:\n\n'
|
||||
' pip install -U pip')
|
||||
sys.exit(-1)
|
||||
except pkg_resources.DistributionNotFound:
|
||||
pass
|
||||
|
||||
if os.environ.get('CONVERT_README'):
|
||||
import pypandoc
|
||||
@@ -26,7 +29,7 @@ elif (3, 0) < version < (3, 3):
|
||||
' ({}.{} detected).'.format(*version))
|
||||
sys.exit(-1)
|
||||
|
||||
VERSION = '3.10'
|
||||
VERSION = '3.11'
|
||||
|
||||
install_requires = ['psutil', 'colorama', 'six', 'decorator']
|
||||
extras_require = {':python_version<"3.4"': ['pathlib2'],
|
||||
|
||||
@@ -19,10 +19,10 @@ containers = ((u'thefuck/ubuntu-python3-bash',
|
||||
|
||||
|
||||
@pytest.fixture(params=containers)
|
||||
def proc(request, spawnu, run_without_docker):
|
||||
def proc(request, spawnu, TIMEOUT):
|
||||
proc = spawnu(*request.param)
|
||||
if not run_without_docker:
|
||||
proc.sendline(u"pip install /src")
|
||||
proc.sendline(u"pip install /src")
|
||||
assert proc.expect([TIMEOUT, u'Successfully installed'])
|
||||
proc.sendline(u"export PS1='$ '")
|
||||
proc.sendline(u'eval $(thefuck --alias)')
|
||||
proc.sendline(u'echo > $HISTFILE')
|
||||
@@ -30,38 +30,29 @@ def proc(request, spawnu, run_without_docker):
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_with_confirmation(proc, TIMEOUT, run_without_docker):
|
||||
def test_with_confirmation(proc, TIMEOUT):
|
||||
with_confirmation(proc, TIMEOUT)
|
||||
if not run_without_docker:
|
||||
history_changed(proc, TIMEOUT, u'echo test')
|
||||
history_changed(proc, TIMEOUT, u'echo test')
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_select_command_with_arrows(proc, TIMEOUT, run_without_docker):
|
||||
def test_select_command_with_arrows(proc, TIMEOUT):
|
||||
select_command_with_arrows(proc, TIMEOUT)
|
||||
if not run_without_docker:
|
||||
history_changed(proc, TIMEOUT, u'git help')
|
||||
history_changed(proc, TIMEOUT, u'git help')
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_refuse_with_confirmation(proc, TIMEOUT, run_without_docker):
|
||||
def test_refuse_with_confirmation(proc, TIMEOUT):
|
||||
refuse_with_confirmation(proc, TIMEOUT)
|
||||
if not run_without_docker:
|
||||
history_not_changed(proc, TIMEOUT)
|
||||
history_not_changed(proc, TIMEOUT)
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_without_confirmation(proc, TIMEOUT, run_without_docker):
|
||||
def test_without_confirmation(proc, TIMEOUT):
|
||||
without_confirmation(proc, TIMEOUT)
|
||||
if not run_without_docker:
|
||||
history_changed(proc, TIMEOUT, u'echo test')
|
||||
history_changed(proc, TIMEOUT, u'echo test')
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_how_to_configure_alias(proc, TIMEOUT):
|
||||
how_to_configure(proc, TIMEOUT)
|
||||
|
||||
@@ -20,34 +20,31 @@ containers = (('thefuck/ubuntu-python3-fish',
|
||||
|
||||
|
||||
@pytest.fixture(params=containers)
|
||||
def proc(request, spawnu):
|
||||
def proc(request, spawnu, TIMEOUT):
|
||||
proc = spawnu(*request.param)
|
||||
proc.sendline(u"pip install /src")
|
||||
assert proc.expect([TIMEOUT, u'Successfully installed'])
|
||||
proc.sendline(u'thefuck --alias > ~/.config/fish/config.fish')
|
||||
proc.sendline(u'fish')
|
||||
return proc
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_with_confirmation(proc, TIMEOUT):
|
||||
with_confirmation(proc, TIMEOUT)
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_select_command_with_arrows(proc, TIMEOUT):
|
||||
select_command_with_arrows(proc, TIMEOUT)
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_refuse_with_confirmation(proc, TIMEOUT):
|
||||
refuse_with_confirmation(proc, TIMEOUT)
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_without_confirmation(proc, TIMEOUT):
|
||||
without_confirmation(proc, TIMEOUT)
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ def plot(proc, TIMEOUT):
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
@pytest.mark.benchmark(min_rounds=10)
|
||||
def test_performance(spawnu, TIMEOUT, benchmark):
|
||||
proc = spawnu(u'thefuck/ubuntu-python3-bash-performance',
|
||||
|
||||
@@ -20,10 +20,10 @@ containers = (('thefuck/ubuntu-python3-tcsh',
|
||||
|
||||
|
||||
@pytest.fixture(params=containers)
|
||||
def proc(request, spawnu, run_without_docker):
|
||||
def proc(request, spawnu, TIMEOUT):
|
||||
proc = spawnu(*request.param)
|
||||
if not run_without_docker:
|
||||
proc.sendline(u'pip install /src')
|
||||
proc.sendline(u'pip install /src')
|
||||
assert proc.expect([TIMEOUT, u'Successfully installed'])
|
||||
proc.sendline(u'tcsh')
|
||||
proc.sendline(u'setenv PYTHONIOENCODING utf8')
|
||||
proc.sendline(u'eval `thefuck --alias`')
|
||||
@@ -31,25 +31,21 @@ def proc(request, spawnu, run_without_docker):
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_with_confirmation(proc, TIMEOUT):
|
||||
with_confirmation(proc, TIMEOUT)
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_select_command_with_arrows(proc, TIMEOUT):
|
||||
select_command_with_arrows(proc, TIMEOUT)
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_refuse_with_confirmation(proc, TIMEOUT):
|
||||
refuse_with_confirmation(proc, TIMEOUT)
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_without_confirmation(proc, TIMEOUT):
|
||||
without_confirmation(proc, TIMEOUT)
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@ containers = (('thefuck/ubuntu-python3-zsh',
|
||||
|
||||
|
||||
@pytest.fixture(params=containers)
|
||||
def proc(request, spawnu, run_without_docker):
|
||||
def proc(request, spawnu, TIMEOUT):
|
||||
proc = spawnu(*request.param)
|
||||
if not run_without_docker:
|
||||
proc.sendline(u'pip install /src')
|
||||
proc.sendline(u'pip install /src')
|
||||
assert proc.expect([TIMEOUT, u'Successfully installed'])
|
||||
proc.sendline(u'eval $(thefuck --alias)')
|
||||
proc.sendline(u'export HISTFILE=~/.zsh_history')
|
||||
proc.sendline(u'echo > $HISTFILE')
|
||||
@@ -35,34 +35,29 @@ def proc(request, spawnu, run_without_docker):
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_with_confirmation(proc, TIMEOUT):
|
||||
with_confirmation(proc, TIMEOUT)
|
||||
history_changed(proc, TIMEOUT, u'echo test')
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_select_command_with_arrows(proc, TIMEOUT):
|
||||
select_command_with_arrows(proc, TIMEOUT)
|
||||
history_changed(proc, TIMEOUT, u'git help')
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_refuse_with_confirmation(proc, TIMEOUT):
|
||||
refuse_with_confirmation(proc, TIMEOUT)
|
||||
history_not_changed(proc, TIMEOUT)
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_without_confirmation(proc, TIMEOUT):
|
||||
without_confirmation(proc, TIMEOUT)
|
||||
history_changed(proc, TIMEOUT, u'echo test')
|
||||
|
||||
|
||||
@pytest.mark.functional
|
||||
@pytest.mark.once_without_docker
|
||||
def test_how_to_configure_alias(proc, TIMEOUT):
|
||||
how_to_configure(proc, TIMEOUT)
|
||||
|
||||
@@ -1,68 +1,41 @@
|
||||
import pytest
|
||||
from mock import Mock, patch
|
||||
from thefuck.rules import apt_get
|
||||
from thefuck.rules.apt_get import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
# python-commandnotfound is available in ubuntu 14.04+
|
||||
@pytest.mark.skipif(not getattr(apt_get, 'enabled_by_default', True),
|
||||
reason='Skip if python-commandnotfound is not available')
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command(script='vim', stderr='vim: command not found')])
|
||||
def test_match(command):
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, return_value', [
|
||||
@pytest.mark.parametrize('command, packages', [
|
||||
(Command(script='vim', stderr='vim: command not found'),
|
||||
[('vim', 'main'), ('vim-tiny', 'main')]),
|
||||
(Command(script='sudo vim', stderr='vim: command not found'),
|
||||
[('vim', 'main'), ('vim-tiny', 'main')])])
|
||||
@patch('thefuck.rules.apt_get.CommandNotFound', create=True)
|
||||
@patch.multiple(apt_get, create=True, apt_get='apt_get')
|
||||
def test_match_mocked(cmdnf_mock, command, return_value):
|
||||
get_packages = Mock(return_value=return_value)
|
||||
cmdnf_mock.CommandNotFound.return_value = Mock(getPackages=get_packages)
|
||||
def test_match(mocker, command, packages):
|
||||
mocker.patch('thefuck.rules.apt_get.which', return_value=None)
|
||||
mock = mocker.patch('thefuck.rules.apt_get.command_not_found',
|
||||
create=True)
|
||||
mock.getPackages.return_value = packages
|
||||
|
||||
assert match(command)
|
||||
assert cmdnf_mock.CommandNotFound.called
|
||||
assert get_packages.called
|
||||
|
||||
|
||||
# python-commandnotfound is available in ubuntu 14.04+
|
||||
@pytest.mark.skipif(not getattr(apt_get, 'enabled_by_default', True),
|
||||
reason='Skip if python-commandnotfound is not available')
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command(script='a_bad_cmd', stderr='a_bad_cmd: command not found'),
|
||||
Command(script='vim', stderr=''), Command()])
|
||||
def test_not_match(command):
|
||||
@pytest.mark.parametrize('command, packages, which', [
|
||||
(Command(script='a_bad_cmd', stderr='a_bad_cmd: command not found'),
|
||||
[], None),
|
||||
(Command(script='vim', stderr=''), [], None),
|
||||
(Command(), [], None),
|
||||
(Command(script='vim', stderr='vim: command not found'),
|
||||
['vim'], '/usr/bin/vim'),
|
||||
(Command(script='sudo vim', stderr='vim: command not found'),
|
||||
['vim'], '/usr/bin/vim')])
|
||||
def test_not_match(mocker, command, packages, which):
|
||||
mocker.patch('thefuck.rules.apt_get.which', return_value=which)
|
||||
mock = mocker.patch('thefuck.rules.apt_get.command_not_found',
|
||||
create=True)
|
||||
mock.getPackages.return_value = packages
|
||||
|
||||
assert not match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, return_value', [
|
||||
(Command(script='a_bad_cmd', stderr='a_bad_cmd: command not found'), []),
|
||||
(Command(script='vim', stderr=''), []), (Command(), [])])
|
||||
@patch('thefuck.rules.apt_get.CommandNotFound', create=True)
|
||||
@patch.multiple(apt_get, create=True, apt_get='apt_get')
|
||||
def test_not_match_mocked(cmdnf_mock, command, return_value):
|
||||
get_packages = Mock(return_value=return_value)
|
||||
cmdnf_mock.CommandNotFound.return_value = Mock(getPackages=get_packages)
|
||||
assert not match(command)
|
||||
|
||||
|
||||
# python-commandnotfound is available in ubuntu 14.04+
|
||||
@pytest.mark.skipif(not getattr(apt_get, 'enabled_by_default', True),
|
||||
reason='Skip if python-commandnotfound is not available')
|
||||
@pytest.mark.parametrize('command, new_command', [
|
||||
(Command('vim'), 'sudo apt-get install vim && vim'),
|
||||
(Command('convert'), 'sudo apt-get install imagemagick && convert'),
|
||||
(Command('sudo vim'), 'sudo apt-get install vim && sudo vim'),
|
||||
(Command('sudo convert'), 'sudo apt-get install imagemagick && sudo convert')])
|
||||
def test_get_new_command(command, new_command):
|
||||
assert get_new_command(command) == new_command
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, new_command, return_value', [
|
||||
@pytest.mark.parametrize('command, new_command, packages', [
|
||||
(Command('vim'), 'sudo apt-get install vim && vim',
|
||||
[('vim', 'main'), ('vim-tiny', 'main')]),
|
||||
(Command('convert'), 'sudo apt-get install imagemagick && convert',
|
||||
@@ -73,9 +46,8 @@ def test_get_new_command(command, new_command):
|
||||
(Command('sudo convert'), 'sudo apt-get install imagemagick && sudo convert',
|
||||
[('imagemagick', 'main'),
|
||||
('graphicsmagick-imagemagick-compat', 'universe')])])
|
||||
@patch('thefuck.rules.apt_get.CommandNotFound', create=True)
|
||||
@patch.multiple(apt_get, create=True, apt_get='apt_get')
|
||||
def test_get_new_command_mocked(cmdnf_mock, command, new_command, return_value):
|
||||
get_packages = Mock(return_value=return_value)
|
||||
cmdnf_mock.CommandNotFound.return_value = Mock(getPackages=get_packages)
|
||||
def test_get_new_command(mocker, command, new_command, packages):
|
||||
mock = mocker.patch('thefuck.rules.apt_get.command_not_found',
|
||||
create=True)
|
||||
mock.getPackages.return_value = packages
|
||||
assert get_new_command(command) == new_command
|
||||
|
||||
@@ -3,19 +3,26 @@ from thefuck.rules.cargo_no_command import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
no_such_subcommand = """No such subcommand
|
||||
no_such_subcommand_old = """No such subcommand
|
||||
|
||||
Did you mean `build`?
|
||||
"""
|
||||
|
||||
no_such_subcommand = """error: no such subcommand
|
||||
|
||||
\tDid you mean `build`?
|
||||
"""
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command(script='cargo buid', stderr=no_such_subcommand)])
|
||||
Command(script='cargo buid', stderr=no_such_subcommand_old),
|
||||
Command(script='cargo buils', stderr=no_such_subcommand)])
|
||||
def test_match(command):
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, new_command', [
|
||||
(Command('cargo buid', stderr=no_such_subcommand), 'cargo build')])
|
||||
(Command('cargo buid', stderr=no_such_subcommand_old), 'cargo build'),
|
||||
(Command('cargo buils', stderr=no_such_subcommand), 'cargo build')])
|
||||
def test_get_new_command(command, new_command):
|
||||
assert get_new_command(command) == new_command
|
||||
|
||||
49
tests/rules/test_fab_command_not_found.py
Normal file
49
tests/rules/test_fab_command_not_found.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import pytest
|
||||
from thefuck.rules.fab_command_not_found import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
stderr = '''
|
||||
Warning: Command(s) not found:
|
||||
extenson
|
||||
deloyp
|
||||
'''
|
||||
stdout = '''
|
||||
Available commands:
|
||||
|
||||
update_config
|
||||
prepare_extension
|
||||
Template A string class for supporting $-substitutions.
|
||||
deploy
|
||||
glob Return a list of paths matching a pathname pattern.
|
||||
install_web
|
||||
set_version
|
||||
'''
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('fab extenson', stderr=stderr),
|
||||
Command('fab deloyp', stderr=stderr),
|
||||
Command('fab extenson deloyp', stderr=stderr)])
|
||||
def test_match(command):
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('gulp extenson', stderr=stderr),
|
||||
Command('fab deloyp')])
|
||||
def test_not_match(command):
|
||||
assert not match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script, result', [
|
||||
('fab extenson', 'fab prepare_extension'),
|
||||
('fab extenson:version=2016',
|
||||
'fab prepare_extension:version=2016'),
|
||||
('fab extenson:version=2016 install_web set_version:val=0.5.0',
|
||||
'fab prepare_extension:version=2016 install_web set_version:val=0.5.0'),
|
||||
('fab extenson:version=2016 deloyp:beta=true -H the.fuck',
|
||||
'fab prepare_extension:version=2016 deploy:beta=true -H the.fuck'),
|
||||
])
|
||||
def test_get_new_command(script, result):
|
||||
command = Command(script, stdout,stderr)
|
||||
assert get_new_command(command) == result
|
||||
@@ -12,22 +12,23 @@ def stderr(branch_name):
|
||||
def new_command(branch_name):
|
||||
return [cmd.format(branch_name) for cmd in [
|
||||
'git branch -d {0} && git branch {0}',
|
||||
'git branch -D {0} && git branch {0}', 'git checkout {0}']]
|
||||
'git branch -d {0} && git checkout -b {0}',
|
||||
'git branch -D {0} && git branch {0}',
|
||||
'git branch -D {0} && git checkout -b {0}', 'git checkout {0}']]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script, branch_name', [
|
||||
('git branch foo', 'foo'),
|
||||
('git branch bar', 'bar')])
|
||||
('git branch foo', 'foo'), ('git checkout bar', 'bar')])
|
||||
def test_match(stderr, script, branch_name):
|
||||
assert match(Command(script=script, stderr=stderr))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script', ['git branch foo', 'git branch bar'])
|
||||
@pytest.mark.parametrize('script', ['git branch foo', 'git checkout bar'])
|
||||
def test_not_match(script):
|
||||
assert not match(Command(script=script, stderr=''))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script, branch_name, ', [
|
||||
('git branch foo', 'foo'), ('git branch bar', 'bar')])
|
||||
('git branch foo', 'foo'), ('git checkout bar', 'bar')])
|
||||
def test_get_new_command(stderr, new_command, script, branch_name):
|
||||
assert get_new_command(Command(script=script, stderr=stderr)) == new_command
|
||||
|
||||
@@ -13,6 +13,17 @@ To /tmp/foo
|
||||
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
|
||||
'''
|
||||
|
||||
git_err2 = '''
|
||||
To /tmp/foo
|
||||
! [rejected] master -> master (non-fast-forward)
|
||||
error: failed to push some refs to '/tmp/bar'
|
||||
hint: Updates were rejected because the remote contains work that you do
|
||||
hint: not have locally. This is usually caused by another repository pushing
|
||||
hint: to the same ref. You may want to first integrate the remote changes
|
||||
hint: (e.g., 'git pull ...') before pushing again.
|
||||
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
|
||||
'''
|
||||
|
||||
git_uptodate = 'Everything up-to-date'
|
||||
git_ok = '''
|
||||
Counting objects: 3, done.
|
||||
@@ -33,6 +44,14 @@ def test_match(command):
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command(script='git push', stderr=git_err2),
|
||||
Command(script='git push nvbn', stderr=git_err2),
|
||||
Command(script='git push nvbn master', stderr=git_err2)])
|
||||
def test_match(command):
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command(script='git push', stderr=git_ok),
|
||||
Command(script='git push', stderr=git_uptodate),
|
||||
@@ -52,3 +71,13 @@ def test_not_match(command):
|
||||
'git pull nvbn master && git push nvbn master')])
|
||||
def test_get_new_command(command, output):
|
||||
assert get_new_command(command) == output
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, output', [
|
||||
(Command(script='git push', stderr=git_err2), 'git pull && git push'),
|
||||
(Command(script='git push nvbn', stderr=git_err2),
|
||||
'git pull nvbn && git push nvbn'),
|
||||
(Command(script='git push nvbn master', stderr=git_err2),
|
||||
'git pull nvbn master && git push nvbn master')])
|
||||
def test_get_new_command(command, output):
|
||||
assert get_new_command(command) == output
|
||||
|
||||
158
tests/rules/test_gradle_not_task.py
Normal file
158
tests/rules/test_gradle_not_task.py
Normal file
@@ -0,0 +1,158 @@
|
||||
import pytest
|
||||
from io import BytesIO
|
||||
from thefuck.rules.gradle_no_task import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
gradle_tasks = b'''
|
||||
:tasks
|
||||
|
||||
------------------------------------------------------------
|
||||
All tasks runnable from root project
|
||||
------------------------------------------------------------
|
||||
|
||||
Android tasks
|
||||
-------------
|
||||
androidDependencies - Displays the Android dependencies of the project.
|
||||
signingReport - Displays the signing info for each variant.
|
||||
sourceSets - Prints out all the source sets defined in this project.
|
||||
|
||||
Build tasks
|
||||
-----------
|
||||
assemble - Assembles all variants of all applications and secondary packages.
|
||||
assembleAndroidTest - Assembles all the Test applications.
|
||||
assembleDebug - Assembles all Debug builds.
|
||||
assembleRelease - Assembles all Release builds.
|
||||
build - Assembles and tests this project.
|
||||
buildDependents - Assembles and tests this project and all projects that depend on it.
|
||||
buildNeeded - Assembles and tests this project and all projects it depends on.
|
||||
compileDebugAndroidTestSources
|
||||
compileDebugSources
|
||||
compileDebugUnitTestSources
|
||||
compileReleaseSources
|
||||
compileReleaseUnitTestSources
|
||||
extractDebugAnnotations - Extracts Android annotations for the debug variant into the archive file
|
||||
extractReleaseAnnotations - Extracts Android annotations for the release variant into the archive file
|
||||
mockableAndroidJar - Creates a version of android.jar that's suitable for unit tests.
|
||||
|
||||
Build Setup tasks
|
||||
-----------------
|
||||
init - Initializes a new Gradle build. [incubating]
|
||||
wrapper - Generates Gradle wrapper files. [incubating]
|
||||
|
||||
Help tasks
|
||||
----------
|
||||
components - Displays the components produced by root project 'org.rerenderer_example.snake'. [incubating]
|
||||
dependencies - Displays all dependencies declared in root project 'org.rerenderer_example.snake'.
|
||||
dependencyInsight - Displays the insight into a specific dependency in root project 'org.rerenderer_example.snake'.
|
||||
help - Displays a help message.
|
||||
model - Displays the configuration model of root project 'org.rerenderer_example.snake'. [incubating]
|
||||
projects - Displays the sub-projects of root project 'org.rerenderer_example.snake'.
|
||||
properties - Displays the properties of root project 'org.rerenderer_example.snake'.
|
||||
tasks - Displays the tasks runnable from root project 'org.rerenderer_example.snake' (some of the displayed tasks may belong to subprojects).
|
||||
|
||||
Install tasks
|
||||
-------------
|
||||
installDebug - Installs the Debug build.
|
||||
installDebugAndroidTest - Installs the android (on device) tests for the Debug build.
|
||||
installRelease - Installs the Release build.
|
||||
uninstallAll - Uninstall all applications.
|
||||
uninstallDebug - Uninstalls the Debug build.
|
||||
uninstallDebugAndroidTest - Uninstalls the android (on device) tests for the Debug build.
|
||||
uninstallRelease - Uninstalls the Release build.
|
||||
|
||||
React tasks
|
||||
-----------
|
||||
bundleDebugJsAndAssets - bundle JS and assets for Debug.
|
||||
bundleReleaseJsAndAssets - bundle JS and assets for Release.
|
||||
|
||||
Verification tasks
|
||||
------------------
|
||||
check - Runs all checks.
|
||||
clean - Deletes the build directory.
|
||||
connectedAndroidTest - Installs and runs instrumentation tests for all flavors on connected devices.
|
||||
connectedCheck - Runs all device checks on currently connected devices.
|
||||
connectedDebugAndroidTest - Installs and runs the tests for debug on connected devices.
|
||||
deviceAndroidTest - Installs and runs instrumentation tests using all Device Providers.
|
||||
deviceCheck - Runs all device checks using Device Providers and Test Servers.
|
||||
lint - Runs lint on all variants.
|
||||
lintDebug - Runs lint on the Debug build.
|
||||
lintRelease - Runs lint on the Release build.
|
||||
test - Run unit tests for all variants.
|
||||
testDebugUnitTest - Run unit tests for the debug build.
|
||||
testReleaseUnitTest - Run unit tests for the release build.
|
||||
|
||||
Other tasks
|
||||
-----------
|
||||
assembleDefault
|
||||
copyDownloadableDepsToLibs
|
||||
jarDebugClasses
|
||||
jarReleaseClasses
|
||||
|
||||
To see all tasks and more detail, run gradlew tasks --all
|
||||
|
||||
To see more detail about a task, run gradlew help --task <task>
|
||||
|
||||
BUILD SUCCESSFUL
|
||||
|
||||
Total time: 1.936 secs
|
||||
'''
|
||||
|
||||
stderr_not_found = '''
|
||||
|
||||
FAILURE: Build failed with an exception.
|
||||
|
||||
* What went wrong:
|
||||
Task '{}' not found in root project 'org.rerenderer_example.snake'.
|
||||
|
||||
* Try:
|
||||
Run gradlew tasks to get a list of available tasks. Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
|
||||
'''.format
|
||||
|
||||
stderr_ambiguous = '''
|
||||
|
||||
FAILURE: Build failed with an exception.
|
||||
|
||||
* What went wrong:
|
||||
Task '{}' is ambiguous in root project 'org.rerenderer_example.snake'. Candidates are: 'assembleRelease', 'assembleReleaseUnitTest'.
|
||||
|
||||
* Try:
|
||||
Run gradlew tasks to get a list of available tasks. Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
|
||||
'''.format
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def tasks(mocker):
|
||||
patch = mocker.patch('thefuck.rules.gradle_no_task.Popen')
|
||||
patch.return_value.stdout = BytesIO(gradle_tasks)
|
||||
return patch
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('./gradlew assembler', stderr=stderr_ambiguous('assembler')),
|
||||
Command('./gradlew instar', stderr=stderr_not_found('instar')),
|
||||
Command('gradle assembler', stderr=stderr_ambiguous('assembler')),
|
||||
Command('gradle instar', stderr=stderr_not_found('instar'))])
|
||||
def test_match(command):
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('./gradlew assemble'),
|
||||
Command('gradle assemble'),
|
||||
Command('npm assembler', stderr=stderr_ambiguous('assembler')),
|
||||
Command('npm instar', stderr=stderr_not_found('instar'))])
|
||||
def test_not_match(command):
|
||||
assert not match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, result', [
|
||||
(Command('./gradlew assembler', stderr=stderr_ambiguous('assembler')),
|
||||
'./gradlew assemble'),
|
||||
(Command('./gradlew instardebug', stderr=stderr_not_found('instardebug')),
|
||||
'./gradlew installDebug'),
|
||||
(Command('gradle assembler', stderr=stderr_ambiguous('assembler')),
|
||||
'gradle assemble'),
|
||||
(Command('gradle instardebug', stderr=stderr_not_found('instardebug')),
|
||||
'gradle installDebug')])
|
||||
def test_get_new_command(command, result):
|
||||
assert get_new_command(command)[0] == result
|
||||
38
tests/rules/test_gradle_wrapper.py
Normal file
38
tests/rules/test_gradle_wrapper.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import pytest
|
||||
from thefuck.rules.gradle_wrapper import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def exists(mocker):
|
||||
return mocker.patch('thefuck.rules.gradle_wrapper.os.path.isfile',
|
||||
return_value=True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('gradle tasks', stderr='gradle: not found'),
|
||||
Command('gradle build', stderr='gradle: not found')])
|
||||
def test_match(mocker, command):
|
||||
mocker.patch('thefuck.rules.gradle_wrapper.which', return_value=None)
|
||||
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, gradlew, which', [
|
||||
(Command('gradle tasks', stderr='gradle: not found'), False, None),
|
||||
(Command('gradle tasks', stderr='command not found'), True, '/usr/bin/gradle'),
|
||||
(Command('npm tasks', stderr='npm: not found'), True, None)])
|
||||
def test_not_match(mocker, exists, command, gradlew, which):
|
||||
mocker.patch('thefuck.rules.gradle_wrapper.which', return_value=which)
|
||||
exists.return_value = gradlew
|
||||
|
||||
assert not match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script, result', [
|
||||
('gradle assemble', './gradlew assemble'),
|
||||
('gradle --help', './gradlew --help'),
|
||||
('gradle build -c', './gradlew build -c')])
|
||||
def test_get_new_command(script, result):
|
||||
command = Command(script)
|
||||
assert get_new_command(command) == result
|
||||
129
tests/rules/test_grunt_task_not_found.py
Normal file
129
tests/rules/test_grunt_task_not_found.py
Normal file
@@ -0,0 +1,129 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from io import BytesIO
|
||||
import pytest
|
||||
from tests.utils import Command
|
||||
from thefuck.rules.grunt_task_not_found import match, get_new_command
|
||||
|
||||
stdout = '''
|
||||
Warning: Task "{}" not found. Use --force to continue.
|
||||
|
||||
Aborted due to warnings.
|
||||
|
||||
|
||||
Execution Time (2016-08-13 21:01:40 UTC+3)
|
||||
loading tasks 11ms ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 92%
|
||||
Total 12ms
|
||||
|
||||
'''.format
|
||||
|
||||
grunt_help_stdout = b'''
|
||||
Grunt: The JavaScript Task Runner (v0.4.5)
|
||||
|
||||
Usage
|
||||
grunt [options] [task [task ...]]
|
||||
|
||||
Options
|
||||
--help, -h Display this help text.
|
||||
--base Specify an alternate base path. By default, all file paths are
|
||||
relative to the Gruntfile. (grunt.file.setBase) *
|
||||
--no-color Disable colored output.
|
||||
--gruntfile Specify an alternate Gruntfile. By default, grunt looks in the
|
||||
current or parent directories for the nearest Gruntfile.js or
|
||||
Gruntfile.coffee file.
|
||||
--debug, -d Enable debugging mode for tasks that support it.
|
||||
--stack Print a stack trace when exiting with a warning or fatal error.
|
||||
--force, -f A way to force your way past warnings. Want a suggestion? Don't
|
||||
use this option, fix your code.
|
||||
--tasks Additional directory paths to scan for task and "extra" files.
|
||||
(grunt.loadTasks) *
|
||||
--npm Npm-installed grunt plugins to scan for task and "extra" files.
|
||||
(grunt.loadNpmTasks) *
|
||||
--no-write Disable writing files (dry run).
|
||||
--verbose, -v Verbose mode. A lot more information output.
|
||||
--version, -V Print the grunt version. Combine with --verbose for more info.
|
||||
--completion Output shell auto-completion rules. See the grunt-cli
|
||||
documentation for more information.
|
||||
|
||||
Options marked with * have methods exposed via the grunt API and should instead
|
||||
be specified inside the Gruntfile wherever possible.
|
||||
|
||||
Available tasks
|
||||
autoprefixer Prefix CSS files. *
|
||||
concurrent Run grunt tasks concurrently *
|
||||
clean Clean files and folders. *
|
||||
compass Compile Sass to CSS using Compass *
|
||||
concat Concatenate files. *
|
||||
connect Start a connect web server. *
|
||||
copy Copy files. *
|
||||
cssmin Minify CSS *
|
||||
htmlmin Minify HTML *
|
||||
imagemin Minify PNG, JPEG, GIF and SVG images *
|
||||
jshint Validate files with JSHint. *
|
||||
uglify Minify files with UglifyJS. *
|
||||
watch Run predefined tasks whenever watched files change.
|
||||
filerev File revisioning based on content hashing *
|
||||
cdnify Replace scripts with refs to the Google CDN *
|
||||
karma run karma. *
|
||||
newer Run a task with only those source files that have been modified
|
||||
since the last successful run.
|
||||
any-newer DEPRECATED TASK. Use the "newer" task instead
|
||||
newer-postrun Internal task.
|
||||
newer-clean Remove cached timestamps.
|
||||
ngAnnotate Add, remove and rebuild AngularJS dependency injection
|
||||
annotations *
|
||||
ngconstant Dynamic angular constant generator task. *
|
||||
svgmin Minify SVG *
|
||||
usemin Replaces references to non-minified scripts / stylesheets *
|
||||
useminPrepare Using HTML markup as the primary source of information *
|
||||
wiredep Inject Bower components into your source code. *
|
||||
serve Compile then start a connect web server
|
||||
server DEPRECATED TASK. Use the "serve" task instead
|
||||
test Alias for "clean:server", "ngconstant:test", "wiredep",
|
||||
"concurrent:test", "autoprefixer", "connect:test", "karma"
|
||||
tasks.
|
||||
build Alias for "ngconstant:production", "clean:dist", "wiredep",
|
||||
"useminPrepare", "concurrent:dist", "autoprefixer", "concat",
|
||||
"ngAnnotate", "copy:dist", "cdnify", "cssmin", "uglify",
|
||||
"filerev", "usemin", "htmlmin" tasks.
|
||||
default Alias for "newer:jshint", "test", "build" tasks.
|
||||
|
||||
Tasks run in the order specified. Arguments may be passed to tasks that accept
|
||||
them by using colons, like "lint:files". Tasks marked with * are "multi tasks"
|
||||
and will iterate over all sub-targets if no argument is specified.
|
||||
|
||||
The list of available tasks may change based on tasks directories or grunt
|
||||
plugins specified in the Gruntfile or via command-line options.
|
||||
|
||||
For more information, see http://gruntjs.com/
|
||||
'''
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def grunt_help(mocker):
|
||||
patch = mocker.patch('thefuck.rules.grunt_task_not_found.Popen')
|
||||
patch.return_value.stdout = BytesIO(grunt_help_stdout)
|
||||
return patch
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('grunt defualt', stdout('defualt')),
|
||||
Command('grunt buld:css', stdout('buld:css'))])
|
||||
def test_match(command):
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('npm nuild', stdout('nuild')),
|
||||
Command('grunt rm')])
|
||||
def test_not_match(command):
|
||||
assert not match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, result', [
|
||||
(Command('grunt defualt', stdout('defualt')), 'grunt default'),
|
||||
(Command('grunt cmpass:all', stdout('cmpass:all')), 'grunt compass:all'),
|
||||
(Command('grunt cmpass:all --color', stdout('cmpass:all')),
|
||||
'grunt compass:all --color')])
|
||||
def test_get_new_command(command, result):
|
||||
assert get_new_command(command) == result
|
||||
@@ -21,15 +21,20 @@ def history_without_current(mocker):
|
||||
('vom file.py', 'vom: not found'),
|
||||
('fucck', 'fucck: not found'),
|
||||
('got commit', 'got: command not found')])
|
||||
def test_match(script, stderr):
|
||||
def test_match(mocker, script, stderr):
|
||||
mocker.patch('thefuck.rules.no_command.which', return_value=None)
|
||||
|
||||
assert match(Command(script, stderr=stderr))
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('no_memoize')
|
||||
@pytest.mark.parametrize('script, stderr', [
|
||||
('qweqwe', 'qweqwe: not found'),
|
||||
('vom file.py', 'some text')])
|
||||
def test_not_match(script, stderr):
|
||||
@pytest.mark.parametrize('script, stderr, which', [
|
||||
('qweqwe', 'qweqwe: not found', None),
|
||||
('vom file.py', 'some text', None),
|
||||
('vim file.py', 'vim: not found', 'vim')])
|
||||
def test_not_match(mocker, script, stderr, which):
|
||||
mocker.patch('thefuck.rules.no_command.which', return_value=which)
|
||||
|
||||
assert not match(Command(script, stderr=stderr))
|
||||
|
||||
|
||||
|
||||
69
tests/rules/test_npm_missing_script.py
Normal file
69
tests/rules/test_npm_missing_script.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import pytest
|
||||
from io import BytesIO
|
||||
from tests.utils import Command
|
||||
from thefuck.rules.npm_missing_script import match, get_new_command
|
||||
|
||||
stderr = '''
|
||||
npm ERR! Linux 4.4.0-31-generic
|
||||
npm ERR! argv "/opt/node/bin/node" "/opt/node/bin/npm" "run" "dvelop"
|
||||
npm ERR! node v4.4.7
|
||||
npm ERR! npm v2.15.8
|
||||
|
||||
npm ERR! missing script: {}
|
||||
npm ERR!
|
||||
npm ERR! If you need help, you may report this error at:
|
||||
npm ERR! <https://github.com/npm/npm/issues>
|
||||
|
||||
npm ERR! Please include the following file with any support request:
|
||||
npm ERR! /home/nvbn/exp/code_view/client_web/npm-debug.log
|
||||
'''.format
|
||||
|
||||
run_script_stdout = b'''
|
||||
Lifecycle scripts included in code-view-web:
|
||||
test
|
||||
jest
|
||||
|
||||
available via `npm run-script`:
|
||||
build
|
||||
cp node_modules/ace-builds/src-min/ -a resources/ace/ && webpack --progress --colors -p --config ./webpack.production.config.js
|
||||
develop
|
||||
cp node_modules/ace-builds/src/ -a resources/ace/ && webpack-dev-server --progress --colors
|
||||
watch-test
|
||||
jest --verbose --watch
|
||||
|
||||
'''
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def run_script(mocker):
|
||||
patch = mocker.patch('thefuck.specific.npm.Popen')
|
||||
patch.return_value.stdout = BytesIO(run_script_stdout)
|
||||
return patch.return_value
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('npm ru wach', stderr=stderr('wach')),
|
||||
Command('npm run live-tes', stderr=stderr('live-tes')),
|
||||
Command('npm run-script sahare', stderr=stderr('sahare'))])
|
||||
def test_match(command):
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('npm wach', stderr=stderr('wach')),
|
||||
Command('vim live-tes', stderr=stderr('live-tes')),
|
||||
Command('npm run-script sahare')])
|
||||
def test_not_match(command):
|
||||
assert not match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script, stderr, result', [
|
||||
('npm ru wach-tests', stderr('wach-tests'), 'npm ru watch-test'),
|
||||
('npm -i run-script dvelop', stderr('dvelop'),
|
||||
'npm -i run-script develop'),
|
||||
('npm -i run-script buld -X POST', stderr('buld'),
|
||||
'npm -i run-script build -X POST')])
|
||||
def test_get_new_command(script, stderr, result):
|
||||
command = Command(script, stderr=stderr)
|
||||
|
||||
assert get_new_command(command)[0] == result
|
||||
84
tests/rules/test_npm_run_script.py
Normal file
84
tests/rules/test_npm_run_script.py
Normal file
@@ -0,0 +1,84 @@
|
||||
import pytest
|
||||
from io import BytesIO
|
||||
from thefuck.rules.npm_run_script import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
stdout = '''
|
||||
Usage: npm <command>
|
||||
|
||||
where <command> is one of:
|
||||
access, add-user, adduser, apihelp, author, bin, bugs, c,
|
||||
cache, completion, config, ddp, dedupe, deprecate, dist-tag,
|
||||
dist-tags, docs, edit, explore, faq, find, find-dupes, get,
|
||||
help, help-search, home, i, info, init, install, issues, la,
|
||||
link, list, ll, ln, login, logout, ls, outdated, owner,
|
||||
pack, ping, prefix, prune, publish, r, rb, rebuild, remove,
|
||||
repo, restart, rm, root, run-script, s, se, search, set,
|
||||
show, shrinkwrap, star, stars, start, stop, t, tag, team,
|
||||
test, tst, un, uninstall, unlink, unpublish, unstar, up,
|
||||
update, upgrade, v, version, view, whoami
|
||||
|
||||
npm <cmd> -h quick help on <cmd>
|
||||
npm -l display full usage info
|
||||
npm faq commonly asked questions
|
||||
npm help <term> search for help on <term>
|
||||
npm help npm involved overview
|
||||
|
||||
Specify configs in the ini-formatted file:
|
||||
/home/nvbn/.npmrc
|
||||
or on the command line via: npm <command> --key value
|
||||
Config info can be viewed via: npm help config
|
||||
|
||||
'''
|
||||
|
||||
run_script_stdout = b'''
|
||||
Lifecycle scripts included in code-view-web:
|
||||
test
|
||||
jest
|
||||
|
||||
available via `npm run-script`:
|
||||
build
|
||||
cp node_modules/ace-builds/src-min/ -a resources/ace/ && webpack --progress --colors -p --config ./webpack.production.config.js
|
||||
develop
|
||||
cp node_modules/ace-builds/src/ -a resources/ace/ && webpack-dev-server --progress --colors
|
||||
watch-test
|
||||
jest --verbose --watch
|
||||
|
||||
'''
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def run_script(mocker):
|
||||
patch = mocker.patch('thefuck.specific.npm.Popen')
|
||||
patch.return_value.stdout = BytesIO(run_script_stdout)
|
||||
return patch.return_value
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('no_memoize')
|
||||
@pytest.mark.parametrize('script', [
|
||||
'npm watch-test', 'npm develop'])
|
||||
def test_match(script):
|
||||
command = Command(script, stdout)
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('no_memoize')
|
||||
@pytest.mark.parametrize('command, run_script_out', [
|
||||
(Command('npm test', 'TEST FAIL'), run_script_stdout),
|
||||
(Command('npm watch-test', 'TEST FAIL'), run_script_stdout),
|
||||
(Command('npm test', stdout), run_script_stdout),
|
||||
(Command('vim watch-test', stdout), run_script_stdout)])
|
||||
def test_not_match(run_script, command, run_script_out):
|
||||
run_script.stdout = BytesIO(run_script_out)
|
||||
assert not match(command)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('no_memoize')
|
||||
@pytest.mark.parametrize('script, result', [
|
||||
('npm watch-test', 'npm run-script watch-test'),
|
||||
('npm -i develop', 'npm run-script -i develop'),
|
||||
('npm -i watch-script --path ..',
|
||||
'npm run-script -i watch-script --path ..')])
|
||||
def test_get_new_command(script, result):
|
||||
command = Command(script, stdout)
|
||||
assert get_new_command(command) == result
|
||||
@@ -1,31 +1,49 @@
|
||||
import pytest
|
||||
from thefuck.rules.open import match, get_new_command
|
||||
from thefuck.rules.open import is_arg_url, match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command(script='open foo.com'),
|
||||
Command(script='open foo.ly'),
|
||||
Command(script='open foo.org'),
|
||||
Command(script='open foo.net'),
|
||||
Command(script='open foo.se'),
|
||||
Command(script='open foo.io'),
|
||||
Command(script='xdg-open foo.com'),
|
||||
Command(script='gnome-open foo.com'),
|
||||
Command(script='kde-open foo.com')])
|
||||
def test_match(command):
|
||||
assert match(command)
|
||||
@pytest.fixture
|
||||
def stderr(script):
|
||||
return 'The file {} does not exist.\n'.format(script.split(' ', 1)[1])
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, new_command', [
|
||||
(Command('open foo.com'), 'open http://foo.com'),
|
||||
(Command('open foo.ly'), 'open http://foo.ly'),
|
||||
(Command('open foo.org'), 'open http://foo.org'),
|
||||
(Command('open foo.net'), 'open http://foo.net'),
|
||||
(Command('open foo.se'), 'open http://foo.se'),
|
||||
(Command('open foo.io'), 'open http://foo.io'),
|
||||
(Command('xdg-open foo.io'), 'xdg-open http://foo.io'),
|
||||
(Command('gnome-open foo.io'), 'gnome-open http://foo.io'),
|
||||
(Command('kde-open foo.io'), 'kde-open http://foo.io')])
|
||||
def test_get_new_command(command, new_command):
|
||||
assert get_new_command(command) == new_command
|
||||
@pytest.mark.parametrize('script', [
|
||||
'open foo.com',
|
||||
'open foo.edu',
|
||||
'open foo.info',
|
||||
'open foo.io',
|
||||
'open foo.ly',
|
||||
'open foo.me',
|
||||
'open foo.net',
|
||||
'open foo.org',
|
||||
'open foo.se',
|
||||
'open www.foo.ru'])
|
||||
def test_is_arg_url(script):
|
||||
assert is_arg_url(Command(script))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script', ['open foo', 'open bar.txt', 'open egg.doc'])
|
||||
def test_not_is_arg_url(script):
|
||||
assert not is_arg_url(Command(script))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script', [
|
||||
'open foo.com',
|
||||
'xdg-open foo.com',
|
||||
'gnome-open foo.com',
|
||||
'kde-open foo.com',
|
||||
'open nonest'])
|
||||
def test_match(script, stderr):
|
||||
assert match(Command(script, stderr=stderr))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script, new_command', [
|
||||
('open foo.io', ['open http://foo.io']),
|
||||
('xdg-open foo.io', ['xdg-open http://foo.io']),
|
||||
('gnome-open foo.io', ['gnome-open http://foo.io']),
|
||||
('kde-open foo.io', ['kde-open http://foo.io']),
|
||||
('open nonest', ['touch nonest && open nonest',
|
||||
'mkdir nonest && open nonest'])])
|
||||
def test_get_new_command(script, new_command, stderr):
|
||||
assert get_new_command(Command(script, stderr=stderr)) == new_command
|
||||
|
||||
101
tests/rules/test_port_already_in_use.py
Normal file
101
tests/rules/test_port_already_in_use.py
Normal file
@@ -0,0 +1,101 @@
|
||||
from io import BytesIO
|
||||
|
||||
import pytest
|
||||
from thefuck.rules.port_already_in_use import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
outputs = [
|
||||
'''
|
||||
|
||||
DE 70% 1/1 build modulesevents.js:141
|
||||
throw er; // Unhandled 'error' event
|
||||
^
|
||||
|
||||
Error: listen EADDRINUSE 127.0.0.1:8080
|
||||
at Object.exports._errnoException (util.js:873:11)
|
||||
at exports._exceptionWithHostPort (util.js:896:20)
|
||||
at Server._listen2 (net.js:1250:14)
|
||||
at listen (net.js:1286:10)
|
||||
at net.js:1395:9
|
||||
at GetAddrInfoReqWrap.asyncCallback [as callback] (dns.js:64:16)
|
||||
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:83:10)
|
||||
|
||||
''',
|
||||
'''
|
||||
[6:40:01 AM] <START> Building Dependency Graph
|
||||
[6:40:01 AM] <START> Crawling File System
|
||||
ERROR Packager can't listen on port 8080
|
||||
Most likely another process is already using this port
|
||||
Run the following command to find out which process:
|
||||
|
||||
lsof -n -i4TCP:8080
|
||||
|
||||
You can either shut down the other process:
|
||||
|
||||
kill -9 <PID>
|
||||
|
||||
or run packager on different port.
|
||||
|
||||
''',
|
||||
'''
|
||||
Traceback (most recent call last):
|
||||
File "/usr/lib/python3.5/runpy.py", line 184, in _run_module_as_main
|
||||
"__main__", mod_spec)
|
||||
File "/usr/lib/python3.5/runpy.py", line 85, in _run_code
|
||||
exec(code, run_globals)
|
||||
File "/home/nvbn/exp/code_view/server/code_view/main.py", line 14, in <module>
|
||||
web.run_app(app)
|
||||
File "/home/nvbn/.virtualenvs/code_view/lib/python3.5/site-packages/aiohttp/web.py", line 310, in run_app
|
||||
backlog=backlog))
|
||||
File "/usr/lib/python3.5/asyncio/base_events.py", line 373, in run_until_complete
|
||||
return future.result()
|
||||
File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
|
||||
raise self._exception
|
||||
File "/usr/lib/python3.5/asyncio/tasks.py", line 240, in _step
|
||||
result = coro.send(None)
|
||||
File "/usr/lib/python3.5/asyncio/base_events.py", line 953, in create_server
|
||||
% (sa, err.strerror.lower()))
|
||||
OSError: [Errno 98] error while attempting to bind on address ('0.0.0.0', 8080): address already in use
|
||||
Task was destroyed but it is pending!
|
||||
task: <Task pending coro=<RedisProtocol._reader_coroutine() running at /home/nvbn/.virtualenvs/code_view/lib/python3.5/site-packages/asyncio_redis/protocol.py:921> wait_for=<Future pending cb=[Task._wakeup()]>>
|
||||
'''
|
||||
]
|
||||
|
||||
lsof_stdout = b'''COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
|
||||
node 18233 nvbn 16u IPv4 557134 0t0 TCP localhost:http-alt (LISTEN)
|
||||
'''
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def lsof(mocker):
|
||||
patch = mocker.patch('thefuck.rules.port_already_in_use.Popen')
|
||||
patch.return_value.stdout = BytesIO(lsof_stdout)
|
||||
return patch
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('no_memoize')
|
||||
@pytest.mark.parametrize(
|
||||
'command',
|
||||
[Command('./app', stdout=output) for output in outputs]
|
||||
+ [Command('./app', stderr=output) for output in outputs])
|
||||
def test_match(command):
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('no_memoize')
|
||||
@pytest.mark.parametrize('command, lsof_output', [
|
||||
(Command('./app'), lsof_stdout),
|
||||
(Command('./app', stdout=outputs[1]), b''),
|
||||
(Command('./app', stderr=outputs[2]), b'')])
|
||||
def test_not_match(lsof, command, lsof_output):
|
||||
lsof.return_value.stdout = BytesIO(lsof_output)
|
||||
|
||||
assert not match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'command',
|
||||
[Command('./app', stdout=output) for output in outputs]
|
||||
+ [Command('./app', stderr=output) for output in outputs])
|
||||
def test_get_new_command(command):
|
||||
assert get_new_command(command) == 'kill 18233 && ./app'
|
||||
46
tests/rules/test_react_native_command_unrecognized.py
Normal file
46
tests/rules/test_react_native_command_unrecognized.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import pytest
|
||||
from thefuck.rules.react_native_command_unrecognized import match, \
|
||||
get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
stderr = 'Command `{}` unrecognized'.format
|
||||
|
||||
stdout = '''
|
||||
Usage: react-native <command>
|
||||
|
||||
Commands:
|
||||
- start: starts the webserver
|
||||
- bundle: builds the javascript bundle for offline use
|
||||
- unbundle: builds javascript as "unbundle" for offline use
|
||||
- new-library: generates a native library bridge
|
||||
- android: generates an Android project for your app
|
||||
- run-android: builds your app and starts it on a connected Android emulator or device
|
||||
- log-android: print Android logs
|
||||
- run-ios: builds your app and starts it on iOS simulator
|
||||
- log-ios: print iOS logs
|
||||
- upgrade: upgrade your app's template files to the latest version; run this after updating the react-native version in your package.json and running npm install
|
||||
- link: link a library
|
||||
'''
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('react-native star', stderr=stderr('star')),
|
||||
Command('react-native android-logs', stderr=stderr('android-logs'))])
|
||||
def test_match(command):
|
||||
assert match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command', [
|
||||
Command('gradle star', stderr=stderr('star')),
|
||||
Command('react-native start')])
|
||||
def test_not_match(command):
|
||||
assert not match(command)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('command, result', [
|
||||
(Command('react-native star', stdout, stderr('star')),
|
||||
'react-native start'),
|
||||
(Command('react-native logsandroid -f', stdout, stderr('logsandroid')),
|
||||
'react-native log-android -f')])
|
||||
def test_get_new_command(command, result):
|
||||
assert get_new_command(command)[0] == result
|
||||
30
tests/rules/test_workon_doesnt_exists.py
Normal file
30
tests/rules/test_workon_doesnt_exists.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import pytest
|
||||
from thefuck.rules.workon_doesnt_exists import match, get_new_command
|
||||
from tests.utils import Command
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def envs(mocker):
|
||||
return mocker.patch(
|
||||
'thefuck.rules.workon_doesnt_exists._get_all_environments',
|
||||
return_value=['thefuck', 'code_view'])
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script', [
|
||||
'workon tehfuck', 'workon code-view', 'workon new-env'])
|
||||
def test_match(script):
|
||||
assert match(Command(script))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script', [
|
||||
'workon thefuck', 'workon code_view', 'work on tehfuck'])
|
||||
def test_not_match(script):
|
||||
assert not match(Command(script))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('script, result', [
|
||||
('workon tehfuck', 'workon thefuck'),
|
||||
('workon code-view', 'workon code_view'),
|
||||
('workon zzzz', 'mkvirtualenv zzzz')])
|
||||
def test_get_new_command(script, result):
|
||||
assert get_new_command(Command(script))[0] == result
|
||||
26
tests/specific/test_npm.py
Normal file
26
tests/specific/test_npm.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from io import BytesIO
|
||||
|
||||
import pytest
|
||||
from thefuck.specific.npm import get_scripts
|
||||
|
||||
run_script_stdout = b'''
|
||||
Lifecycle scripts included in code-view-web:
|
||||
test
|
||||
jest
|
||||
|
||||
available via `npm run-script`:
|
||||
build
|
||||
cp node_modules/ace-builds/src-min/ -a resources/ace/ && webpack --progress --colors -p --config ./webpack.production.config.js
|
||||
develop
|
||||
cp node_modules/ace-builds/src/ -a resources/ace/ && webpack-dev-server --progress --colors
|
||||
watch-test
|
||||
jest --verbose --watch
|
||||
|
||||
'''
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('no_memoize')
|
||||
def test_get_scripts(mocker):
|
||||
patch = mocker.patch('thefuck.specific.npm.Popen')
|
||||
patch.return_value.stdout = BytesIO(run_script_stdout)
|
||||
assert get_scripts() == ['build', 'develop', 'watch-test']
|
||||
@@ -59,7 +59,9 @@ class TestSettingsFromEnv(object):
|
||||
'THEFUCK_WAIT_COMMAND': '55',
|
||||
'THEFUCK_REQUIRE_CONFIRMATION': 'true',
|
||||
'THEFUCK_NO_COLORS': 'false',
|
||||
'THEFUCK_PRIORITY': 'bash=10:lisp=wrong:vim=15'})
|
||||
'THEFUCK_PRIORITY': 'bash=10:lisp=wrong:vim=15',
|
||||
'THEFUCK_WAIT_SLOW_COMMAND': '999',
|
||||
'THEFUCK_SLOW_COMMANDS': 'lein:react-native:./gradlew'})
|
||||
settings.init()
|
||||
assert settings.rules == ['bash', 'lisp']
|
||||
assert settings.exclude_rules == ['git', 'vim']
|
||||
@@ -67,6 +69,8 @@ class TestSettingsFromEnv(object):
|
||||
assert settings.require_confirmation is True
|
||||
assert settings.no_colors is False
|
||||
assert settings.priority == {'bash': 10, 'vim': 15}
|
||||
assert settings.wait_slow_command == 999
|
||||
assert settings.slow_commands == ['lein', 'react-native', './gradlew']
|
||||
|
||||
def test_from_env_with_DEFAULT(self, environ, settings):
|
||||
environ.update({'THEFUCK_RULES': 'DEFAULT_RULES:bash:lisp'})
|
||||
|
||||
@@ -92,13 +92,13 @@ class Settings(dict):
|
||||
return self._rules_from_env(val)
|
||||
elif attr == 'priority':
|
||||
return dict(self._priority_from_env(val))
|
||||
elif attr == 'wait_command':
|
||||
elif attr in ('wait_command', 'history_limit', 'wait_slow_command'):
|
||||
return int(val)
|
||||
elif attr in ('require_confirmation', 'no_colors', 'debug',
|
||||
'alter_history'):
|
||||
return val.lower() == 'true'
|
||||
elif attr == 'history_limit':
|
||||
return int(val)
|
||||
elif attr == 'slow_commands':
|
||||
return val.split(':')
|
||||
else:
|
||||
return val
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ DEFAULT_SETTINGS = {'rules': DEFAULT_RULES,
|
||||
'priority': {},
|
||||
'history_limit': None,
|
||||
'alter_history': True,
|
||||
'wait_slow_command': 15,
|
||||
'slow_commands': ['lein', 'react-native', 'gradle',
|
||||
'./gradlew'],
|
||||
'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'}}
|
||||
|
||||
ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
|
||||
@@ -41,7 +44,9 @@ ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
|
||||
'THEFUCK_DEBUG': 'debug',
|
||||
'THEFUCK_PRIORITY': 'priority',
|
||||
'THEFUCK_HISTORY_LIMIT': 'history_limit',
|
||||
'THEFUCK_ALTER_HISTORY': 'alter_history'}
|
||||
'THEFUCK_ALTER_HISTORY': 'alter_history',
|
||||
'THEFUCK_WAIT_SLOW_COMMAND': 'wait_slow_command',
|
||||
'THEFUCK_SLOW_COMMANDS': 'slow_commands'}
|
||||
|
||||
SETTINGS_HEADER = u"""# The Fuck settings file
|
||||
#
|
||||
|
||||
@@ -1,32 +1,43 @@
|
||||
from thefuck.specific.apt import apt_available
|
||||
from thefuck.utils import memoize
|
||||
from thefuck.utils import memoize, which
|
||||
from thefuck.shells import shell
|
||||
|
||||
try:
|
||||
import CommandNotFound
|
||||
from CommandNotFound import CommandNotFound
|
||||
|
||||
command_not_found = CommandNotFound()
|
||||
enabled_by_default = apt_available
|
||||
except ImportError:
|
||||
enabled_by_default = False
|
||||
|
||||
|
||||
def _get_executable(command):
|
||||
if command.script_parts[0] == 'sudo':
|
||||
return command.script_parts[1]
|
||||
else:
|
||||
return command.script_parts[0]
|
||||
|
||||
|
||||
@memoize
|
||||
def get_package(command):
|
||||
def get_package(executable):
|
||||
try:
|
||||
c = CommandNotFound.CommandNotFound()
|
||||
cmd = command.split(' ')
|
||||
pkgs = c.getPackages(cmd[0] if cmd[0] != 'sudo' else cmd[1])
|
||||
name, _ = pkgs[0]
|
||||
return name
|
||||
packages = command_not_found.getPackages(executable)
|
||||
return packages[0][0]
|
||||
except IndexError:
|
||||
# IndexError is thrown when no matching package is found
|
||||
return None
|
||||
|
||||
|
||||
def match(command):
|
||||
return 'not found' in command.stderr and get_package(command.script)
|
||||
if 'not found' in command.stderr:
|
||||
executable = _get_executable(command)
|
||||
return not which(executable) and get_package(executable)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
name = get_package(command.script)
|
||||
executable = _get_executable(command)
|
||||
name = get_package(executable)
|
||||
formatme = shell.and_('sudo apt-get install {}', '{}')
|
||||
return formatme.format(name, command.script)
|
||||
|
||||
@@ -15,7 +15,7 @@ def _get_brew_commands(brew_path_prefix):
|
||||
brew_cmd_path = brew_path_prefix + BREW_CMD_PATH
|
||||
|
||||
return [name[:-3] for name in os.listdir(brew_cmd_path)
|
||||
if name.endswith('.rb')]
|
||||
if name.endswith(('.rb', '.sh'))]
|
||||
|
||||
|
||||
def _get_brew_tap_specific_commands(brew_path_prefix):
|
||||
|
||||
@@ -4,7 +4,7 @@ from thefuck.utils import replace_argument, for_app
|
||||
|
||||
@for_app('cargo', at_least=1)
|
||||
def match(command):
|
||||
return ('No such subcommand' in command.stderr
|
||||
return ('no such subcommand' in command.stderr.lower()
|
||||
and 'Did you mean' in command.stderr)
|
||||
|
||||
|
||||
|
||||
37
thefuck/rules/fab_command_not_found.py
Normal file
37
thefuck/rules/fab_command_not_found.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from thefuck.utils import eager, get_closest, for_app
|
||||
|
||||
|
||||
@for_app('fab')
|
||||
def match(command):
|
||||
return 'Warning: Command(s) not found:' in command.stderr
|
||||
|
||||
|
||||
# We need different behavior then in get_all_matched_commands.
|
||||
@eager
|
||||
def _get_between(content, start, end=None):
|
||||
should_yield = False
|
||||
for line in content.split('\n'):
|
||||
if start in line:
|
||||
should_yield = True
|
||||
continue
|
||||
|
||||
if end and end in line:
|
||||
return
|
||||
|
||||
if should_yield and line:
|
||||
yield line.strip().split(' ')[0]
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
not_found_commands = _get_between(
|
||||
command.stderr, 'Warning: Command(s) not found:', 'Available commands:')
|
||||
possible_commands = _get_between(
|
||||
command.stdout, 'Available commands:')
|
||||
|
||||
script = command.script
|
||||
for not_found in not_found_commands:
|
||||
fix = get_closest(not_found, possible_commands)
|
||||
script = script.replace(' {}'.format(not_found),
|
||||
' {}'.format(fix))
|
||||
|
||||
return script
|
||||
@@ -6,8 +6,7 @@ from thefuck.utils import eager
|
||||
|
||||
@git_support
|
||||
def match(command):
|
||||
return ('branch' in command.script
|
||||
and "fatal: A branch named '" in command.stderr
|
||||
return ("fatal: A branch named '" in command.stderr
|
||||
and " already exists." in command.stderr)
|
||||
|
||||
|
||||
@@ -17,7 +16,9 @@ def get_new_command(command):
|
||||
branch_name = re.findall(
|
||||
r"fatal: A branch named '([^']*)' already exists.", command.stderr)[0]
|
||||
new_command_templates = [['git branch -d {0}', 'git branch {0}'],
|
||||
['git branch -d {0}', 'git checkout -b {0}'],
|
||||
['git branch -D {0}', 'git branch {0}'],
|
||||
['git branch -D {0}', 'git checkout -b {0}'],
|
||||
['git checkout {0}']]
|
||||
for new_command_template in new_command_templates:
|
||||
yield shell.and_(*new_command_template).format(branch_name)
|
||||
|
||||
@@ -5,10 +5,13 @@ from thefuck.specific.git import git_support
|
||||
|
||||
@git_support
|
||||
def match(command):
|
||||
return ('push' in command.script
|
||||
and '! [rejected]' in command.stderr
|
||||
and 'failed to push some refs to' in command.stderr
|
||||
and 'Updates were rejected because the tip of your current branch is behind' in command.stderr)
|
||||
return ('push' in command.script and
|
||||
'! [rejected]' in command.stderr and
|
||||
'failed to push some refs to' in command.stderr and
|
||||
('Updates were rejected because the tip of your'
|
||||
' current branch is behind' in command.stderr or
|
||||
'Updates were rejected because the remote '
|
||||
'contains work that you do' in command.stderr))
|
||||
|
||||
|
||||
@git_support
|
||||
|
||||
34
thefuck/rules/gradle_no_task.py
Normal file
34
thefuck/rules/gradle_no_task.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import re
|
||||
from subprocess import Popen, PIPE
|
||||
from thefuck.utils import for_app, eager, replace_command
|
||||
|
||||
regex = re.compile(r"Task '(.*)' (is ambiguous|not found)")
|
||||
|
||||
|
||||
@for_app('gradle', './gradlew')
|
||||
def match(command):
|
||||
return regex.findall(command.stderr)
|
||||
|
||||
|
||||
@eager
|
||||
def _get_all_tasks(gradle):
|
||||
proc = Popen([gradle, 'tasks'], stdout=PIPE)
|
||||
should_yield = False
|
||||
for line in proc.stdout.readlines():
|
||||
line = line.decode().strip()
|
||||
if line.startswith('----'):
|
||||
should_yield = True
|
||||
continue
|
||||
|
||||
if not line.strip():
|
||||
should_yield = False
|
||||
continue
|
||||
|
||||
if should_yield and not line.startswith('All tasks runnable from root project'):
|
||||
yield line.split(' ')[0]
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
wrong_task = regex.findall(command.stderr)[0][0]
|
||||
all_tasks = _get_all_tasks(command.script_parts[0])
|
||||
return replace_command(command, wrong_task, all_tasks)
|
||||
13
thefuck/rules/gradle_wrapper.py
Normal file
13
thefuck/rules/gradle_wrapper.py
Normal file
@@ -0,0 +1,13 @@
|
||||
import os
|
||||
from thefuck.utils import for_app, which
|
||||
|
||||
|
||||
@for_app('gradle')
|
||||
def match(command):
|
||||
return (not which(command.script_parts[0])
|
||||
and 'not found' in command.stderr
|
||||
and os.path.isfile('gradlew'))
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
return u'./gradlew {}'.format(' '.join(command.script_parts[1:]))
|
||||
36
thefuck/rules/grunt_task_not_found.py
Normal file
36
thefuck/rules/grunt_task_not_found.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import re
|
||||
from subprocess import Popen, PIPE
|
||||
from thefuck.utils import for_app, eager, get_closest
|
||||
|
||||
regex = re.compile(r'Warning: Task "(.*)" not found.')
|
||||
|
||||
|
||||
@for_app('grunt')
|
||||
def match(command):
|
||||
return regex.findall(command.stdout)
|
||||
|
||||
|
||||
@eager
|
||||
def _get_all_tasks():
|
||||
proc = Popen(['grunt', '--help'], stdout=PIPE)
|
||||
should_yield = False
|
||||
for line in proc.stdout.readlines():
|
||||
line = line.decode().strip()
|
||||
|
||||
if 'Available tasks' in line:
|
||||
should_yield = True
|
||||
continue
|
||||
|
||||
if should_yield and not line:
|
||||
return
|
||||
|
||||
if ' ' in line:
|
||||
yield line.split(' ')[0]
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
misspelled_task = regex.findall(command.stdout)[0].split(':')[0]
|
||||
tasks = _get_all_tasks()
|
||||
fixed = get_closest(misspelled_task, tasks)
|
||||
return command.script.replace(' {}'.format(misspelled_task),
|
||||
' {}'.format(fixed))
|
||||
@@ -1,12 +1,12 @@
|
||||
from difflib import get_close_matches
|
||||
from thefuck.utils import get_all_executables, \
|
||||
get_valid_history_without_current, get_closest
|
||||
get_valid_history_without_current, get_closest, which
|
||||
from thefuck.specific.sudo import sudo_support
|
||||
|
||||
|
||||
@sudo_support
|
||||
def match(command):
|
||||
return (command.script_parts
|
||||
return (not which(command.script_parts[0])
|
||||
and 'not found' in command.stderr
|
||||
and bool(get_close_matches(command.script_parts[0],
|
||||
get_all_executables())))
|
||||
|
||||
17
thefuck/rules/npm_missing_script.py
Normal file
17
thefuck/rules/npm_missing_script.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import re
|
||||
from thefuck.utils import for_app, replace_command
|
||||
from thefuck.specific.npm import get_scripts, npm_available
|
||||
|
||||
enabled_by_default = npm_available
|
||||
|
||||
|
||||
@for_app('npm')
|
||||
def match(command):
|
||||
return (any(part.startswith('ru') for part in command.script_parts)
|
||||
and 'npm ERR! missing script: ' in command.stderr)
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
misspelled_script = re.findall(
|
||||
r'.*missing script: (.*)\n', command.stderr)[0]
|
||||
return replace_command(command, misspelled_script, get_scripts())
|
||||
17
thefuck/rules/npm_run_script.py
Normal file
17
thefuck/rules/npm_run_script.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from thefuck.specific.npm import npm_available, get_scripts
|
||||
from thefuck.utils import for_app
|
||||
|
||||
enabled_by_default = npm_available
|
||||
|
||||
|
||||
@for_app('npm')
|
||||
def match(command):
|
||||
return ('Usage: npm <command>' in command.stdout
|
||||
and not any(part.startswith('ru') for part in command.script_parts)
|
||||
and command.script_parts[1] in get_scripts())
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
parts = command.script_parts[:]
|
||||
parts.insert(1, 'run-script')
|
||||
return ' '.join(parts)
|
||||
@@ -1,6 +1,9 @@
|
||||
from thefuck.specific.npm import npm_available
|
||||
from thefuck.utils import replace_argument, for_app, eager, get_closest
|
||||
from thefuck.specific.sudo import sudo_support
|
||||
|
||||
enabled_by_default = npm_available
|
||||
|
||||
|
||||
def _get_wrong_command(script_parts):
|
||||
commands = [part for part in script_parts[1:] if not part.startswith('-')]
|
||||
|
||||
@@ -5,22 +5,36 @@
|
||||
# The file ~/github.com does not exist.
|
||||
# Perhaps you meant 'http://github.com'?
|
||||
#
|
||||
from thefuck.utils import for_app
|
||||
from thefuck.shells import shell
|
||||
from thefuck.utils import eager, for_app
|
||||
|
||||
|
||||
def is_arg_url(command):
|
||||
return ('.com' in command.script or
|
||||
'.edu' in command.script or
|
||||
'.info' in command.script or
|
||||
'.io' in command.script or
|
||||
'.ly' in command.script or
|
||||
'.me' in command.script or
|
||||
'.net' in command.script or
|
||||
'.org' in command.script or
|
||||
'.se' in command.script or
|
||||
'www.' in command.script)
|
||||
|
||||
|
||||
@for_app('open', 'xdg-open', 'gnome-open', 'kde-open')
|
||||
def match(command):
|
||||
return ('.com' in command.script
|
||||
or '.net' in command.script
|
||||
or '.org' in command.script
|
||||
or '.ly' in command.script
|
||||
or '.io' in command.script
|
||||
or '.se' in command.script
|
||||
or '.edu' in command.script
|
||||
or '.info' in command.script
|
||||
or '.me' in command.script
|
||||
or 'www.' in command.script)
|
||||
return (is_arg_url(command) or
|
||||
command.stderr.strip().startswith('The file ') and
|
||||
command.stderr.strip().endswith(' does not exist.'))
|
||||
|
||||
|
||||
@eager
|
||||
def get_new_command(command):
|
||||
return command.script.replace('open ', 'open http://')
|
||||
stderr = command.stderr.strip()
|
||||
if is_arg_url(command):
|
||||
yield command.script.replace('open ', 'open http://')
|
||||
elif stderr.startswith('The file ') and stderr.endswith(' does not exist.'):
|
||||
arg = command.script.split(' ', 1)[1]
|
||||
for option in ['touch', 'mkdir']:
|
||||
yield shell.and_(u'{} {}'.format(option, arg), command.script)
|
||||
|
||||
@@ -4,7 +4,7 @@ from thefuck.specific.sudo import sudo_support
|
||||
|
||||
|
||||
@sudo_support
|
||||
@for_app('pip')
|
||||
@for_app('pip', 'pip2', 'pip3')
|
||||
def match(command):
|
||||
return ('pip' in command.script and
|
||||
'unknown command' in command.stderr and
|
||||
|
||||
41
thefuck/rules/port_already_in_use.py
Normal file
41
thefuck/rules/port_already_in_use.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import re
|
||||
from subprocess import Popen, PIPE
|
||||
from thefuck.utils import memoize, which
|
||||
from thefuck.shells import shell
|
||||
|
||||
enabled_by_default = bool(which('lsof'))
|
||||
|
||||
patterns = [r"bind on address \('.*', (?P<port>\d+)\)",
|
||||
r'Unable to bind [^ ]*:(?P<port>\d+)',
|
||||
r"can't listen on port (?P<port>\d+)",
|
||||
r'listen EADDRINUSE [^ ]*:(?P<port>\d+)']
|
||||
|
||||
|
||||
@memoize
|
||||
def _get_pid_by_port(port):
|
||||
proc = Popen(['lsof', '-i', ':{}'.format(port)], stdout=PIPE)
|
||||
lines = proc.stdout.read().decode().split('\n')
|
||||
if len(lines) > 1:
|
||||
return lines[1].split()[1]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
@memoize
|
||||
def _get_used_port(command):
|
||||
for pattern in patterns:
|
||||
matched = (re.search(pattern, command.stderr)
|
||||
or re.search(pattern, command.stdout))
|
||||
if matched:
|
||||
return matched.group('port')
|
||||
|
||||
|
||||
def match(command):
|
||||
port = _get_used_port(command)
|
||||
return port and _get_pid_by_port(port)
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
port = _get_used_port(command)
|
||||
pid = _get_pid_by_port(port)
|
||||
return shell.and_(u'kill {}'.format(pid), command.script)
|
||||
14
thefuck/rules/react_native_command_unrecognized.py
Normal file
14
thefuck/rules/react_native_command_unrecognized.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import re
|
||||
from thefuck.utils import for_app, replace_command
|
||||
|
||||
|
||||
@for_app('react-native')
|
||||
def match(command):
|
||||
return re.match(r'Command `.*` unrecognized', command.stderr)
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
misspelled_command = re.findall(r'Command `(.*)` unrecognized',
|
||||
command.stderr)[0]
|
||||
commands = re.findall(r' - (.*): .*\n', command.stdout)
|
||||
return replace_command(command, misspelled_command, commands)
|
||||
36
thefuck/rules/workon_doesnt_exists.py
Normal file
36
thefuck/rules/workon_doesnt_exists.py
Normal file
@@ -0,0 +1,36 @@
|
||||
try:
|
||||
from pathlib import Path
|
||||
except ImportError:
|
||||
from pathlib2 import Path
|
||||
|
||||
from thefuck.utils import for_app, replace_command, eager, memoize
|
||||
|
||||
|
||||
@memoize
|
||||
@eager
|
||||
def _get_all_environments():
|
||||
root = Path('~/.virtualenvs').expanduser()
|
||||
if not root.is_dir():
|
||||
return
|
||||
|
||||
for child in root.iterdir():
|
||||
if child.is_dir():
|
||||
yield child.name
|
||||
|
||||
|
||||
@for_app('workon')
|
||||
def match(command):
|
||||
return (len(command.script_parts) >= 2
|
||||
and command.script_parts[1] not in _get_all_environments())
|
||||
|
||||
|
||||
def get_new_command(command):
|
||||
misspelled_env = command.script_parts[1]
|
||||
create_new = u'mkvirtualenv {}'.format(misspelled_env)
|
||||
|
||||
available = _get_all_environments()
|
||||
if available:
|
||||
return replace_command(command, misspelled_env, available) \
|
||||
+ [create_new]
|
||||
else:
|
||||
return create_new
|
||||
21
thefuck/specific/npm.py
Normal file
21
thefuck/specific/npm.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import re
|
||||
from subprocess import Popen, PIPE
|
||||
from thefuck.utils import memoize, eager, which
|
||||
|
||||
npm_available = bool(which('npm'))
|
||||
|
||||
|
||||
@memoize
|
||||
@eager
|
||||
def get_scripts():
|
||||
"""Get custom npm scripts."""
|
||||
proc = Popen(['npm', 'run-script'], stdout=PIPE)
|
||||
should_yeild = False
|
||||
for line in proc.stdout.readlines():
|
||||
line = line.decode()
|
||||
if 'available via `npm run-script`:' in line:
|
||||
should_yeild = True
|
||||
continue
|
||||
|
||||
if should_yeild and re.match(r'^ [^ ]+', line):
|
||||
yield line.strip().split(' ')[0]
|
||||
@@ -34,7 +34,7 @@ class Command(object):
|
||||
self._script_parts = shell.split_command(self.script)
|
||||
except Exception:
|
||||
logs.debug(u"Can't split command script {} because:\n {}".format(
|
||||
self, sys.exc_info()))
|
||||
self, sys.exc_info()))
|
||||
self._script_parts = None
|
||||
return self._script_parts
|
||||
|
||||
@@ -47,7 +47,7 @@ class Command(object):
|
||||
|
||||
def __repr__(self):
|
||||
return u'Command(script={}, stdout={}, stderr={})'.format(
|
||||
self.script, self.stdout, self.stderr)
|
||||
self.script, self.stdout, self.stderr)
|
||||
|
||||
def update(self, **kwargs):
|
||||
"""Returns new command with replaced fields.
|
||||
@@ -61,7 +61,7 @@ class Command(object):
|
||||
return Command(**kwargs)
|
||||
|
||||
@staticmethod
|
||||
def _wait_output(popen):
|
||||
def _wait_output(popen, is_slow):
|
||||
"""Returns `True` if we can get output of the command in the
|
||||
`settings.wait_command` time.
|
||||
|
||||
@@ -73,7 +73,8 @@ class Command(object):
|
||||
"""
|
||||
proc = Process(popen.pid)
|
||||
try:
|
||||
proc.wait(settings.wait_command)
|
||||
proc.wait(settings.wait_slow_command if is_slow
|
||||
else settings.wait_command)
|
||||
return True
|
||||
except TimeoutExpired:
|
||||
for child in proc.children(recursive=True):
|
||||
@@ -113,9 +114,12 @@ class Command(object):
|
||||
env = dict(os.environ)
|
||||
env.update(settings.env)
|
||||
|
||||
with logs.debug_time(u'Call: {}; with env: {};'.format(script, env)):
|
||||
result = Popen(script, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=env)
|
||||
if cls._wait_output(result):
|
||||
is_slow = script.split(' ')[0] in settings.slow_commands
|
||||
with logs.debug_time(u'Call: {}; with env: {}; is slow: '.format(
|
||||
script, env, is_slow)):
|
||||
result = Popen(script, shell=True, stdin=PIPE,
|
||||
stdout=PIPE, stderr=PIPE, env=env)
|
||||
if cls._wait_output(result, is_slow):
|
||||
stdout = result.stdout.read().decode('utf-8')
|
||||
stderr = result.stderr.read().decode('utf-8')
|
||||
|
||||
@@ -168,9 +172,9 @@ class Rule(object):
|
||||
return 'Rule(name={}, match={}, get_new_command={}, ' \
|
||||
'enabled_by_default={}, side_effect={}, ' \
|
||||
'priority={}, requires_output)'.format(
|
||||
self.name, self.match, self.get_new_command,
|
||||
self.enabled_by_default, self.side_effect,
|
||||
self.priority, self.requires_output)
|
||||
self.name, self.match, self.get_new_command,
|
||||
self.enabled_by_default, self.side_effect,
|
||||
self.priority, self.requires_output)
|
||||
|
||||
@classmethod
|
||||
def from_path(cls, path):
|
||||
@@ -270,7 +274,7 @@ class CorrectedCommand(object):
|
||||
|
||||
def __repr__(self):
|
||||
return u'CorrectedCommand(script={}, side_effect={}, priority={})'.format(
|
||||
self.script, self.side_effect, self.priority)
|
||||
self.script, self.side_effect, self.priority)
|
||||
|
||||
def run(self, old_cmd):
|
||||
"""Runs command from rule for passed command.
|
||||
|
||||
Reference in New Issue
Block a user