1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-11-01 07:32:09 +00:00

Compare commits

...

39 Commits
3.10 ... 3.11

Author SHA1 Message Date
Vladimir Iakovlev
c63e0d1582 Bump to 3.11 2016-08-14 13:18:43 +03:00
Vladimir Iakovlev
a3eb124033 #N/A: Disable port_already_in_use on systems without lsof 2016-08-14 08:39:26 +03:00
Vladimir Iakovlev
a1f115eb19 N/A: Add osx to travis-ci config 2016-08-14 08:18:02 +03:00
Vladimir Iakovlev
56851e8d31 #N/A: Add port_already_in_use rule 2016-08-14 06:59:26 +03:00
Vladimir Iakovlev
a8c3c2d728 #N/A: Fix tests on python 2 2016-08-13 21:46:12 +03:00
Vladimir Iakovlev
844d3af8ae #N/A: Add grunt_task_not_found rule 2016-08-13 21:13:05 +03:00
Vladimir Iakovlev
a2693bd737 #N/A: Add workon_doesnt_exists rule 2016-08-13 20:55:00 +03:00
Vladimir Iakovlev
ae3e231a5f #N/A: Add gradle_no_task rule 2016-08-13 20:31:33 +03:00
Vladimir Iakovlev
21bb439d7c #N/A: Add gradle_wrapper rule 2016-08-13 19:30:46 +03:00
Vladimir Iakovlev
efcf7da7db #N/A: Add react_native_command_unrecognized rule 2016-08-13 19:14:55 +03:00
Vladimir Iakovlev
5f79217e97 #N/A: Add slow_commands and wait_slow_command settings options 2016-08-13 18:55:11 +03:00
Vladimir Iakovlev
fdfbfc80c0 #N/A: Add npm_missing_script rule 2016-08-13 18:28:45 +03:00
Vladimir Iakovlev
b09a4e394e #N/A: Add npm_run_script rule 2016-08-13 17:38:40 +03:00
Vladimir Iakovlev
34973fe97e #N/A: Fix tests without CommandNotFound 2016-08-13 17:33:16 +03:00
Vladimir Iakovlev
379d2953c9 #N/A: Use for_app helper 2016-08-13 16:14:05 +03:00
Vladimir Iakovlev
065b350ada #N/A: Add fab_command_not_found rule 2016-08-13 16:10:12 +03:00
Vladimir Iakovlev
ca787a1cba #N/A: Ensure that command doesn't exists in no_command rule 2016-08-13 15:39:42 +03:00
Vladimir Iakovlev
4c2fc490f2 Merge branch 'master' of github.com:nvbn/thefuck 2016-08-13 15:33:12 +03:00
Vladimir Iakovlev
0c2083485d #N/A: Make apt-get rule more accurate, remove unstable tests 2016-08-13 15:30:57 +03:00
Vladimir Iakovlev
bc78c83224 Merge pull request #535 from scorphus/534-improve-open
#534: Improve open rule
2016-08-13 20:20:12 +08:00
Vladimir Iakovlev
b2a5009116 #N/A: Ensure that thefuck successfully installed in functional tests 2016-08-13 14:50:21 +03:00
Vladimir Iakovlev
087584a1e0 #N/A: Remove ability to run functional tests without docker 2016-08-13 14:11:18 +03:00
Pablo Santiago Blum de Aguiar
b6b15bf0d1 #534: Improve open rule by creating the file or dir 2016-08-12 01:09:05 -03:00
Pablo Santiago Blum de Aguiar
a169575b0f #534: Move “is arg a url?” logic to a function
This function will be used in `get_new_command`.
2016-08-12 00:53:11 -03:00
Pablo Santiago Blum de Aguiar
fbea803a9b #534: Use a stderr fixture on open rule tests 2016-08-12 00:20:12 -03:00
Vladimir Iakovlev
51415a5cb1 Merge pull request #530 from scorphus/git-branch-exists-checkout
#N/A: Use git_branch_exists rule with `checkout` too
2016-07-22 13:11:05 +03:00
Pablo Santiago Blum de Aguiar
237bc57999 #N/A: Use git_branch_exists rule with checkout too 2016-07-21 13:59:41 -03:00
nvbn
2af65071d8 Merge branch 'yeahbert-git_push_contains_work' 2016-07-07 15:42:02 +03:00
nvbn
c93b547624 #529: Minor style changes 2016-07-07 15:41:51 +03:00
Julian Zimmermann
837ca73f50 Added "contains work" error for git push 2016-07-07 11:47:51 +02:00
Vladimir Iakovlev
a3b2e6872b Merge pull request #527 from josephfrazier/brew-update
Add `brew update` to Homebrew commands
2016-06-30 01:46:36 +03:00
Joseph Frazier
29ed1800e1 Add brew update to Homebrew commands
`brew update` is implemented in shell instead of ruby, so
`_get_brew_commands` needs to list .sh files as well as .rb

Resolves https://github.com/nvbn/thefuck/issues/526
2016-06-29 15:50:01 -04:00
Vladimir Iakovlev
965c05bfdf Merge pull request #525 from tdsmith/patch-1
Avoid importing pip
2016-06-29 14:11:26 +03:00
Tim D. Smith
a7d1c725e4 Keep working if pip isn't installed
This might be installed by invoking setup.py directly.
2016-06-28 21:50:06 -07:00
Tim D. Smith
99e828d15d Avoid importing pip
pip doesn't have a stable Python API and shouldn't be `import`ed. `pkg_resources` is provided by setuptools.
2016-06-28 21:48:06 -07:00
nvbn
ae2b767a4d Merge branch 'scorphus-update-cargo-no-command' 2016-06-28 15:51:01 +03:00
nvbn
3893e0cdca #517: Little refactoring 2016-06-28 15:50:41 +03:00
nvbn
2988e4871f Merge branch 'update-cargo-no-command' of https://github.com/scorphus/thefuck into scorphus-update-cargo-no-command 2016-06-28 15:49:04 +03:00
Pablo Santiago Blum de Aguiar
2c1666abc4 #N/A: Update cargo_no_command rule to support current Cargo 2016-06-18 13:50:59 -03:00
47 changed files with 1292 additions and 202 deletions

View File

@@ -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

View File

@@ -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` &ndash; fixes `npm` custom script name in `npm run-script <script>`;
* `npm_run_script` &ndash; adds missing `run-script` for custom `npm` scripts;
* `npm_wrong_command` &ndash; fixes wrong npm commands like `npm urgrade`;
* `no_command` &ndash; fixes wrong console commands, for example `vom/vim`;
* `no_such_file` &ndash; creates missing directories with `mv` and `cp` commands;
* `open` &ndash; prepends `http` to address passed to `open`;
* `open` &ndash; either prepends `http://` to address passed to `open` or create a new file or directory and passes it to `open`;
* `pip_unknown_command` &ndash; fixes wrong `pip` commands, for example `pip instatl/pip install`;
* `port_already_in_use` &ndash; kills process that bound port;
* `python_command` &ndash; prepends `python` when you trying to run not executable/without `./` python script;
* `python_execute` &ndash; appends missing `.py` when executing Python files;
* `quotation_marks` &ndash; fixes uneven usage of `'` and `"` when containing args';
* `react_native_command_unrecognized` &ndash; fixes unrecognized `react-native` commands;
* `rm_dir` &ndash; adds `-rf` when you trying to remove directory;
* `sed_unterminated_s` &ndash; adds missing '/' to `sed`'s `s` commands;
* `sl_ls` &ndash; changes `sl` to `ls`;
@@ -221,7 +229,8 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `tmux` &ndash; fixes `tmux` commands;
* `unknown_command` &ndash; fixes hadoop hdfs-style "unknown command", for example adds missing '-' to the command on `hdfs dfs ls`;
* `vagrant_up` &ndash; starts up the vagrant instance;
* `whois` &ndash; fixes `whois` command.
* `whois` &ndash; fixes `whois` command;
* `workon_doesnt_exists` &ndash; 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` &ndash; dict with rules priorities, rule with lower `priority` will be matched first;
* `debug` &ndash; enables debug output, by default `False`;
* `history_limit` &ndash; numeric value of how many history commands will be scanned, like `2000`;
* `alter_history` &ndash; push fixed command to history, by default `True`.
* `alter_history` &ndash; push fixed command to history, by default `True`;
* `wait_slow_command` &ndash; max amount of time in seconds for getting previous command output if it in `slow_commands` list;
* `slow_commands` &ndash; 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` &ndash; enables debug output, `true/false`;
* `THEFUCK_HISTORY_LIMIT` &ndash; how many history commands will be scanned, like `2000`;
* `THEFUCK_ALTER_HISTORY` &ndash; push fixed command to history `true/false`.
* `THEFUCK_ALTER_HISTORY` &ndash; push fixed command to history `true/false`;
* `THEFUCK_WAIT_SLOW_COMMAND` &ndash; max amount of time in seconds for getting previous command output if it in `slow_commands` list;
* `THEFUCK_SLOW_COMMANDS` &ndash; list of slow commands, like `lein:gradle`.
For example:

View File

@@ -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'],

View File

@@ -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)

View File

@@ -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)

View File

@@ -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',

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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

View 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

View 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

View 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

View File

@@ -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))

View 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

View 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

View File

@@ -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

View 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'

View 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

View 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

View 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']

View File

@@ -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'})

View File

@@ -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

View File

@@ -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
#

View 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)

View File

@@ -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):

View File

@@ -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)

View 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

View File

@@ -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)

View File

@@ -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

View 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)

View 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:]))

View 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))

View File

@@ -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())))

View 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())

View 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)

View File

@@ -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('-')]

View File

@@ -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)

View File

@@ -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

View 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)

View 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)

View 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
View 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]

View File

@@ -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.