From 8cc19daaaa4bf14fcc92307dbc447176b51dec58 Mon Sep 17 00:00:00 2001 From: nvbn Date: Sun, 6 Sep 2015 00:56:18 +0300 Subject: [PATCH 01/14] Use `pytest-docker-pexpect` for func tests --- .travis.yml | 4 +- requirements.txt | 1 + tests/functional/plots.py | 15 +++---- tests/functional/test_bash.py | 59 +++++++++++++------------ tests/functional/test_fish.py | 66 +++++++++++++--------------- tests/functional/test_install.py | 19 ++++---- tests/functional/test_performance.py | 25 +++++------ tests/functional/test_tcsh.py | 55 +++++++++++------------ tests/functional/test_zsh.py | 63 +++++++++++++------------- tests/functional/utils.py | 57 ------------------------ thefuck/main.py | 7 ++- 11 files changed, 155 insertions(+), 216 deletions(-) diff --git a/.travis.yml b/.travis.yml index 65dc39fb..c74827ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ addons: - pandoc - git env: - - FUNCTIONAL=true BARE=true + - FUNCTIONAL=true install: - pip install coveralls - pip install -r requirements.txt @@ -23,5 +23,5 @@ install: - rm -rf build script: - export COVERAGE_PYTHON_VERSION=python-${TRAVIS_PYTHON_VERSION:0:1} - - coverage run --source=thefuck,tests -m py.test -v --capture=sys + - coverage run --source=thefuck,tests -m py.test -v --capture=sys --run-without-docker after_success: coveralls diff --git a/requirements.txt b/requirements.txt index 76ca36a2..05a9aea6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ setuptools>=17.1 pexpect pypandoc pytest-benchmark +pytest-docker-pexpect diff --git a/tests/functional/plots.py b/tests/functional/plots.py index 611742f2..87d49aa5 100644 --- a/tests/functional/plots.py +++ b/tests/functional/plots.py @@ -1,6 +1,3 @@ -from pexpect import TIMEOUT - - def _set_confirmation(proc, require): proc.sendline(u'mkdir -p ~/.thefuck') proc.sendline( @@ -8,7 +5,7 @@ def _set_confirmation(proc, require): require)) -def with_confirmation(proc): +def with_confirmation(proc, TIMEOUT): """Ensures that command can be fixed when confirmation enabled.""" _set_confirmation(proc, True) @@ -23,19 +20,19 @@ def with_confirmation(proc): assert proc.expect([TIMEOUT, u'test']) -def history_changed(proc, to): +def history_changed(proc, TIMEOUT, to): """Ensures that history changed.""" proc.send('\033[A') assert proc.expect([TIMEOUT, to]) -def history_not_changed(proc): +def history_not_changed(proc, TIMEOUT): """Ensures that history not changed.""" proc.send('\033[A') assert proc.expect([TIMEOUT, u'fuck']) -def select_command_with_arrows(proc): +def select_command_with_arrows(proc, TIMEOUT): """Ensures that command can be selected with arrow keys.""" _set_confirmation(proc, True) @@ -55,7 +52,7 @@ def select_command_with_arrows(proc): assert proc.expect([TIMEOUT, u'Not a git repository']) -def refuse_with_confirmation(proc): +def refuse_with_confirmation(proc, TIMEOUT): """Ensures that fix can be refused when confirmation enabled.""" _set_confirmation(proc, True) @@ -70,7 +67,7 @@ def refuse_with_confirmation(proc): assert proc.expect([TIMEOUT, u'Aborted']) -def without_confirmation(proc): +def without_confirmation(proc, TIMEOUT): """Ensures that command can be fixed when confirmation disabled.""" _set_confirmation(proc, False) diff --git a/tests/functional/test_bash.py b/tests/functional/test_bash.py index 849f7329..53d779b7 100644 --- a/tests/functional/test_bash.py +++ b/tests/functional/test_bash.py @@ -2,27 +2,28 @@ import pytest from tests.functional.plots import with_confirmation, without_confirmation, \ refuse_with_confirmation, history_changed, history_not_changed, \ select_command_with_arrows -from tests.functional.utils import spawn, functional, images +from tests.functional.utils import functional -containers = images(('ubuntu-python3-bash', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python3 python3-pip python3-dev git -RUN pip3 install -U setuptools -RUN ln -s /usr/bin/pip3 /usr/bin/pip -'''), - ('ubuntu-python2-bash', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python python-pip python-dev git -RUN pip2 install -U pip setuptools -''')) +containers = ((u'thefuck/ubuntu-python3-bash', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python3 python3-pip python3-dev git + RUN pip3 install -U setuptools + RUN ln -s /usr/bin/pip3 /usr/bin/pip''', + u'bash'), + (u'thefuck/ubuntu-python2-bash', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python python-pip python-dev git + RUN pip2 install -U pip setuptools''', + u'bash')) @pytest.fixture(params=containers) -def proc(request): - tag, dockerfile = request.param - proc = spawn(request, tag, dockerfile, u'bash') +def proc(request, spawnu, run_without_docker): + proc = spawnu(*request.param) + if not run_without_docker: + proc.sendline(u"pip install /src") proc.sendline(u"export PS1='$ '") proc.sendline(u'eval $(thefuck --alias)') proc.sendline(u'echo > $HISTFILE') @@ -30,24 +31,24 @@ def proc(request): @functional -def test_with_confirmation(proc): - with_confirmation(proc) - history_changed(proc, u'echo test') +def test_with_confirmation(proc, TIMEOUT): + with_confirmation(proc, TIMEOUT) + history_changed(proc, TIMEOUT, u'echo test') @functional -def test_select_command_with_arrows(proc): - select_command_with_arrows(proc) - history_changed(proc, u'git push') +def test_select_command_with_arrows(proc, TIMEOUT): + select_command_with_arrows(proc, TIMEOUT) + history_changed(proc, TIMEOUT, u'git push') @functional -def test_refuse_with_confirmation(proc): - refuse_with_confirmation(proc) - history_not_changed(proc) +def test_refuse_with_confirmation(proc, TIMEOUT): + refuse_with_confirmation(proc, TIMEOUT) + history_not_changed(proc, TIMEOUT) @functional -def test_without_confirmation(proc): - without_confirmation(proc) - history_changed(proc, u'echo test') +def test_without_confirmation(proc, TIMEOUT): + without_confirmation(proc, TIMEOUT) + history_changed(proc, TIMEOUT, u'echo test') diff --git a/tests/functional/test_fish.py b/tests/functional/test_fish.py index a70efa10..636fb3c5 100644 --- a/tests/functional/test_fish.py +++ b/tests/functional/test_fish.py @@ -1,59 +1,55 @@ import pytest from tests.functional.plots import with_confirmation, without_confirmation, \ refuse_with_confirmation, select_command_with_arrows -from tests.functional.utils import spawn, functional, images, bare +from tests.functional.utils import functional -containers = images(('ubuntu-python3-fish', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python3 python3-pip python3-dev fish git -RUN pip3 install -U setuptools -RUN ln -s /usr/bin/pip3 /usr/bin/pip -RUN apt-get install -yy fish -'''), - ('ubuntu-python2-fish', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python python-pip python-dev git -RUN pip2 install -U pip setuptools -RUN apt-get install -yy fish -''')) +containers = (('thefuck/ubuntu-python3-fish', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python3 python3-pip python3-dev fish git + RUN pip3 install -U setuptools + RUN ln -s /usr/bin/pip3 /usr/bin/pip + RUN apt-get install -yy fish''', + u'fish'), + ('thefuck/ubuntu-python2-fish', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python python-pip python-dev git + RUN pip2 install -U pip setuptools + RUN apt-get install -yy fish''', + u'fish')) @pytest.fixture(params=containers) -def proc(request): - tag, dockerfile = request.param - proc = spawn(request, tag, dockerfile, u'fish') +def proc(request, spawnu): + proc = spawnu(*request.param) + proc.sendline(u"pip install /src") proc.sendline(u'thefuck --alias > ~/.config/fish/config.fish') proc.sendline(u'fish') return proc @functional -@pytest.mark.skipif( - bool(bare), reason='https://github.com/travis-ci/apt-source-whitelist/issues/71') -def test_with_confirmation(proc): - with_confirmation(proc) +@pytest.mark.skip_without_docker +def test_with_confirmation(proc, TIMEOUT): + with_confirmation(proc, TIMEOUT) @functional -@pytest.mark.skipif( - bool(bare), reason='https://github.com/travis-ci/apt-source-whitelist/issues/71') -def test_select_command_with_arrows(proc): - select_command_with_arrows(proc) +@pytest.mark.skip_without_docker +def test_select_command_with_arrows(proc, TIMEOUT): + select_command_with_arrows(proc, TIMEOUT) @functional -@pytest.mark.skipif( - bool(bare), reason='https://github.com/travis-ci/apt-source-whitelist/issues/71') -def test_refuse_with_confirmation(proc): - refuse_with_confirmation(proc) +@pytest.mark.skip_without_docker +def test_refuse_with_confirmation(proc, TIMEOUT): + refuse_with_confirmation(proc, TIMEOUT) @functional -@pytest.mark.skipif( - bool(bare), reason='https://github.com/travis-ci/apt-source-whitelist/issues/71') -def test_without_confirmation(proc): - without_confirmation(proc) +@pytest.mark.skip_without_docker +def test_without_confirmation(proc, TIMEOUT): + without_confirmation(proc, TIMEOUT) # TODO: ensure that history changes. diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index cd4b9faa..2fece023 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -1,25 +1,26 @@ import pytest -from pexpect import TIMEOUT -from tests.functional.utils import spawn, functional, bare +from thefuck.main import _get_current_version +from tests.functional.utils import functional -envs = ((u'bash', 'ubuntu-bash', u''' + +envs = ((u'bash', 'thefuck/ubuntu-bash', u''' FROM ubuntu:latest RUN apt-get update RUN apt-get install -yy bash -'''), (u'bash', 'generic-bash', u''' +'''), (u'bash', 'thefuck/generic-bash', u''' FROM fedora:latest RUN dnf install -yy python-devel sudo wget gcc ''')) @functional -@pytest.mark.skipif( - bool(bare), reason="Can't be tested in bare run") +@pytest.mark.skip_without_docker @pytest.mark.parametrize('shell, tag, dockerfile', envs) -def test_installation(request, shell, tag, dockerfile): - proc = spawn(request, tag, dockerfile, shell, install=False) +def test_installation(spawnu, shell, TIMEOUT, tag, dockerfile): + proc = spawnu(tag, dockerfile, shell) proc.sendline(u'cat /src/install.sh | sh - && $0') proc.sendline(u'thefuck --version') - assert proc.expect([TIMEOUT, u'The Fuck'], timeout=600) + assert proc.expect([TIMEOUT, u'thefuck {}'.format(_get_current_version())], + timeout=600) proc.sendline(u'fuck') assert proc.expect([TIMEOUT, u'No fucks given']) diff --git a/tests/functional/test_performance.py b/tests/functional/test_performance.py index 2310f158..1582a875 100644 --- a/tests/functional/test_performance.py +++ b/tests/functional/test_performance.py @@ -1,7 +1,6 @@ -from pexpect import TIMEOUT import pytest import time -from tests.functional.utils import spawn, functional, bare +from tests.functional.utils import functional dockerfile = u''' FROM ubuntu:latest @@ -11,24 +10,17 @@ RUN pip3 install -U setuptools RUN ln -s /usr/bin/pip3 /usr/bin/pip RUN adduser --disabled-password --gecos '' test ENV SEED "{seed}" -COPY thefuck /src WORKDIR /src -RUN pip install . USER test RUN echo 'eval $(thefuck --alias)' > /home/test/.bashrc RUN echo > /home/test/.bash_history RUN git config --global user.email "you@example.com" RUN git config --global user.name "Your Name" +USER root '''.format(seed=time.time()) -@pytest.fixture -def proc(request): - return spawn(request, 'ubuntu-python3-bash-performance', - dockerfile, u'bash', install=False, copy_src=True) - - -def plot(proc): +def plot(proc, TIMEOUT): proc.sendline(u'cd /home/test/') proc.sendline(u'fuck') assert proc.expect([TIMEOUT, u'No fucks given']) @@ -49,8 +41,11 @@ def plot(proc): @functional -@pytest.mark.skipif( - bool(bare), reason='Would lie on a bare run') +@pytest.mark.skip_without_docker @pytest.mark.benchmark(min_rounds=10) -def test_performance(proc, benchmark): - assert benchmark(plot, proc) is None +def test_performance(spawnu, TIMEOUT, benchmark): + proc = spawnu(u'thefuck/ubuntu-python3-bash-performance', + dockerfile, u'bash') + proc.sendline(u'pip install /src') + proc.sendline(u'su test') + assert benchmark(plot, proc, TIMEOUT) is None diff --git a/tests/functional/test_tcsh.py b/tests/functional/test_tcsh.py index 8114e205..0614cdb1 100644 --- a/tests/functional/test_tcsh.py +++ b/tests/functional/test_tcsh.py @@ -1,51 +1,52 @@ import pytest -from tests.functional.utils import spawn, functional, images +from tests.functional.utils import functional from tests.functional.plots import with_confirmation, without_confirmation, \ refuse_with_confirmation, select_command_with_arrows -containers = images(('ubuntu-python3-tcsh', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python3 python3-pip python3-dev git -RUN pip3 install -U setuptools -RUN ln -s /usr/bin/pip3 /usr/bin/pip -RUN apt-get install -yy tcsh -'''), - ('ubuntu-python2-tcsh', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python python-pip python-dev git -RUN pip2 install -U pip setuptools -RUN apt-get install -yy tcsh -''')) +containers = (('thefuck/ubuntu-python3-tcsh', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python3 python3-pip python3-dev git + RUN pip3 install -U setuptools + RUN ln -s /usr/bin/pip3 /usr/bin/pip + RUN apt-get install -yy tcsh''', + u'tcsh'), + ('thefuck/ubuntu-python2-tcsh', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python python-pip python-dev git + RUN pip2 install -U pip setuptools + RUN apt-get install -yy tcsh''', + u'tcsh')) @pytest.fixture(params=containers) -def proc(request): - tag, dockerfile = request.param - proc = spawn(request, tag, dockerfile, u'tcsh') +def proc(request, spawnu, run_without_docker): + proc = spawnu(*request.param) + if not run_without_docker: + proc.sendline(u'pip install /src') proc.sendline(u'tcsh') proc.sendline(u'eval `thefuck --alias`') return proc @functional -def test_with_confirmation(proc): - with_confirmation(proc) +def test_with_confirmation(proc, TIMEOUT): + with_confirmation(proc, TIMEOUT) @functional -def test_select_command_with_arrows(proc): - select_command_with_arrows(proc) +def test_select_command_with_arrows(proc, TIMEOUT): + select_command_with_arrows(proc, TIMEOUT) @functional -def test_refuse_with_confirmation(proc): - refuse_with_confirmation(proc) +def test_refuse_with_confirmation(proc, TIMEOUT): + refuse_with_confirmation(proc, TIMEOUT) @functional -def test_without_confirmation(proc): - without_confirmation(proc) +def test_without_confirmation(proc, TIMEOUT): + without_confirmation(proc, TIMEOUT) # TODO: ensure that history changes. diff --git a/tests/functional/test_zsh.py b/tests/functional/test_zsh.py index 01d0584a..498d22ed 100644 --- a/tests/functional/test_zsh.py +++ b/tests/functional/test_zsh.py @@ -1,29 +1,30 @@ import pytest -from tests.functional.utils import spawn, functional, images +from tests.functional.utils import functional from tests.functional.plots import with_confirmation, without_confirmation, \ refuse_with_confirmation, history_changed, history_not_changed, select_command_with_arrows -containers = images(('ubuntu-python3-zsh', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python3 python3-pip python3-dev git -RUN pip3 install -U setuptools -RUN ln -s /usr/bin/pip3 /usr/bin/pip -RUN apt-get install -yy zsh -'''), - ('ubuntu-python2-zsh', u''' -FROM ubuntu:latest -RUN apt-get update -RUN apt-get install -yy python python-pip python-dev git -RUN pip2 install -U pip setuptools -RUN apt-get install -yy zsh -''')) +containers = (('ubuntu-python3-zsh', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python3 python3-pip python3-dev git + RUN pip3 install -U setuptools + RUN ln -s /usr/bin/pip3 /usr/bin/pip + RUN apt-get install -yy zsh''', + u'zsh'), + ('ubuntu-python2-zsh', + u'''FROM ubuntu:latest + RUN apt-get update + RUN apt-get install -yy python python-pip python-dev git + RUN pip2 install -U pip setuptools + RUN apt-get install -yy zsh''', + u'zsh')) @pytest.fixture(params=containers) -def proc(request): - tag, dockerfile = request.param - proc = spawn(request, tag, dockerfile, u'zsh') +def proc(request, spawnu, run_without_docker): + proc = spawnu(*request.param) + if not run_without_docker: + proc.sendline(u'pip install /src') proc.sendline(u'eval $(thefuck --alias)') proc.sendline(u'export HISTFILE=~/.zsh_history') proc.sendline(u'echo > $HISTFILE') @@ -34,24 +35,24 @@ def proc(request): @functional -def test_with_confirmation(proc): - with_confirmation(proc) - history_changed(proc, u'echo test') +def test_with_confirmation(proc, TIMEOUT): + with_confirmation(proc, TIMEOUT) + history_changed(proc, TIMEOUT, u'echo test') @functional -def test_select_command_with_arrows(proc): - select_command_with_arrows(proc) - history_changed(proc, u'git push') +def test_select_command_with_arrows(proc, TIMEOUT): + select_command_with_arrows(proc, TIMEOUT) + history_changed(proc, TIMEOUT, u'git push') @functional -def test_refuse_with_confirmation(proc): - refuse_with_confirmation(proc) - history_not_changed(proc) +def test_refuse_with_confirmation(proc, TIMEOUT): + refuse_with_confirmation(proc, TIMEOUT) + history_not_changed(proc, TIMEOUT) @functional -def test_without_confirmation(proc): - without_confirmation(proc) - history_changed(proc, u'echo test') +def test_without_confirmation(proc, TIMEOUT): + without_confirmation(proc, TIMEOUT) + history_changed(proc, TIMEOUT, u'echo test') diff --git a/tests/functional/utils.py b/tests/functional/utils.py index 7042c088..18dcb9df 100644 --- a/tests/functional/utils.py +++ b/tests/functional/utils.py @@ -1,65 +1,8 @@ import pytest import os -import subprocess -import shutil -from tempfile import mkdtemp -from pathlib import Path -import sys -import pexpect -from tests.utils import root - -bare = os.environ.get('BARE') enabled = os.environ.get('FUNCTIONAL') - -def build_container(tag, dockerfile, copy_src=False): - tmpdir = mkdtemp() - try: - if copy_src: - subprocess.call(['cp', '-a', str(root), tmpdir]) - dockerfile_path = Path(tmpdir).joinpath('Dockerfile') - with dockerfile_path.open('w') as file: - file.write(dockerfile) - if subprocess.call(['docker', 'build', '--tag={}'.format(tag), tmpdir]) != 0: - raise Exception("Can't build a container") - finally: - shutil.rmtree(tmpdir) - - -def spawn(request, tag, dockerfile, cmd, install=True, copy_src=False): - if bare: - proc = pexpect.spawnu(cmd) - else: - tag = 'thefuck/{}'.format(tag) - build_container(tag, dockerfile, copy_src) - proc = pexpect.spawnu('docker run --rm=true --volume {}:/src --tty=true ' - '--interactive=true {} {}'.format(root, tag, cmd)) - if install: - proc.sendline('pip install /src') - - proc.sendline('cd /') - - proc.logfile = sys.stdout - - def _finalizer(): - proc.terminate() - if not bare: - container_id = subprocess.check_output(['docker', 'ps']) \ - .decode('utf-8').split('\n')[-2].split()[0] - subprocess.check_call(['docker', 'kill', container_id]) - - request.addfinalizer(_finalizer) - return proc - - -def images(*items): - if bare: - return [items[0]] - else: - return items - - functional = pytest.mark.skipif( not enabled, reason='Functional tests are disabled by default.') diff --git a/thefuck/main.py b/thefuck/main.py index 1b059c3d..94aaef5b 100644 --- a/thefuck/main.py +++ b/thefuck/main.py @@ -103,6 +103,10 @@ def fix_command(): run_command(command, selected_command, settings) +def _get_current_version(): + return pkg_resources.require('thefuck')[0].version + + def print_alias(entry_point=True): if entry_point: warn('`thefuck-alias` is deprecated, use `thefuck --alias` instead.') @@ -120,8 +124,7 @@ def main(): parser = ArgumentParser(prog='thefuck') parser.add_argument('-v', '--version', action='version', - version='%(prog)s {}'.format( - pkg_resources.require('thefuck')[0].version)) + version='%(prog)s {}'.format(_get_current_version())) parser.add_argument('-a', '--alias', action='store_true', help='[custom-alias-name] prints alias for current shell') From cdd5f21e88fe037627a5b01fd5999e7133877e59 Mon Sep 17 00:00:00 2001 From: nvbn Date: Sun, 6 Sep 2015 01:13:44 +0300 Subject: [PATCH 02/14] Add `functional` marker --- .travis.yml | 5 ++--- README.md | 2 +- tests/conftest.py | 14 ++++++++++++++ tests/functional/test_bash.py | 9 ++++----- tests/functional/test_fish.py | 9 ++++----- tests/functional/test_install.py | 4 +--- tests/functional/test_performance.py | 3 +-- tests/functional/test_tcsh.py | 9 ++++----- tests/functional/test_zsh.py | 9 ++++----- tests/functional/utils.py | 8 -------- 10 files changed, 35 insertions(+), 37 deletions(-) delete mode 100644 tests/functional/utils.py diff --git a/.travis.yml b/.travis.yml index c74827ff..f75836e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: python +sudo: false python: - "3.4" - "3.3" @@ -14,8 +15,6 @@ addons: - tcsh - pandoc - git -env: - - FUNCTIONAL=true install: - pip install coveralls - pip install -r requirements.txt @@ -23,5 +22,5 @@ install: - rm -rf build script: - export COVERAGE_PYTHON_VERSION=python-${TRAVIS_PYTHON_VERSION:0:1} - - coverage run --source=thefuck,tests -m py.test -v --capture=sys --run-without-docker + - coverage run --source=thefuck,tests -m py.test -v --capture=sys --run-without-docker --enable-functional after_success: coveralls diff --git a/README.md b/README.md index 75eccb3d..5181adec 100644 --- a/README.md +++ b/README.md @@ -326,7 +326,7 @@ py.test Run unit and functional tests (requires docker): ```bash -FUNCTIONAL=true py.test +py.test --enable-functional ``` For sending package to pypi: diff --git a/tests/conftest.py b/tests/conftest.py index f05cd222..9e115352 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,6 +2,13 @@ import pytest from mock import Mock +def pytest_addoption(parser): + """Adds `--run-without-docker` argument.""" + group = parser.getgroup("thefuck") + group.addoption('--enable-functional', action="store_true", default=False, + help="Enable functional tests") + + @pytest.fixture def no_memoize(monkeypatch): monkeypatch.setattr('thefuck.utils.memoize.disabled', True) @@ -15,3 +22,10 @@ def settings(): @pytest.fixture(autouse=True) def no_cache(monkeypatch): monkeypatch.setattr('thefuck.utils.cache.disabled', True) + + +@pytest.fixture(autouse=True) +def functional(request): + if request.node.get_marker('functional') \ + and not request.config.getoption('enable_functional'): + pytest.skip('functional tests are disabled') diff --git a/tests/functional/test_bash.py b/tests/functional/test_bash.py index 53d779b7..91859341 100644 --- a/tests/functional/test_bash.py +++ b/tests/functional/test_bash.py @@ -2,7 +2,6 @@ import pytest from tests.functional.plots import with_confirmation, without_confirmation, \ refuse_with_confirmation, history_changed, history_not_changed, \ select_command_with_arrows -from tests.functional.utils import functional containers = ((u'thefuck/ubuntu-python3-bash', u'''FROM ubuntu:latest @@ -30,25 +29,25 @@ def proc(request, spawnu, run_without_docker): return proc -@functional +@pytest.mark.functional def test_with_confirmation(proc, TIMEOUT): with_confirmation(proc, TIMEOUT) history_changed(proc, TIMEOUT, u'echo test') -@functional +@pytest.mark.functional def test_select_command_with_arrows(proc, TIMEOUT): select_command_with_arrows(proc, TIMEOUT) history_changed(proc, TIMEOUT, u'git push') -@functional +@pytest.mark.functional def test_refuse_with_confirmation(proc, TIMEOUT): refuse_with_confirmation(proc, TIMEOUT) history_not_changed(proc, TIMEOUT) -@functional +@pytest.mark.functional def test_without_confirmation(proc, TIMEOUT): without_confirmation(proc, TIMEOUT) history_changed(proc, TIMEOUT, u'echo test') diff --git a/tests/functional/test_fish.py b/tests/functional/test_fish.py index 636fb3c5..eb02f053 100644 --- a/tests/functional/test_fish.py +++ b/tests/functional/test_fish.py @@ -1,7 +1,6 @@ import pytest from tests.functional.plots import with_confirmation, without_confirmation, \ refuse_with_confirmation, select_command_with_arrows -from tests.functional.utils import functional containers = (('thefuck/ubuntu-python3-fish', u'''FROM ubuntu:latest @@ -29,25 +28,25 @@ def proc(request, spawnu): return proc -@functional +@pytest.mark.functional @pytest.mark.skip_without_docker def test_with_confirmation(proc, TIMEOUT): with_confirmation(proc, TIMEOUT) -@functional +@pytest.mark.functional @pytest.mark.skip_without_docker def test_select_command_with_arrows(proc, TIMEOUT): select_command_with_arrows(proc, TIMEOUT) -@functional +@pytest.mark.functional @pytest.mark.skip_without_docker def test_refuse_with_confirmation(proc, TIMEOUT): refuse_with_confirmation(proc, TIMEOUT) -@functional +@pytest.mark.functional @pytest.mark.skip_without_docker def test_without_confirmation(proc, TIMEOUT): without_confirmation(proc, TIMEOUT) diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py index 2fece023..4b2becb7 100644 --- a/tests/functional/test_install.py +++ b/tests/functional/test_install.py @@ -1,7 +1,5 @@ import pytest from thefuck.main import _get_current_version -from tests.functional.utils import functional - envs = ((u'bash', 'thefuck/ubuntu-bash', u''' FROM ubuntu:latest @@ -13,7 +11,7 @@ RUN dnf install -yy python-devel sudo wget gcc ''')) -@functional +@pytest.mark.functional @pytest.mark.skip_without_docker @pytest.mark.parametrize('shell, tag, dockerfile', envs) def test_installation(spawnu, shell, TIMEOUT, tag, dockerfile): diff --git a/tests/functional/test_performance.py b/tests/functional/test_performance.py index 1582a875..c9b69357 100644 --- a/tests/functional/test_performance.py +++ b/tests/functional/test_performance.py @@ -1,6 +1,5 @@ import pytest import time -from tests.functional.utils import functional dockerfile = u''' FROM ubuntu:latest @@ -40,7 +39,7 @@ def plot(proc, TIMEOUT): assert proc.expect([TIMEOUT, u'test']) -@functional +@pytest.mark.functional @pytest.mark.skip_without_docker @pytest.mark.benchmark(min_rounds=10) def test_performance(spawnu, TIMEOUT, benchmark): diff --git a/tests/functional/test_tcsh.py b/tests/functional/test_tcsh.py index 0614cdb1..c4c27e05 100644 --- a/tests/functional/test_tcsh.py +++ b/tests/functional/test_tcsh.py @@ -1,5 +1,4 @@ import pytest -from tests.functional.utils import functional from tests.functional.plots import with_confirmation, without_confirmation, \ refuse_with_confirmation, select_command_with_arrows @@ -30,22 +29,22 @@ def proc(request, spawnu, run_without_docker): return proc -@functional +@pytest.mark.functional def test_with_confirmation(proc, TIMEOUT): with_confirmation(proc, TIMEOUT) -@functional +@pytest.mark.functional def test_select_command_with_arrows(proc, TIMEOUT): select_command_with_arrows(proc, TIMEOUT) -@functional +@pytest.mark.functional def test_refuse_with_confirmation(proc, TIMEOUT): refuse_with_confirmation(proc, TIMEOUT) -@functional +@pytest.mark.functional def test_without_confirmation(proc, TIMEOUT): without_confirmation(proc, TIMEOUT) diff --git a/tests/functional/test_zsh.py b/tests/functional/test_zsh.py index 498d22ed..8d8bbc34 100644 --- a/tests/functional/test_zsh.py +++ b/tests/functional/test_zsh.py @@ -1,5 +1,4 @@ import pytest -from tests.functional.utils import functional from tests.functional.plots import with_confirmation, without_confirmation, \ refuse_with_confirmation, history_changed, history_not_changed, select_command_with_arrows @@ -34,25 +33,25 @@ def proc(request, spawnu, run_without_docker): return proc -@functional +@pytest.mark.functional def test_with_confirmation(proc, TIMEOUT): with_confirmation(proc, TIMEOUT) history_changed(proc, TIMEOUT, u'echo test') -@functional +@pytest.mark.functional def test_select_command_with_arrows(proc, TIMEOUT): select_command_with_arrows(proc, TIMEOUT) history_changed(proc, TIMEOUT, u'git push') -@functional +@pytest.mark.functional def test_refuse_with_confirmation(proc, TIMEOUT): refuse_with_confirmation(proc, TIMEOUT) history_not_changed(proc, TIMEOUT) -@functional +@pytest.mark.functional def test_without_confirmation(proc, TIMEOUT): without_confirmation(proc, TIMEOUT) history_changed(proc, TIMEOUT, u'echo test') diff --git a/tests/functional/utils.py b/tests/functional/utils.py deleted file mode 100644 index 18dcb9df..00000000 --- a/tests/functional/utils.py +++ /dev/null @@ -1,8 +0,0 @@ -import pytest -import os - -enabled = os.environ.get('FUNCTIONAL') - -functional = pytest.mark.skipif( - not enabled, - reason='Functional tests are disabled by default.') From 22eac045c84a17f41818669b4bfa5b3a530eea1d Mon Sep 17 00:00:00 2001 From: nvbn Date: Sun, 6 Sep 2015 01:14:49 +0300 Subject: [PATCH 03/14] Use `@pytest.mark.once_without_docker` --- tests/functional/test_bash.py | 4 ++++ tests/functional/test_tcsh.py | 4 ++++ tests/functional/test_zsh.py | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/tests/functional/test_bash.py b/tests/functional/test_bash.py index 91859341..403c64aa 100644 --- a/tests/functional/test_bash.py +++ b/tests/functional/test_bash.py @@ -30,24 +30,28 @@ 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 push') @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') diff --git a/tests/functional/test_tcsh.py b/tests/functional/test_tcsh.py index c4c27e05..5632370a 100644 --- a/tests/functional/test_tcsh.py +++ b/tests/functional/test_tcsh.py @@ -30,21 +30,25 @@ 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) diff --git a/tests/functional/test_zsh.py b/tests/functional/test_zsh.py index 8d8bbc34..47fe82bb 100644 --- a/tests/functional/test_zsh.py +++ b/tests/functional/test_zsh.py @@ -34,24 +34,28 @@ 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 push') @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') From 5cfd493d05b5e63a13dba011265d7cbc5b59b75f Mon Sep 17 00:00:00 2001 From: nvbn Date: Sun, 6 Sep 2015 01:24:29 +0300 Subject: [PATCH 04/14] Make "arrows" tests less dependent on environment --- tests/functional/plots.py | 4 +++- tests/functional/test_bash.py | 2 +- tests/functional/test_zsh.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/functional/plots.py b/tests/functional/plots.py index 87d49aa5..1b4d06aa 100644 --- a/tests/functional/plots.py +++ b/tests/functional/plots.py @@ -47,9 +47,11 @@ def select_command_with_arrows(proc, TIMEOUT): assert proc.expect([TIMEOUT, u'git help']) proc.send('\033[A') assert proc.expect([TIMEOUT, u'git push']) + proc.send('\033[B') + assert proc.expect([TIMEOUT, u'git help']) proc.send('\n') - assert proc.expect([TIMEOUT, u'Not a git repository']) + assert proc.expect([TIMEOUT, u'usage']) def refuse_with_confirmation(proc, TIMEOUT): diff --git a/tests/functional/test_bash.py b/tests/functional/test_bash.py index 403c64aa..a8b69ffe 100644 --- a/tests/functional/test_bash.py +++ b/tests/functional/test_bash.py @@ -40,7 +40,7 @@ def test_with_confirmation(proc, TIMEOUT): @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 push') + history_changed(proc, TIMEOUT, u'git help') @pytest.mark.functional diff --git a/tests/functional/test_zsh.py b/tests/functional/test_zsh.py index 47fe82bb..1bcb77fb 100644 --- a/tests/functional/test_zsh.py +++ b/tests/functional/test_zsh.py @@ -44,7 +44,7 @@ def test_with_confirmation(proc, TIMEOUT): @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 push') + history_changed(proc, TIMEOUT, u'git help') @pytest.mark.functional From 2f9db24ed18c18a9ec05d206a858dc501c38a923 Mon Sep 17 00:00:00 2001 From: nvbn Date: Sun, 6 Sep 2015 12:55:59 +0300 Subject: [PATCH 05/14] #357 Add `exclude_rules` settings option --- README.md | 4 ++++ tests/test_conf.py | 7 ++++++- tests/test_corrector.py | 42 ++++++++++++++++++++++++++++------------- thefuck/conf.py | 7 ++++++- thefuck/corrector.py | 3 ++- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 5181adec..593ca5f6 100644 --- a/README.md +++ b/README.md @@ -271,6 +271,7 @@ requires_output = True The Fuck has a few settings parameters which can be changed in `~/.thefuck/settings.py`: * `rules` – list of enabled rules, by default `thefuck.conf.DEFAULT_RULES`; +* `exclude_rules` – list of disabled rules, by default `[]`; * `require_confirmation` – requires confirmation before running new command, by default `True`; * `wait_command` – max amount of time in seconds for getting previous command output; * `no_colors` – disable colored output; @@ -281,6 +282,7 @@ Example of `settings.py`: ```python rules = ['sudo', 'no_command'] +exclude_rules = ['git_push'] require_confirmation = True wait_command = 10 no_colors = False @@ -291,6 +293,7 @@ debug = False Or via environment variables: * `THEFUCK_RULES` – list of enabled rules, like `DEFAULT_RULES:rm_root` or `sudo:no_command`; +* `THEFUCK_EXCLUDE_RULES` – list of disabled rules, like `git_pull:git_push`; * `THEFUCK_REQUIRE_CONFIRMATION` – require confirmation before running new command, `true/false`; * `THEFUCK_WAIT_COMMAND` – max amount of time in seconds for getting previous command output; * `THEFUCK_NO_COLORS` – disable colored output, `true/false`; @@ -302,6 +305,7 @@ For example: ```bash export THEFUCK_RULES='sudo:no_command' +export THEFUCK_EXCLUDE_RULES='git_pull:git_push' export THEFUCK_REQUIRE_CONFIRMATION='true' export THEFUCK_WAIT_COMMAND=10 export THEFUCK_NO_COLORS='false' diff --git a/tests/test_conf.py b/tests/test_conf.py index 1fde6a67..2137f7c7 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -39,17 +39,20 @@ class TestSettingsFromFile(object): wait_command=10, require_confirmation=True, no_colors=True, - priority={'vim': 100}) + priority={'vim': 100}, + exclude_rules=['git']) settings = conf.get_settings(Mock()) assert settings.rules == ['test'] assert settings.wait_command == 10 assert settings.require_confirmation is True assert settings.no_colors is True assert settings.priority == {'vim': 100} + assert settings.exclude_rules == ['git'] def test_from_file_with_DEFAULT(self, load_source): load_source.return_value = Mock(rules=conf.DEFAULT_RULES + ['test'], wait_command=10, + exclude_rules=[], require_confirmation=True, no_colors=True) settings = conf.get_settings(Mock()) @@ -60,12 +63,14 @@ class TestSettingsFromFile(object): class TestSettingsFromEnv(object): def test_from_env(self, environ): environ.update({'THEFUCK_RULES': 'bash:lisp', + 'THEFUCK_EXCLUDE_RULES': 'git:vim', 'THEFUCK_WAIT_COMMAND': '55', 'THEFUCK_REQUIRE_CONFIRMATION': 'true', 'THEFUCK_NO_COLORS': 'false', 'THEFUCK_PRIORITY': 'bash=10:lisp=wrong:vim=15'}) settings = conf.get_settings(Mock()) assert settings.rules == ['bash', 'lisp'] + assert settings.exclude_rules == ['git', 'vim'] assert settings.wait_command == 55 assert settings.require_confirmation is True assert settings.no_colors is False diff --git a/tests/test_corrector.py b/tests/test_corrector.py index 2c58a017..1071ae47 100644 --- a/tests/test_corrector.py +++ b/tests/test_corrector.py @@ -22,23 +22,39 @@ def test_load_rule(mocker): class TestGetRules(object): - @pytest.fixture(autouse=True) + @pytest.fixture def glob(self, mocker): - return mocker.patch('thefuck.corrector.Path.glob', return_value=[]) + results = {} + mocker.patch('thefuck.corrector.Path.glob', + new_callable=lambda: lambda *_: results.pop('value', [])) + return lambda value: results.update({'value': value}) - def _compare_names(self, rules, names): - return [r.name for r in rules] == names - - @pytest.mark.parametrize('conf_rules, rules', [ - (conf.DEFAULT_RULES, ['bash', 'lisp', 'bash', 'lisp']), - (types.RulesNamesList(['bash']), ['bash', 'bash'])]) - def test_get(self, monkeypatch, glob, conf_rules, rules): - glob.return_value = [PosixPath('bash.py'), PosixPath('lisp.py')] + @pytest.fixture(autouse=True) + def load_source(self, monkeypatch): monkeypatch.setattr('thefuck.corrector.load_source', lambda x, _: Rule(x)) - assert self._compare_names( - corrector.get_rules(Path('~'), Mock(rules=conf_rules, priority={})), - rules) + + def _compare_names(self, rules, names): + assert {r.name for r in rules} == set(names) + + def _prepare_rules(self, rules): + if rules == conf.DEFAULT_RULES: + return rules + else: + return types.RulesNamesList(rules) + + @pytest.mark.parametrize('paths, conf_rules, exclude_rules, loaded_rules', [ + (['git.py', 'bash.py'], conf.DEFAULT_RULES, [], ['git', 'bash']), + (['git.py', 'bash.py'], ['git'], [], ['git']), + (['git.py', 'bash.py'], conf.DEFAULT_RULES, ['git'], ['bash']), + (['git.py', 'bash.py'], ['git'], ['git'], [])]) + def test_get_rules(self, glob, paths, conf_rules, exclude_rules, loaded_rules): + glob([PosixPath(path) for path in paths]) + settings = Mock(rules=self._prepare_rules(conf_rules), + priority={}, + exclude_rules=self._prepare_rules(exclude_rules)) + rules = corrector.get_rules(Path('~'), settings) + self._compare_names(rules, loaded_rules) class TestIsRuleMatch(object): diff --git a/thefuck/conf.py b/thefuck/conf.py index 2efb6897..e107329a 100644 --- a/thefuck/conf.py +++ b/thefuck/conf.py @@ -26,6 +26,7 @@ DEFAULT_PRIORITY = 1000 DEFAULT_SETTINGS = {'rules': DEFAULT_RULES, + 'exclude_rules': [], 'wait_command': 3, 'require_confirmation': True, 'no_colors': False, @@ -34,6 +35,7 @@ DEFAULT_SETTINGS = {'rules': DEFAULT_RULES, 'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'}} ENV_TO_ATTR = {'THEFUCK_RULES': 'rules', + 'THEFUCK_EXCLUDE_RULES': 'exclude_rules', 'THEFUCK_WAIT_COMMAND': 'wait_command', 'THEFUCK_REQUIRE_CONFIRMATION': 'require_confirmation', 'THEFUCK_NO_COLORS': 'no_colors', @@ -84,7 +86,7 @@ def _priority_from_env(val): def _val_from_env(env, attr): """Transforms env-strings to python.""" val = os.environ[env] - if attr == 'rules': + if attr in ('rules', 'exclude_rules'): return _rules_from_env(val) elif attr == 'priority': return dict(_priority_from_env(val)) @@ -123,6 +125,9 @@ def get_settings(user_dir): if not isinstance(conf['rules'], types.RulesNamesList): conf['rules'] = types.RulesNamesList(conf['rules']) + if not isinstance(conf['exclude_rules'], types.RulesNamesList): + conf['exclude_rules'] = types.RulesNamesList(conf['exclude_rules']) + return types.Settings(conf) diff --git a/thefuck/corrector.py b/thefuck/corrector.py index 1126817e..de442cd2 100644 --- a/thefuck/corrector.py +++ b/thefuck/corrector.py @@ -23,7 +23,8 @@ def get_loaded_rules(rules, settings): for rule in rules: if rule.name != '__init__.py': loaded_rule = load_rule(rule, settings) - if loaded_rule in settings.rules: + if loaded_rule in settings.rules and \ + loaded_rule not in settings.exclude_rules: yield loaded_rule From 439287256817b77fd2d88a8601d4f491196f2130 Mon Sep 17 00:00:00 2001 From: nvbn Date: Sun, 6 Sep 2015 13:29:42 +0300 Subject: [PATCH 06/14] #356 Print useful information when `fuck` called and alias isn't configured --- setup.py | 3 ++- tests/functional/plots.py | 6 ++++++ tests/functional/test_bash.py | 8 +++++++- tests/functional/test_zsh.py | 13 ++++++++++--- thefuck/logs.py | 16 ++++++++++++++++ thefuck/main.py | 12 ++++++++++++ thefuck/shells.py | 24 ++++++++++++++++++++++++ 7 files changed, 77 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index d20ba4dc..4464fa7d 100755 --- a/setup.py +++ b/setup.py @@ -41,4 +41,5 @@ setup(name='thefuck', extras_require=extras_require, entry_points={'console_scripts': [ 'thefuck = thefuck.main:main', - 'thefuck-alias = thefuck.main:print_alias']}) + 'thefuck-alias = thefuck.main:print_alias', + 'fuck = thefuck.main:how_to_configure_alias']}) diff --git a/tests/functional/plots.py b/tests/functional/plots.py index 1b4d06aa..910b4b2b 100644 --- a/tests/functional/plots.py +++ b/tests/functional/plots.py @@ -78,3 +78,9 @@ def without_confirmation(proc, TIMEOUT): proc.sendline(u'fuck') assert proc.expect([TIMEOUT, u'echo test']) assert proc.expect([TIMEOUT, u'test']) + + +def how_to_configure(proc, TIMEOUT): + proc.sendline(u'unalias fuck') + proc.sendline(u'fuck') + assert proc.expect([TIMEOUT, "alias isn't configured"]) diff --git a/tests/functional/test_bash.py b/tests/functional/test_bash.py index a8b69ffe..26d3e7d4 100644 --- a/tests/functional/test_bash.py +++ b/tests/functional/test_bash.py @@ -1,7 +1,7 @@ import pytest from tests.functional.plots import with_confirmation, without_confirmation, \ refuse_with_confirmation, history_changed, history_not_changed, \ - select_command_with_arrows + select_command_with_arrows, how_to_configure containers = ((u'thefuck/ubuntu-python3-bash', u'''FROM ubuntu:latest @@ -55,3 +55,9 @@ def test_refuse_with_confirmation(proc, TIMEOUT): 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) diff --git a/tests/functional/test_zsh.py b/tests/functional/test_zsh.py index 1bcb77fb..c0df1f09 100644 --- a/tests/functional/test_zsh.py +++ b/tests/functional/test_zsh.py @@ -1,8 +1,9 @@ import pytest from tests.functional.plots import with_confirmation, without_confirmation, \ - refuse_with_confirmation, history_changed, history_not_changed, select_command_with_arrows + refuse_with_confirmation, history_changed, history_not_changed, \ + select_command_with_arrows, how_to_configure -containers = (('ubuntu-python3-zsh', +containers = (('thefuck/ubuntu-python3-zsh', u'''FROM ubuntu:latest RUN apt-get update RUN apt-get install -yy python3 python3-pip python3-dev git @@ -10,7 +11,7 @@ containers = (('ubuntu-python3-zsh', RUN ln -s /usr/bin/pip3 /usr/bin/pip RUN apt-get install -yy zsh''', u'zsh'), - ('ubuntu-python2-zsh', + ('thefuck/ubuntu-python2-zsh', u'''FROM ubuntu:latest RUN apt-get update RUN apt-get install -yy python python-pip python-dev git @@ -59,3 +60,9 @@ def test_refuse_with_confirmation(proc, TIMEOUT): 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) diff --git a/thefuck/logs.py b/thefuck/logs.py index ae5f1ad2..4db63486 100644 --- a/thefuck/logs.py +++ b/thefuck/logs.py @@ -76,3 +76,19 @@ def debug_time(msg, settings): yield finally: debug(u'{} took: {}'.format(msg, datetime.now() - started), settings) + + +def how_to_configure_alias(configuration_details, settings): + print("Seems like {bold}fuck{reset} alias isn't configured!".format( + bold=color(colorama.Style.BRIGHT, settings), + reset=color(colorama.Style.RESET_ALL, settings))) + if configuration_details: + content, path = configuration_details + print( + "Please put {bold}{content}{reset} in your " + "{bold}{path}{reset}.".format( + bold=color(colorama.Style.BRIGHT, settings), + reset=color(colorama.Style.RESET_ALL, settings), + path=path, + content=content)) + print('More details - https://github.com/nvbn/thefuck#manual-installation') diff --git a/thefuck/main.py b/thefuck/main.py index 94aaef5b..046973a1 100644 --- a/thefuck/main.py +++ b/thefuck/main.py @@ -120,6 +120,18 @@ def print_alias(entry_point=True): print(shells.app_alias(alias)) +def how_to_configure_alias(): + """Shows useful information about how-to configure alias. + + It'll be only visible when user type fuck and when alias isn't configured. + + """ + colorama.init() + user_dir = setup_user_dir() + settings = conf.get_settings(user_dir) + logs.how_to_configure_alias(shells.how_to_configure(), settings) + + def main(): parser = ArgumentParser(prog='thefuck') parser.add_argument('-v', '--version', diff --git a/thefuck/shells.py b/thefuck/shells.py index 753373fa..9634f1d7 100644 --- a/thefuck/shells.py +++ b/thefuck/shells.py @@ -72,6 +72,9 @@ class Generic(object): def and_(self, *commands): return u' && '.join(commands) + def how_to_configure(self): + return + class Bash(Generic): def app_alias(self, fuck): @@ -103,6 +106,15 @@ class Bash(Generic): def _script_from_history(self, line): return line + def how_to_configure(self): + if os.path.join(os.path.expanduser('~'), '.bashrc'): + config = '~/.bashrc' + elif os.path.join(os.path.expanduser('~'), '.bash_profile'): + config = '~/.bashrc' + else: + config = 'bash config' + return 'eval $(thefuck --alias)', config + class Fish(Generic): @@ -156,6 +168,9 @@ class Fish(Generic): def and_(self, *commands): return u'; and '.join(commands) + def how_to_configure(self): + return 'eval thefuck --alias', '~/.config/fish/config.fish' + class Zsh(Generic): def app_alias(self, fuck): @@ -191,6 +206,9 @@ class Zsh(Generic): else: return '' + def how_to_configure(self): + return 'eval $(thefuck --alias)', '~/.zshrc' + class Tcsh(Generic): def app_alias(self, fuck): @@ -217,6 +235,9 @@ class Tcsh(Generic): def _get_history_line(self, command_script): return u'#+{}\n{}\n'.format(int(time()), command_script) + def how_to_configure(self): + return 'eval `thefuck --alias`', '~/.tcshrc' + shells = defaultdict(Generic, { 'bash': Bash(), @@ -266,3 +287,6 @@ def get_aliases(): @memoize def get_history(): return list(_get_shell().get_history()) + +def how_to_configure(): + return _get_shell().how_to_configure() From dd0667ea8f74a3e762dd981215667a73a18e6331 Mon Sep 17 00:00:00 2001 From: nvbn Date: Sun, 6 Sep 2015 13:37:48 +0300 Subject: [PATCH 07/14] #356 Ignore `thefuck` entry points --- tests/test_utils.py | 2 +- thefuck/utils.py | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 99c22464..b51ec394 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -53,7 +53,7 @@ def get_aliases(mocker): @pytest.mark.usefixtures('no_memoize', 'get_aliases') -def test_get_all_callables(): +def test_get_all_executables(): all_callables = get_all_executables() assert 'vim' in all_callables assert 'fsck' in all_callables diff --git a/thefuck/utils.py b/thefuck/utils.py index 2736e853..57fe2f50 100644 --- a/thefuck/utils.py +++ b/thefuck/utils.py @@ -10,6 +10,7 @@ import pickle import re from pathlib import Path +import pkg_resources import six @@ -94,11 +95,17 @@ def get_all_executables(): return fallback tf_alias = thefuck_alias() - return [exe.name + tf_entry_points = pkg_resources.require('thefuck')[0]\ + .get_entry_map()\ + .get('console_scripts', {})\ + .keys() + bins = [exe.name for path in os.environ.get('PATH', '').split(':') for exe in _safe(lambda: list(Path(path).iterdir()), []) - if not _safe(exe.is_dir, True)] + [ - alias for alias in get_aliases() if alias != tf_alias] + if not _safe(exe.is_dir, True) + and exe.name not in tf_entry_points] + aliases = [alias for alias in get_aliases() if alias != tf_alias] + return bins + aliases def replace_argument(script, from_, to): From f964c65f16b973fdf5070f93aea8aa3e0026dbd8 Mon Sep 17 00:00:00 2001 From: nvbn Date: Sun, 6 Sep 2015 13:40:29 +0300 Subject: [PATCH 08/14] #362 Fix tests for python 2 --- tests/functional/plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/plots.py b/tests/functional/plots.py index 910b4b2b..fcbede63 100644 --- a/tests/functional/plots.py +++ b/tests/functional/plots.py @@ -83,4 +83,4 @@ def without_confirmation(proc, TIMEOUT): def how_to_configure(proc, TIMEOUT): proc.sendline(u'unalias fuck') proc.sendline(u'fuck') - assert proc.expect([TIMEOUT, "alias isn't configured"]) + assert proc.expect([TIMEOUT, u"alias isn't configured"]) From 105d3d813749ebf9ee70692e7bbecb16a6d7c6fe Mon Sep 17 00:00:00 2001 From: nvbn Date: Sun, 6 Sep 2015 21:47:12 +0300 Subject: [PATCH 09/14] Make settings a global singleton --- tests/test_conf.py | 10 ++++---- thefuck/conf.py | 41 ++++++++++++++---------------- thefuck/corrector.py | 47 ++++++++++++++++++----------------- thefuck/logs.py | 59 ++++++++++++++++++++++---------------------- thefuck/main.py | 42 +++++++++++++++---------------- thefuck/types.py | 20 ++++++--------- thefuck/ui.py | 11 +++++---- thefuck/utils.py | 3 ++- 8 files changed, 114 insertions(+), 119 deletions(-) diff --git a/tests/test_conf.py b/tests/test_conf.py index 2137f7c7..411cd063 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -29,7 +29,7 @@ def environ(monkeypatch): def test_settings_defaults(load_source): load_source.return_value = object() for key, val in conf.DEFAULT_SETTINGS.items(): - assert getattr(conf.get_settings(Mock()), key) == val + assert getattr(conf.init_settings(Mock()), key) == val @pytest.mark.usefixture('environ') @@ -41,7 +41,7 @@ class TestSettingsFromFile(object): no_colors=True, priority={'vim': 100}, exclude_rules=['git']) - settings = conf.get_settings(Mock()) + settings = conf.init_settings(Mock()) assert settings.rules == ['test'] assert settings.wait_command == 10 assert settings.require_confirmation is True @@ -55,7 +55,7 @@ class TestSettingsFromFile(object): exclude_rules=[], require_confirmation=True, no_colors=True) - settings = conf.get_settings(Mock()) + settings = conf.init_settings(Mock()) assert settings.rules == conf.DEFAULT_RULES + ['test'] @@ -68,7 +68,7 @@ class TestSettingsFromEnv(object): 'THEFUCK_REQUIRE_CONFIRMATION': 'true', 'THEFUCK_NO_COLORS': 'false', 'THEFUCK_PRIORITY': 'bash=10:lisp=wrong:vim=15'}) - settings = conf.get_settings(Mock()) + settings = conf.init_settings(Mock()) assert settings.rules == ['bash', 'lisp'] assert settings.exclude_rules == ['git', 'vim'] assert settings.wait_command == 55 @@ -78,7 +78,7 @@ class TestSettingsFromEnv(object): def test_from_env_with_DEFAULT(self, environ): environ.update({'THEFUCK_RULES': 'DEFAULT_RULES:bash:lisp'}) - settings = conf.get_settings(Mock()) + settings = conf.init_settings(Mock()) assert settings.rules == conf.DEFAULT_RULES + ['bash', 'lisp'] diff --git a/thefuck/conf.py b/thefuck/conf.py index e107329a..2b8178ba 100644 --- a/thefuck/conf.py +++ b/thefuck/conf.py @@ -1,12 +1,11 @@ -from copy import copy from imp import load_source import os import sys from six import text_type -from . import logs, types +from .types import RulesNamesList, Settings -class _DefaultRulesNames(types.RulesNamesList): +class _DefaultRulesNames(RulesNamesList): def __add__(self, items): return _DefaultRulesNames(list(self) + items) @@ -24,7 +23,6 @@ class _DefaultRulesNames(types.RulesNamesList): DEFAULT_RULES = _DefaultRulesNames([]) DEFAULT_PRIORITY = 1000 - DEFAULT_SETTINGS = {'rules': DEFAULT_RULES, 'exclude_rules': [], 'wait_command': 3, @@ -42,7 +40,6 @@ ENV_TO_ATTR = {'THEFUCK_RULES': 'rules', 'THEFUCK_PRIORITY': 'priority', 'THEFUCK_DEBUG': 'debug'} - SETTINGS_HEADER = u"""# ~/.thefuck/settings.py: The Fuck settings file # # The rules are defined as in the example bellow: @@ -105,30 +102,28 @@ def _settings_from_env(): if env in os.environ} -def get_settings(user_dir): - """Returns settings filled with values from `settings.py` and env.""" - conf = copy(DEFAULT_SETTINGS) - try: - conf.update(_settings_from_file(user_dir)) - except Exception: - logs.exception("Can't load settings from file", - sys.exc_info(), - types.Settings(conf)) +settings = Settings(DEFAULT_SETTINGS) + + +def init_settings(user_dir): + """Fills `settings` with values from `settings.py` and env.""" + from .logs import exception try: - conf.update(_settings_from_env()) + settings.update(_settings_from_file(user_dir)) except Exception: - logs.exception("Can't load settings from env", - sys.exc_info(), - types.Settings(conf)) + exception("Can't load settings from file", sys.exc_info()) - if not isinstance(conf['rules'], types.RulesNamesList): - conf['rules'] = types.RulesNamesList(conf['rules']) + try: + settings.update(_settings_from_env()) + except Exception: + exception("Can't load settings from env", sys.exc_info()) - if not isinstance(conf['exclude_rules'], types.RulesNamesList): - conf['exclude_rules'] = types.RulesNamesList(conf['exclude_rules']) + if not isinstance(settings['rules'], RulesNamesList): + settings.rules = RulesNamesList(settings['rules']) - return types.Settings(conf) + if not isinstance(settings.exclude_rules, RulesNamesList): + settings.exclude_rules = RulesNamesList(settings.exclude_rules) def initialize_settings_file(user_dir): diff --git a/thefuck/corrector.py b/thefuck/corrector.py index de442cd2..8840749a 100644 --- a/thefuck/corrector.py +++ b/thefuck/corrector.py @@ -1,16 +1,18 @@ import sys from imp import load_source from pathlib import Path -from . import conf, types, logs +from .conf import settings, DEFAULT_PRIORITY +from .types import Rule, CorrectedCommand, SortedCorrectedCommandsSequence +from . import logs -def load_rule(rule, settings): +def load_rule(rule): """Imports rule module and returns it.""" name = rule.name[:-3] - with logs.debug_time(u'Importing rule: {};'.format(name), settings): + with logs.debug_time(u'Importing rule: {};'.format(name)): rule_module = load_source(name, str(rule)) - priority = getattr(rule_module, 'priority', conf.DEFAULT_PRIORITY) - return types.Rule(name, rule_module.match, + priority = getattr(rule_module, 'priority', DEFAULT_PRIORITY) + return Rule(name, rule_module.match, rule_module.get_new_command, getattr(rule_module, 'enabled_by_default', True), getattr(rule_module, 'side_effect', None), @@ -18,27 +20,27 @@ def load_rule(rule, settings): getattr(rule_module, 'requires_output', True)) -def get_loaded_rules(rules, settings): +def get_loaded_rules(rules): """Yields all available rules.""" for rule in rules: if rule.name != '__init__.py': - loaded_rule = load_rule(rule, settings) + loaded_rule = load_rule(rule) if loaded_rule in settings.rules and \ - loaded_rule not in settings.exclude_rules: + loaded_rule not in settings.exclude_rules: yield loaded_rule -def get_rules(user_dir, settings): +def get_rules(user_dir): """Returns all enabled rules.""" bundled = Path(__file__).parent \ .joinpath('rules') \ .glob('*.py') user = user_dir.joinpath('rules').glob('*.py') - return sorted(get_loaded_rules(sorted(bundled) + sorted(user), settings), + return sorted(get_loaded_rules(sorted(bundled) + sorted(user)), key=lambda rule: rule.priority) -def is_rule_match(command, rule, settings): +def is_rule_match(command, rule): """Returns first matched rule for command.""" script_only = command.stdout is None and command.stderr is None @@ -46,27 +48,26 @@ def is_rule_match(command, rule, settings): return False try: - with logs.debug_time(u'Trying rule: {};'.format(rule.name), - settings): + with logs.debug_time(u'Trying rule: {};'.format(rule.name)): if rule.match(command, settings): return True except Exception: - logs.rule_failed(rule, sys.exc_info(), settings) + logs.rule_failed(rule, sys.exc_info()) -def make_corrected_commands(command, rule, settings): +def make_corrected_commands(command, rule): new_commands = rule.get_new_command(command, settings) if not isinstance(new_commands, list): new_commands = (new_commands,) for n, new_command in enumerate(new_commands): - yield types.CorrectedCommand(script=new_command, - side_effect=rule.side_effect, - priority=(n + 1) * rule.priority) + yield CorrectedCommand(script=new_command, + side_effect=rule.side_effect, + priority=(n + 1) * rule.priority) -def get_corrected_commands(command, user_dir, settings): +def get_corrected_commands(command, user_dir): corrected_commands = ( - corrected for rule in get_rules(user_dir, settings) - if is_rule_match(command, rule, settings) - for corrected in make_corrected_commands(command, rule, settings)) - return types.SortedCorrectedCommandsSequence(corrected_commands, settings) + corrected for rule in get_rules(user_dir) + if is_rule_match(command, rule) + for corrected in make_corrected_commands(command, rule)) + return SortedCorrectedCommandsSequence(corrected_commands) diff --git a/thefuck/logs.py b/thefuck/logs.py index 4db63486..93162979 100644 --- a/thefuck/logs.py +++ b/thefuck/logs.py @@ -5,9 +5,10 @@ from datetime import datetime import sys from traceback import format_exception import colorama +from .conf import settings -def color(color_, settings): +def color(color_): """Utility for ability to disabling colored output.""" if settings.no_colors: return '' @@ -15,37 +16,37 @@ def color(color_, settings): return color_ -def exception(title, exc_info, settings): +def exception(title, exc_info): sys.stderr.write( u'{warn}[WARN] {title}:{reset}\n{trace}' u'{warn}----------------------------{reset}\n\n'.format( warn=color(colorama.Back.RED + colorama.Fore.WHITE - + colorama.Style.BRIGHT, settings), - reset=color(colorama.Style.RESET_ALL, settings), + + colorama.Style.BRIGHT), + reset=color(colorama.Style.RESET_ALL), title=title, trace=''.join(format_exception(*exc_info)))) -def rule_failed(rule, exc_info, settings): - exception('Rule {}'.format(rule.name), exc_info, settings) +def rule_failed(rule, exc_info): + exception('Rule {}'.format(rule.name), exc_info) -def failed(msg, settings): +def failed(msg): sys.stderr.write('{red}{msg}{reset}\n'.format( msg=msg, - red=color(colorama.Fore.RED, settings), - reset=color(colorama.Style.RESET_ALL, settings))) + red=color(colorama.Fore.RED), + reset=color(colorama.Style.RESET_ALL))) -def show_corrected_command(corrected_command, settings): +def show_corrected_command(corrected_command): sys.stderr.write('{bold}{script}{reset}{side_effect}\n'.format( script=corrected_command.script, side_effect=' (+side effect)' if corrected_command.side_effect else '', - bold=color(colorama.Style.BRIGHT, settings), - reset=color(colorama.Style.RESET_ALL, settings))) + bold=color(colorama.Style.BRIGHT), + reset=color(colorama.Style.RESET_ALL))) -def confirm_text(corrected_command, settings): +def confirm_text(corrected_command): sys.stderr.write( ('{clear}{bold}{script}{reset}{side_effect} ' '[{green}enter{reset}/{blue}↑{reset}/{blue}↓{reset}' @@ -53,42 +54,42 @@ def confirm_text(corrected_command, settings): script=corrected_command.script, side_effect=' (+side effect)' if corrected_command.side_effect else '', clear='\033[1K\r', - bold=color(colorama.Style.BRIGHT, settings), - green=color(colorama.Fore.GREEN, settings), - red=color(colorama.Fore.RED, settings), - reset=color(colorama.Style.RESET_ALL, settings), - blue=color(colorama.Fore.BLUE, settings))) + bold=color(colorama.Style.BRIGHT), + green=color(colorama.Fore.GREEN), + red=color(colorama.Fore.RED), + reset=color(colorama.Style.RESET_ALL), + blue=color(colorama.Fore.BLUE))) -def debug(msg, settings): +def debug(msg): if settings.debug: sys.stderr.write(u'{blue}{bold}DEBUG:{reset} {msg}\n'.format( msg=msg, - reset=color(colorama.Style.RESET_ALL, settings), - blue=color(colorama.Fore.BLUE, settings), - bold=color(colorama.Style.BRIGHT, settings))) + reset=color(colorama.Style.RESET_ALL), + blue=color(colorama.Fore.BLUE), + bold=color(colorama.Style.BRIGHT))) @contextmanager -def debug_time(msg, settings): +def debug_time(msg): started = datetime.now() try: yield finally: - debug(u'{} took: {}'.format(msg, datetime.now() - started), settings) + debug(u'{} took: {}'.format(msg, datetime.now() - started)) -def how_to_configure_alias(configuration_details, settings): +def how_to_configure_alias(configuration_details): print("Seems like {bold}fuck{reset} alias isn't configured!".format( - bold=color(colorama.Style.BRIGHT, settings), - reset=color(colorama.Style.RESET_ALL, settings))) + bold=color(colorama.Style.BRIGHT), + reset=color(colorama.Style.RESET_ALL))) if configuration_details: content, path = configuration_details print( "Please put {bold}{content}{reset} in your " "{bold}{path}{reset}.".format( - bold=color(colorama.Style.BRIGHT, settings), - reset=color(colorama.Style.RESET_ALL, settings), + bold=color(colorama.Style.BRIGHT), + reset=color(colorama.Style.RESET_ALL), path=path, content=content)) print('More details - https://github.com/nvbn/thefuck#manual-installation') diff --git a/thefuck/main.py b/thefuck/main.py index 046973a1..262dcee5 100644 --- a/thefuck/main.py +++ b/thefuck/main.py @@ -10,7 +10,8 @@ import sys from psutil import Process, TimeoutExpired import colorama import six -from . import logs, conf, types, shells +from . import logs, types, shells +from .conf import initialize_settings_file, init_settings, settings from .corrector import get_corrected_commands from .ui import select_command @@ -21,11 +22,11 @@ def setup_user_dir(): rules_dir = user_dir.joinpath('rules') if not rules_dir.is_dir(): rules_dir.mkdir(parents=True) - conf.initialize_settings_file(user_dir) + initialize_settings_file(user_dir) return user_dir -def wait_output(settings, popen): +def wait_output(popen): """Returns `True` if we can get output of the command in the `settings.wait_command` time. @@ -43,7 +44,7 @@ def wait_output(settings, popen): return False -def get_command(settings, args): +def get_command(args): """Creates command from `args` and executes it.""" if six.PY2: script = ' '.join(arg.decode('utf-8') for arg in args[1:]) @@ -58,23 +59,22 @@ def get_command(settings, args): env = dict(os.environ) env.update(settings.env) - with logs.debug_time(u'Call: {}; with env: {};'.format(script, env), - settings): + with logs.debug_time(u'Call: {}; with env: {};'.format(script, env)): result = Popen(script, shell=True, stdout=PIPE, stderr=PIPE, env=env) - if wait_output(settings, result): + if wait_output(result): stdout = result.stdout.read().decode('utf-8') stderr = result.stderr.read().decode('utf-8') - logs.debug(u'Received stdout: {}'.format(stdout), settings) - logs.debug(u'Received stderr: {}'.format(stderr), settings) + logs.debug(u'Received stdout: {}'.format(stdout)) + logs.debug(u'Received stderr: {}'.format(stderr)) return types.Command(script, stdout, stderr) else: - logs.debug(u'Execution timed out!', settings) + logs.debug(u'Execution timed out!') return types.Command(script, None, None) -def run_command(old_cmd, command, settings): +def run_command(old_cmd, command): """Runs command from rule for passed command.""" if command.side_effect: command.side_effect(old_cmd, command.script, settings) @@ -87,20 +87,20 @@ def run_command(old_cmd, command, settings): def fix_command(): colorama.init() user_dir = setup_user_dir() - settings = conf.get_settings(user_dir) - with logs.debug_time('Total', settings): - logs.debug(u'Run with settings: {}'.format(pformat(settings)), settings) + init_settings(user_dir) + with logs.debug_time('Total'): + logs.debug(u'Run with settings: {}'.format(pformat(settings))) - command = get_command(settings, sys.argv) + command = get_command(sys.argv) if not command: - logs.debug('Empty command, nothing to do', settings) + logs.debug('Empty command, nothing to do') return - corrected_commands = get_corrected_commands(command, user_dir, settings) - selected_command = select_command(corrected_commands, settings) + corrected_commands = get_corrected_commands(command, user_dir) + selected_command = select_command(corrected_commands) if selected_command: - run_command(command, selected_command, settings) + run_command(command, selected_command) def _get_current_version(): @@ -128,8 +128,8 @@ def how_to_configure_alias(): """ colorama.init() user_dir = setup_user_dir() - settings = conf.get_settings(user_dir) - logs.how_to_configure_alias(shells.how_to_configure(), settings) + init_settings(user_dir) + logs.how_to_configure_alias(shells.how_to_configure()) def main(): diff --git a/thefuck/types.py b/thefuck/types.py index cf091376..8e302412 100644 --- a/thefuck/types.py +++ b/thefuck/types.py @@ -1,6 +1,5 @@ from collections import namedtuple from traceback import format_stack -from .logs import debug Command = namedtuple('Command', ('script', 'stdout', 'stderr')) @@ -8,6 +7,7 @@ Rule = namedtuple('Rule', ('name', 'match', 'get_new_command', 'enabled_by_default', 'side_effect', 'priority', 'requires_output')) + class CorrectedCommand(object): def __init__(self, script, side_effect, priority): self.script = script @@ -17,7 +17,7 @@ class CorrectedCommand(object): def __eq__(self, other): """Ignores `priority` field.""" if isinstance(other, CorrectedCommand): - return (other.script, other.side_effect) ==\ + return (other.script, other.side_effect) == \ (self.script, self.side_effect) else: return False @@ -41,13 +41,8 @@ class Settings(dict): def __getattr__(self, item): return self.get(item) - def update(self, **kwargs): - """ - Returns new settings with values from `kwargs` for unset settings. - """ - conf = dict(kwargs) - conf.update(self) - return Settings(conf) + def __setattr__(self, key, value): + self[key] = value class SortedCorrectedCommandsSequence(object): @@ -59,8 +54,7 @@ class SortedCorrectedCommandsSequence(object): """ - def __init__(self, commands, settings): - self._settings = settings + def __init__(self, commands): self._commands = commands self._cached = self._realise_first() self._realised = False @@ -81,13 +75,15 @@ class SortedCorrectedCommandsSequence(object): def _realise(self): """Realises generator, removes duplicates and sorts commands.""" + from .logs import debug + if self._cached: commands = self._remove_duplicates(self._commands) self._cached = [self._cached[0]] + sorted( commands, key=lambda corrected_command: corrected_command.priority) self._realised = True debug('SortedCommandsSequence was realised with: {}, after: {}'.format( - self._cached, '\n'.join(format_stack())), self._settings) + self._cached, '\n'.join(format_stack()))) def __getitem__(self, item): if item != 0 and not self._realised: diff --git a/thefuck/ui.py b/thefuck/ui.py index 36dced97..4cd73037 100644 --- a/thefuck/ui.py +++ b/thefuck/ui.py @@ -1,6 +1,7 @@ # -*- encoding: utf-8 -*- import sys +from .conf import settings from . import logs try: @@ -71,7 +72,7 @@ class CommandSelector(object): fn(self.value) -def select_command(corrected_commands, settings): +def select_command(corrected_commands): """Returns: - the first command when confirmation disabled; @@ -80,21 +81,21 @@ def select_command(corrected_commands, settings): """ if not corrected_commands: - logs.failed('No fucks given', settings) + logs.failed('No fucks given') return selector = CommandSelector(corrected_commands) if not settings.require_confirmation: - logs.show_corrected_command(selector.value, settings) + logs.show_corrected_command(selector.value) return selector.value - selector.on_change(lambda val: logs.confirm_text(val, settings)) + selector.on_change(lambda val: logs.confirm_text(val)) for action in read_actions(): if action == SELECT: sys.stderr.write('\n') return selector.value elif action == ABORT: - logs.failed('\nAborted', settings) + logs.failed('\nAborted') return elif action == PREVIOUS: selector.previous() diff --git a/thefuck/utils.py b/thefuck/utils.py index 57fe2f50..59cfcf7f 100644 --- a/thefuck/utils.py +++ b/thefuck/utils.py @@ -12,6 +12,7 @@ import re from pathlib import Path import pkg_resources import six +from .types import Settings DEVNULL = open(os.devnull, 'w') @@ -70,7 +71,7 @@ def wrap_settings(params): """ def _wrap_settings(fn, command, settings): - return fn(command, settings.update(**params)) + return fn(command, Settings(settings, **params)) return decorator(_wrap_settings) From 382eb8b86c7e4d403aa3cc00b7d4880b93ebbf42 Mon Sep 17 00:00:00 2001 From: nvbn Date: Mon, 7 Sep 2015 12:12:16 +0300 Subject: [PATCH 10/14] Fix tests --- tests/conftest.py | 12 ++++++++--- tests/test_conf.py | 21 +++++++++--------- tests/test_corrector.py | 29 +++++++++++++------------ tests/test_logs.py | 22 ++++++++++++------- tests/test_main.py | 10 ++++----- tests/test_types.py | 19 +++++------------ tests/test_ui.py | 45 +++++++++++++++------------------------ tests/test_utils.py | 6 +++--- thefuck/corrector.py | 10 ++++----- thefuck/rules/fix_file.py | 4 ++-- thefuck/utils.py | 8 ++++--- 11 files changed, 91 insertions(+), 95 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 9e115352..e54acfc0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,5 @@ import pytest -from mock import Mock +from thefuck import conf def pytest_addoption(parser): @@ -14,9 +14,15 @@ def no_memoize(monkeypatch): monkeypatch.setattr('thefuck.utils.memoize.disabled', True) +@pytest.fixture(autouse=True) +def settings(request): + request.addfinalizer(lambda: conf.settings.update(conf.DEFAULT_SETTINGS)) + return conf.settings + + @pytest.fixture -def settings(): - return Mock(debug=False, no_colors=True) +def no_colors(settings): + settings.no_colors = True @pytest.fixture(autouse=True) diff --git a/tests/test_conf.py b/tests/test_conf.py index 411cd063..e1042072 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -26,22 +26,23 @@ def environ(monkeypatch): @pytest.mark.usefixture('environ') -def test_settings_defaults(load_source): +def test_settings_defaults(load_source, settings): load_source.return_value = object() + conf.init_settings(Mock()) for key, val in conf.DEFAULT_SETTINGS.items(): - assert getattr(conf.init_settings(Mock()), key) == val + assert getattr(settings, key) == val @pytest.mark.usefixture('environ') class TestSettingsFromFile(object): - def test_from_file(self, load_source): + def test_from_file(self, load_source, settings): load_source.return_value = Mock(rules=['test'], wait_command=10, require_confirmation=True, no_colors=True, priority={'vim': 100}, exclude_rules=['git']) - settings = conf.init_settings(Mock()) + conf.init_settings(Mock()) assert settings.rules == ['test'] assert settings.wait_command == 10 assert settings.require_confirmation is True @@ -49,26 +50,26 @@ class TestSettingsFromFile(object): assert settings.priority == {'vim': 100} assert settings.exclude_rules == ['git'] - def test_from_file_with_DEFAULT(self, load_source): + def test_from_file_with_DEFAULT(self, load_source, settings): load_source.return_value = Mock(rules=conf.DEFAULT_RULES + ['test'], wait_command=10, exclude_rules=[], require_confirmation=True, no_colors=True) - settings = conf.init_settings(Mock()) + conf.init_settings(Mock()) assert settings.rules == conf.DEFAULT_RULES + ['test'] @pytest.mark.usefixture('load_source') class TestSettingsFromEnv(object): - def test_from_env(self, environ): + def test_from_env(self, environ, settings): environ.update({'THEFUCK_RULES': 'bash:lisp', 'THEFUCK_EXCLUDE_RULES': 'git:vim', 'THEFUCK_WAIT_COMMAND': '55', 'THEFUCK_REQUIRE_CONFIRMATION': 'true', 'THEFUCK_NO_COLORS': 'false', 'THEFUCK_PRIORITY': 'bash=10:lisp=wrong:vim=15'}) - settings = conf.init_settings(Mock()) + conf.init_settings(Mock()) assert settings.rules == ['bash', 'lisp'] assert settings.exclude_rules == ['git', 'vim'] assert settings.wait_command == 55 @@ -76,9 +77,9 @@ class TestSettingsFromEnv(object): assert settings.no_colors is False assert settings.priority == {'bash': 10, 'vim': 15} - def test_from_env_with_DEFAULT(self, environ): + def test_from_env_with_DEFAULT(self, environ, settings): environ.update({'THEFUCK_RULES': 'DEFAULT_RULES:bash:lisp'}) - settings = conf.init_settings(Mock()) + conf.init_settings(Mock()) assert settings.rules == conf.DEFAULT_RULES + ['bash', 'lisp'] diff --git a/tests/test_corrector.py b/tests/test_corrector.py index 1071ae47..f84e7d18 100644 --- a/tests/test_corrector.py +++ b/tests/test_corrector.py @@ -16,7 +16,7 @@ def test_load_rule(mocker): enabled_by_default=True, priority=900, requires_output=True)) - assert corrector.load_rule(Path('/rules/bash.py'), settings=Mock(priority={})) \ + assert corrector.load_rule(Path('/rules/bash.py')) \ == Rule('bash', match, get_new_command, priority=900) load_source.assert_called_once_with('bash', '/rules/bash.py') @@ -48,29 +48,30 @@ class TestGetRules(object): (['git.py', 'bash.py'], ['git'], [], ['git']), (['git.py', 'bash.py'], conf.DEFAULT_RULES, ['git'], ['bash']), (['git.py', 'bash.py'], ['git'], ['git'], [])]) - def test_get_rules(self, glob, paths, conf_rules, exclude_rules, loaded_rules): + def test_get_rules(self, glob, settings, paths, conf_rules, exclude_rules, + loaded_rules): glob([PosixPath(path) for path in paths]) - settings = Mock(rules=self._prepare_rules(conf_rules), + settings.update(rules=self._prepare_rules(conf_rules), priority={}, exclude_rules=self._prepare_rules(exclude_rules)) - rules = corrector.get_rules(Path('~'), settings) + rules = corrector.get_rules(Path('~')) self._compare_names(rules, loaded_rules) class TestIsRuleMatch(object): - def test_no_match(self, settings): + def test_no_match(self): assert not corrector.is_rule_match( - Command('ls'), Rule('', lambda *_: False), settings) + Command('ls'), Rule('', lambda *_: False)) - def test_match(self, settings): + def test_match(self): rule = Rule('', lambda x, _: x.script == 'cd ..') - assert corrector.is_rule_match(Command('cd ..'), rule, settings) + assert corrector.is_rule_match(Command('cd ..'), rule) - def test_when_rule_failed(self, capsys, settings): + @pytest.mark.usefixtures('no_colors') + def test_when_rule_failed(self, capsys): rule = Rule('test', Mock(side_effect=OSError('Denied')), requires_output=False) - assert not corrector.is_rule_match( - Command('ls'), rule, settings) + assert not corrector.is_rule_match(Command('ls'), rule) assert capsys.readouterr()[1].split('\n')[0] == '[WARN] Rule test:' @@ -78,14 +79,14 @@ class TestMakeCorrectedCommands(object): def test_with_rule_returns_list(self): rule = Rule(get_new_command=lambda x, _: [x.script + '!', x.script + '@'], priority=100) - assert list(make_corrected_commands(Command(script='test'), rule, None)) \ + assert list(make_corrected_commands(Command(script='test'), rule)) \ == [CorrectedCommand(script='test!', priority=100), CorrectedCommand(script='test@', priority=200)] def test_with_rule_returns_command(self): rule = Rule(get_new_command=lambda x, _: x.script + '!', priority=100) - assert list(make_corrected_commands(Command(script='test'), rule, None)) \ + assert list(make_corrected_commands(Command(script='test'), rule)) \ == [CorrectedCommand(script='test!', priority=100)] def test_get_corrected_commands(mocker): @@ -97,5 +98,5 @@ def test_get_corrected_commands(mocker): get_new_command=lambda x, _: [x.script + '@', x.script + ';'], priority=60)] mocker.patch('thefuck.corrector.get_rules', return_value=rules) - assert [cmd.script for cmd in get_corrected_commands(command, None, Mock(debug=False))] \ + assert [cmd.script for cmd in get_corrected_commands(command, None)] \ == ['test!', 'test@', 'test;'] diff --git a/tests/test_logs.py b/tests/test_logs.py index 6e87fe8b..4b585ee4 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -1,14 +1,20 @@ +import pytest from mock import Mock from thefuck import logs -def test_color(): - assert logs.color('red', Mock(no_colors=False)) == 'red' - assert logs.color('red', Mock(no_colors=True)) == '' +def test_color(settings): + settings.no_colors = False + assert logs.color('red') == 'red' + settings.no_colors = True + assert logs.color('red') == '' -def test_debug(capsys): - logs.debug('test', Mock(no_colors=True, debug=True)) - assert capsys.readouterr() == ('', 'DEBUG: test\n') - logs.debug('test', Mock(no_colors=True, debug=False)) - assert capsys.readouterr() == ('', '') +@pytest.mark.usefixtures('no_colors') +@pytest.mark.parametrize('debug, stderr', [ + (True, 'DEBUG: test\n'), + (False, '')]) +def test_debug(capsys, settings, debug, stderr): + settings.debug = debug + logs.debug('test') + assert capsys.readouterr() == ('', stderr) diff --git a/tests/test_main.py b/tests/test_main.py index 0ea2cc2e..d33b539a 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -24,9 +24,9 @@ class TestGetCommand(object): monkeypatch.setattr('thefuck.shells.from_shell', lambda x: x) monkeypatch.setattr('thefuck.shells.to_shell', lambda x: x) - def test_get_command_calls(self, Popen): - assert main.get_command(Mock(env={}), - ['thefuck', 'apt-get', 'search', 'vim']) \ + def test_get_command_calls(self, Popen, settings): + settings.env = {} + assert main.get_command(['thefuck', 'apt-get', 'search', 'vim']) \ == Command('apt-get search vim', 'stdout', 'stderr') Popen.assert_called_once_with('apt-get search vim', shell=True, @@ -41,6 +41,6 @@ class TestGetCommand(object): (['thefuck', 'ls'], 'ls')]) def test_get_command_script(self, args, result): if result: - assert main.get_command(Mock(env={}), args).script == result + assert main.get_command(args).script == result else: - assert main.get_command(Mock(env={}), args) is None + assert main.get_command(args) is None diff --git a/tests/test_types.py b/tests/test_types.py index 1201d2c2..bc57e489 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -10,14 +10,6 @@ def test_rules_names_list(): assert Rule('bash') not in RulesNamesList(['lisp']) -def test_update_settings(): - settings = Settings({'key': 'val'}) - new_settings = settings.update(key='new-val', unset='unset-value') - assert new_settings.key == 'val' - assert new_settings.unset == 'unset-value' - assert settings.key == 'val' - - class TestSortedCorrectedCommandsSequence(object): def test_realises_generator_only_on_demand(self, settings): should_realise = False @@ -28,24 +20,23 @@ class TestSortedCorrectedCommandsSequence(object): assert should_realise yield CorrectedCommand('git checkout', priority=100) - commands = SortedCorrectedCommandsSequence(gen(), settings) + commands = SortedCorrectedCommandsSequence(gen()) assert commands[0] == CorrectedCommand('git commit') should_realise = True assert commands[1] == CorrectedCommand('git checkout', priority=100) assert commands[2] == CorrectedCommand('git branch', priority=200) - def test_remove_duplicates(self, settings): + def test_remove_duplicates(self): side_effect = lambda *_: None seq = SortedCorrectedCommandsSequence( iter([CorrectedCommand('ls', priority=100), CorrectedCommand('ls', priority=200), - CorrectedCommand('ls', side_effect, 300)]), - settings) + CorrectedCommand('ls', side_effect, 300)])) assert set(seq) == {CorrectedCommand('ls', priority=100), CorrectedCommand('ls', side_effect, 300)} - def test_with_blank(self, settings): - seq = SortedCorrectedCommandsSequence(iter([]), settings) + def test_with_blank(self): + seq = SortedCorrectedCommandsSequence(iter([])) assert list(seq) == [] diff --git a/tests/test_ui.py b/tests/test_ui.py index f997374c..aec2f099 100644 --- a/tests/test_ui.py +++ b/tests/test_ui.py @@ -56,66 +56,55 @@ def test_command_selector(): assert changes == [1, 2, 3, 1, 3] +@pytest.mark.usefixtures('no_colors') class TestSelectCommand(object): @pytest.fixture - def commands_with_side_effect(self, settings): + def commands_with_side_effect(self): return SortedCorrectedCommandsSequence( iter([CorrectedCommand('ls', lambda *_: None, 100), - CorrectedCommand('cd', lambda *_: None, 100)]), - settings) + CorrectedCommand('cd', lambda *_: None, 100)])) @pytest.fixture - def commands(self, settings): + def commands(self): return SortedCorrectedCommandsSequence( iter([CorrectedCommand('ls', None, 100), - CorrectedCommand('cd', None, 100)]), - settings) + CorrectedCommand('cd', None, 100)])) def test_without_commands(self, capsys): - assert ui.select_command([], Mock(debug=False, no_color=True)) is None + assert ui.select_command([]) is None assert capsys.readouterr() == ('', 'No fucks given\n') - def test_without_confirmation(self, capsys, commands): - assert ui.select_command(commands, - Mock(debug=False, no_color=True, - require_confirmation=False)) == commands[0] + def test_without_confirmation(self, capsys, commands, settings): + settings.require_confirmation = False + assert ui.select_command(commands) == commands[0] assert capsys.readouterr() == ('', 'ls\n') - def test_without_confirmation_with_side_effects(self, capsys, - commands_with_side_effect): - assert ui.select_command(commands_with_side_effect, - Mock(debug=False, no_color=True, - require_confirmation=False)) \ + def test_without_confirmation_with_side_effects( + self, capsys, commands_with_side_effect, settings): + settings.require_confirmation = False + assert ui.select_command(commands_with_side_effect) \ == commands_with_side_effect[0] assert capsys.readouterr() == ('', 'ls (+side effect)\n') def test_with_confirmation(self, capsys, patch_getch, commands): patch_getch(['\n']) - assert ui.select_command(commands, - Mock(debug=False, no_color=True, - require_confirmation=True)) == commands[0] + assert ui.select_command(commands) == commands[0] assert capsys.readouterr() == ('', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\n') def test_with_confirmation_abort(self, capsys, patch_getch, commands): patch_getch([KeyboardInterrupt]) - assert ui.select_command(commands, - Mock(debug=False, no_color=True, - require_confirmation=True)) is None + assert ui.select_command(commands) is None assert capsys.readouterr() == ('', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\nAborted\n') def test_with_confirmation_with_side_effct(self, capsys, patch_getch, commands_with_side_effect): patch_getch(['\n']) - assert ui.select_command(commands_with_side_effect, - Mock(debug=False, no_color=True, - require_confirmation=True))\ + assert ui.select_command(commands_with_side_effect)\ == commands_with_side_effect[0] assert capsys.readouterr() == ('', u'\x1b[1K\rls (+side effect) [enter/↑/↓/ctrl+c]\n') def test_with_confirmation_select_second(self, capsys, patch_getch, commands): patch_getch(['\x1b', '[', 'B', '\n']) - assert ui.select_command(commands, - Mock(debug=False, no_color=True, - require_confirmation=True)) == commands[1] + assert ui.select_command(commands) == commands[1] assert capsys.readouterr() == ( '', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\x1b[1K\rcd [enter/↑/↓/ctrl+c]\n') diff --git a/tests/test_utils.py b/tests/test_utils.py index b51ec394..3566b4f5 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,7 +1,7 @@ import pytest from mock import Mock import six -from thefuck.utils import wrap_settings, \ +from thefuck.utils import default_settings, \ memoize, get_closest, get_all_executables, replace_argument, \ get_all_matched_commands, is_app, for_app, cache from thefuck.types import Settings @@ -12,9 +12,9 @@ from tests.utils import Command ({'key': 'val'}, {}, {'key': 'val'}), ({'key': 'new-val'}, {'key': 'val'}, {'key': 'val'}), ({'key': 'new-val', 'unset': 'unset'}, {'key': 'val'}, {'key': 'val', 'unset': 'unset'})]) -def test_wrap_settings(override, old, new): +def test_default_settings(override, old, new): fn = lambda _, settings: settings - assert wrap_settings(override)(fn)(None, Settings(old)) == new + assert default_settings(override)(fn)(None, Settings(old)) == new def test_memoize(): diff --git a/thefuck/corrector.py b/thefuck/corrector.py index 8840749a..c1477a57 100644 --- a/thefuck/corrector.py +++ b/thefuck/corrector.py @@ -13,11 +13,11 @@ def load_rule(rule): rule_module = load_source(name, str(rule)) priority = getattr(rule_module, 'priority', DEFAULT_PRIORITY) return Rule(name, rule_module.match, - rule_module.get_new_command, - getattr(rule_module, 'enabled_by_default', True), - getattr(rule_module, 'side_effect', None), - settings.priority.get(name, priority), - getattr(rule_module, 'requires_output', True)) + rule_module.get_new_command, + getattr(rule_module, 'enabled_by_default', True), + getattr(rule_module, 'side_effect', None), + settings.priority.get(name, priority), + getattr(rule_module, 'requires_output', True)) def get_loaded_rules(rules): diff --git a/thefuck/rules/fix_file.py b/thefuck/rules/fix_file.py index 7af1fb31..895244d8 100644 --- a/thefuck/rules/fix_file.py +++ b/thefuck/rules/fix_file.py @@ -1,6 +1,6 @@ import re import os -from thefuck.utils import memoize, wrap_settings +from thefuck.utils import memoize, default_settings from thefuck import shells @@ -57,7 +57,7 @@ def match(command, settings): return _search(command.stderr) or _search(command.stdout) -@wrap_settings({'fixlinecmd': '{editor} {file} +{line}', +@default_settings({'fixlinecmd': '{editor} {file} +{line}', 'fixcolcmd': None}) def get_new_command(command, settings): m = _search(command.stderr) or _search(command.stdout) diff --git a/thefuck/utils.py b/thefuck/utils.py index 59cfcf7f..66d6b15b 100644 --- a/thefuck/utils.py +++ b/thefuck/utils.py @@ -60,18 +60,20 @@ def which(program): return None -def wrap_settings(params): +def default_settings(params): """Adds default values to settings if it not presented. Usage: - @wrap_settings({'apt': '/usr/bin/apt'}) + @default_settings({'apt': '/usr/bin/apt'}) def match(command, settings): print(settings.apt) """ def _wrap_settings(fn, command, settings): - return fn(command, Settings(settings, **params)) + for k, w in params.items(): + settings.setdefault(k, w) + return fn(command, settings) return decorator(_wrap_settings) From df4d2cc88d315a8caed168bc84b02ad763b9f858 Mon Sep 17 00:00:00 2001 From: nvbn Date: Mon, 7 Sep 2015 13:00:29 +0300 Subject: [PATCH 11/14] =?UTF-8?q?=E2=9A=A0=EF=B8=8F=20Remove=20`settings`?= =?UTF-8?q?=20param=20from=20rules=20`match`,=20`get=5Fnew=5Fcommand`=20an?= =?UTF-8?q?d=20`side=5Feffect`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 ++-- tests/rules/test_apt_get.py | 10 +-- tests/rules/test_apt_get_search.py | 6 +- tests/rules/test_brew_install.py | 6 +- tests/rules/test_brew_unknown_command.py | 10 +-- tests/rules/test_brew_upgrade.py | 4 +- tests/rules/test_cargo_no_command.py | 4 +- tests/rules/test_cd_mkdir.py | 6 +- tests/rules/test_cd_parent.py | 6 +- tests/rules/test_composer_not_command.py | 13 ++-- tests/rules/test_cp_omitting_directory.py | 6 +- tests/rules/test_dirty_untar.py | 6 +- tests/rules/test_dirty_unzip.py | 6 +- tests/rules/test_django_south_ghost.py | 12 ++-- tests/rules/test_django_south_merge.py | 12 ++-- tests/rules/test_docker_not_command.py | 6 +- tests/rules/test_dry.py | 4 +- tests/rules/test_fix_alt_space.py | 8 +-- tests/rules/test_fix_file.py | 14 ++--- tests/rules/test_git_add.py | 6 +- tests/rules/test_git_branch_delete.py | 8 +-- tests/rules/test_git_branch_list.py | 12 ++-- tests/rules/test_git_checkout.py | 6 +- tests/rules/test_git_diff_staged.py | 6 +- tests/rules/test_git_fix_stash.py | 4 +- tests/rules/test_git_not_command.py | 18 +++--- tests/rules/test_git_pull.py | 8 +-- tests/rules/test_git_pull_clone.py | 4 +- tests/rules/test_git_push.py | 8 +-- tests/rules/test_git_push_force.py | 6 +- tests/rules/test_git_push_pull.py | 6 +- tests/rules/test_git_stash.py | 6 +- tests/rules/test_go_run.py | 4 +- tests/rules/test_grep_recursive.py | 6 +- tests/rules/test_gulp_not_task.py | 6 +- tests/rules/test_has_exists_script.py | 13 ++-- tests/rules/test_heroku_not_command.py | 6 +- tests/rules/test_history.py | 6 +- tests/rules/test_java.py | 4 +- tests/rules/test_javac.py | 4 +- tests/rules/test_lein_not_task.py | 8 +-- tests/rules/test_ls_lah.py | 16 ++--- tests/rules/test_man.py | 6 +- tests/rules/test_man_no_space.py | 6 +- tests/rules/test_mercurial.py | 6 +- tests/rules/test_mkdir_p.py | 6 +- tests/rules/test_mvn_no_command.py | 6 +- .../rules/test_mvn_unknown_lifecycle_phase.py | 6 +- tests/rules/test_no_command.py | 14 ++--- tests/rules/test_no_such_file.py | 6 +- tests/rules/test_open.py | 4 +- tests/rules/test_pacman.py | 10 +-- tests/rules/test_pacman_not_found.py | 8 +-- tests/rules/test_pip_unknown_command.py | 9 ++- tests/rules/test_python_command.py | 6 +- tests/rules/test_python_execute.py | 4 +- tests/rules/test_quotation_marks.py | 4 +- tests/rules/test_rm_dir.py | 6 +- tests/rules/test_rm_root.py | 6 +- tests/rules/test_sed_unterminated_s.py | 20 +++--- tests/rules/test_sl_ls.py | 6 +- tests/rules/test_ssh_known_host.py | 18 +++--- tests/rules/test_sudo.py | 6 +- tests/rules/test_switch_lang.py | 6 +- tests/rules/test_systemctl.py | 18 +++--- tests/rules/test_tmux.py | 4 +- tests/rules/test_tsuru_login.py | 6 +- tests/rules/test_tsuru_not_command.py | 6 +- tests/rules/test_unknown_command.py | 6 +- tests/rules/test_vagrant_up.py | 6 +- tests/rules/test_whois.py | 6 +- tests/specific/test_git.py | 10 +-- tests/specific/test_sudo.py | 4 +- tests/test_corrector.py | 18 +++--- tests/test_utils.py | 63 ++++++++++++++++--- thefuck/corrector.py | 5 +- thefuck/main.py | 3 +- thefuck/rules/apt_get.py | 4 +- thefuck/rules/apt_get_search.py | 4 +- thefuck/rules/brew_install.py | 4 +- thefuck/rules/brew_unknown_command.py | 4 +- thefuck/rules/brew_upgrade.py | 4 +- thefuck/rules/cargo.py | 4 +- thefuck/rules/cargo_no_command.py | 4 +- thefuck/rules/cd_correction.py | 6 +- thefuck/rules/cd_mkdir.py | 4 +- thefuck/rules/cd_parent.py | 4 +- thefuck/rules/composer_not_command.py | 4 +- thefuck/rules/cp_omitting_directory.py | 4 +- thefuck/rules/cpp11.py | 4 +- thefuck/rules/dirty_untar.py | 6 +- thefuck/rules/dirty_unzip.py | 6 +- thefuck/rules/django_south_ghost.py | 4 +- thefuck/rules/django_south_merge.py | 4 +- thefuck/rules/docker_not_command.py | 4 +- thefuck/rules/dry.py | 4 +- thefuck/rules/fix_alt_space.py | 4 +- thefuck/rules/fix_file.py | 7 ++- thefuck/rules/git_add.py | 4 +- thefuck/rules/git_branch_delete.py | 4 +- thefuck/rules/git_branch_list.py | 4 +- thefuck/rules/git_checkout.py | 4 +- thefuck/rules/git_diff_staged.py | 4 +- thefuck/rules/git_fix_stash.py | 4 +- thefuck/rules/git_not_command.py | 4 +- thefuck/rules/git_pull.py | 4 +- thefuck/rules/git_pull_clone.py | 4 +- thefuck/rules/git_push.py | 4 +- thefuck/rules/git_push_force.py | 4 +- thefuck/rules/git_push_pull.py | 4 +- thefuck/rules/git_stash.py | 4 +- thefuck/rules/go_run.py | 4 +- thefuck/rules/grep_recursive.py | 4 +- thefuck/rules/gulp_not_task.py | 4 +- thefuck/rules/has_exists_script.py | 4 +- thefuck/rules/heroku_not_command.py | 4 +- thefuck/rules/history.py | 4 +- thefuck/rules/java.py | 4 +- thefuck/rules/javac.py | 4 +- thefuck/rules/lein_not_task.py | 4 +- thefuck/rules/ls_lah.py | 4 +- thefuck/rules/man.py | 4 +- thefuck/rules/man_no_space.py | 4 +- thefuck/rules/mercurial.py | 4 +- thefuck/rules/mkdir_p.py | 4 +- thefuck/rules/mvn_no_command.py | 4 +- thefuck/rules/mvn_unknown_lifecycle_phase.py | 4 +- thefuck/rules/no_command.py | 4 +- thefuck/rules/no_such_file.py | 4 +- thefuck/rules/open.py | 4 +- thefuck/rules/pacman.py | 4 +- thefuck/rules/pacman_not_found.py | 4 +- thefuck/rules/pip_unknown_command.py | 4 +- thefuck/rules/python_command.py | 4 +- thefuck/rules/python_execute.py | 4 +- thefuck/rules/quotation_marks.py | 4 +- thefuck/rules/rm_dir.py | 4 +- thefuck/rules/rm_root.py | 4 +- thefuck/rules/sed_unterminated_s.py | 4 +- thefuck/rules/sl_ls.py | 4 +- thefuck/rules/ssh_known_hosts.py | 6 +- thefuck/rules/sudo.py | 4 +- thefuck/rules/switch_lang.py | 4 +- thefuck/rules/systemctl.py | 4 +- thefuck/rules/test.py.py | 4 +- thefuck/rules/tmux.py | 4 +- thefuck/rules/tsuru_login.py | 4 +- thefuck/rules/tsuru_not_command.py | 4 +- thefuck/rules/unknown_command.py | 4 +- thefuck/rules/vagrant_up.py | 4 +- thefuck/rules/whois.py | 4 +- thefuck/specific/git.py | 4 +- thefuck/specific/sudo.py | 7 +-- thefuck/utils.py | 36 ++++++++--- 154 files changed, 535 insertions(+), 465 deletions(-) diff --git a/README.md b/README.md index 593ca5f6..f24018eb 100644 --- a/README.md +++ b/README.md @@ -226,20 +226,21 @@ For adding your own rule you should create `your-rule-name.py` in `~/.thefuck/rules`. The rule should contain two functions: ```python -match(command: Command, settings: Settings) -> bool -get_new_command(command: Command, settings: Settings) -> str | list[str] +match(command: Command) -> bool +get_new_command(command: Command) -> str | list[str] ``` Also the rule can contain an optional function ```python -side_effect(old_command: Command, fixed_command: str, settings: Settings) -> None +side_effect(old_command: Command, fixed_command: str) -> None ``` and optional `enabled_by_default`, `requires_output` and `priority` variables. `Command` has three attributes: `script`, `stdout` and `stderr`. -`Settings` is a special object filled with `~/.thefuck/settings.py` and values from env ([see more below](#settings)). +*Rules api changed in 3.0:* For accessing settings in rule you need to import it with `from thefuck.conf import settings`. +`settings` is a special object filled with `~/.thefuck/settings.py` and values from env ([see more below](#settings)). Simple example of the rule for running script with `sudo`: @@ -264,7 +265,8 @@ requires_output = True ``` [More examples of rules](https://github.com/nvbn/thefuck/tree/master/thefuck/rules), -[utility functions for rules](https://github.com/nvbn/thefuck/tree/master/thefuck/utils.py). +[utility functions for rules](https://github.com/nvbn/thefuck/tree/master/thefuck/utils.py), +[app/os-specific helpers](https://github.com/nvbn/thefuck/tree/master/thefuck/specific/). ## Settings diff --git a/tests/rules/test_apt_get.py b/tests/rules/test_apt_get.py index ca2d95d9..f0362795 100644 --- a/tests/rules/test_apt_get.py +++ b/tests/rules/test_apt_get.py @@ -11,7 +11,7 @@ from tests.utils import Command @pytest.mark.parametrize('command', [ Command(script='vim', stderr='vim: command not found')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command, return_value', [ @@ -24,7 +24,7 @@ def test_match(command): 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) - assert match(command, None) + assert match(command) assert cmdnf_mock.CommandNotFound.called assert get_packages.called @@ -32,7 +32,7 @@ def test_match_mocked(cmdnf_mock, command, return_value): @pytest.mark.parametrize('command', [ Command(script='vim', stderr=''), Command()]) def test_not_match(command): - assert not match(command, None) + assert not match(command) # python-commandnotfound is available in ubuntu 14.04+ @@ -44,7 +44,7 @@ def test_not_match(command): (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, None) == new_command + assert get_new_command(command) == new_command @pytest.mark.parametrize('command, new_command, return_value', [ @@ -63,4 +63,4 @@ def test_get_new_command(command, new_command): 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) - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_apt_get_search.py b/tests/rules/test_apt_get_search.py index 82f3d041..f9074485 100644 --- a/tests/rules/test_apt_get_search.py +++ b/tests/rules/test_apt_get_search.py @@ -4,7 +4,7 @@ from tests.utils import Command def test_match(): - assert match(Command('apt-get search foo'), None) + assert match(Command('apt-get search foo')) @pytest.mark.parametrize('command', [ @@ -18,8 +18,8 @@ def test_match(): Command('apt-get update') ]) def test_not_match(command): - assert not match(command, None) + assert not match(command) def test_get_new_command(): - assert get_new_command(Command('apt-get search foo'), None) == 'apt-cache search foo' + assert get_new_command(Command('apt-get search foo')) == 'apt-cache search foo' diff --git a/tests/rules/test_brew_install.py b/tests/rules/test_brew_install.py index e8eae865..de6ad21d 100644 --- a/tests/rules/test_brew_install.py +++ b/tests/rules/test_brew_install.py @@ -28,9 +28,9 @@ def _is_not_okay_to_test(): def test_match(brew_no_available_formula, brew_already_installed, brew_install_no_argument): assert match(Command('brew install elsticsearch', - stderr=brew_no_available_formula), None) + stderr=brew_no_available_formula)) assert not match(Command('brew install git', - stderr=brew_already_installed), None) + stderr=brew_already_installed)) assert not match(Command('brew install', stderr=brew_install_no_argument), None) @@ -39,7 +39,7 @@ def test_match(brew_no_available_formula, brew_already_installed, reason='No need to run if there\'s no formula') def test_get_new_command(brew_no_available_formula): assert get_new_command(Command('brew install elsticsearch', - stderr=brew_no_available_formula), None)\ + stderr=brew_no_available_formula))\ == 'brew install elasticsearch' assert get_new_command(Command('brew install aa', diff --git a/tests/rules/test_brew_unknown_command.py b/tests/rules/test_brew_unknown_command.py index 42e6c7f8..45e02f67 100644 --- a/tests/rules/test_brew_unknown_command.py +++ b/tests/rules/test_brew_unknown_command.py @@ -15,15 +15,15 @@ def brew_unknown_cmd2(): def test_match(brew_unknown_cmd): - assert match(Command('brew inst', stderr=brew_unknown_cmd), None) + assert match(Command('brew inst', stderr=brew_unknown_cmd)) for command in _brew_commands(): - assert not match(Command('brew ' + command), None) + assert not match(Command('brew ' + command)) def test_get_new_command(brew_unknown_cmd, brew_unknown_cmd2): - assert get_new_command(Command('brew inst', stderr=brew_unknown_cmd), - None) == ['brew list', 'brew install', 'brew uninstall'] + assert get_new_command(Command('brew inst', stderr=brew_unknown_cmd)) \ + == ['brew list', 'brew install', 'brew uninstall'] - cmds = get_new_command(Command('brew instaa', stderr=brew_unknown_cmd2), None) + cmds = get_new_command(Command('brew instaa', stderr=brew_unknown_cmd2)) assert 'brew install' in cmds assert 'brew uninstall' in cmds diff --git a/tests/rules/test_brew_upgrade.py b/tests/rules/test_brew_upgrade.py index 941bb2c3..e1afc8ee 100644 --- a/tests/rules/test_brew_upgrade.py +++ b/tests/rules/test_brew_upgrade.py @@ -6,10 +6,10 @@ from tests.utils import Command @pytest.mark.parametrize('command', [ Command(script='brew upgrade')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command, new_command', [ (Command('brew upgrade'), 'brew upgrade --all')]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_cargo_no_command.py b/tests/rules/test_cargo_no_command.py index df875858..2c70b6b1 100644 --- a/tests/rules/test_cargo_no_command.py +++ b/tests/rules/test_cargo_no_command.py @@ -12,10 +12,10 @@ no_such_subcommand = """No such subcommand @pytest.mark.parametrize('command', [ Command(script='cargo buid', stderr=no_such_subcommand)]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command, new_command', [ (Command('cargo buid', stderr=no_such_subcommand), 'cargo build')]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_cd_mkdir.py b/tests/rules/test_cd_mkdir.py index ae5449ff..759ada36 100644 --- a/tests/rules/test_cd_mkdir.py +++ b/tests/rules/test_cd_mkdir.py @@ -9,17 +9,17 @@ from tests.utils import Command stderr='cd: foo: No such file or directory'), Command(script='cd foo/bar/baz', stderr='cd: can\'t cd to foo/bar/baz')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ Command(script='cd foo', stderr=''), Command()]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, new_command', [ (Command('cd foo'), 'mkdir -p foo && cd foo'), (Command('cd foo/bar/baz'), 'mkdir -p foo/bar/baz && cd foo/bar/baz')]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_cd_parent.py b/tests/rules/test_cd_parent.py index 61d1ab1d..812f5885 100644 --- a/tests/rules/test_cd_parent.py +++ b/tests/rules/test_cd_parent.py @@ -3,10 +3,10 @@ from tests.utils import Command def test_match(): - assert match(Command('cd..', stderr='cd..: command not found'), None) - assert not match(Command(), None) + assert match(Command('cd..', stderr='cd..: command not found')) + assert not match(Command()) def test_get_new_command(): assert get_new_command( - Command('cd..'), None) == 'cd ..' + Command('cd..')) == 'cd ..' diff --git a/tests/rules/test_composer_not_command.py b/tests/rules/test_composer_not_command.py index 9ffdd004..c4e4a5a4 100644 --- a/tests/rules/test_composer_not_command.py +++ b/tests/rules/test_composer_not_command.py @@ -41,17 +41,16 @@ def composer_not_command_one_of_this(): def test_match(composer_not_command, composer_not_command_one_of_this): assert match(Command('composer udpate', - stderr=composer_not_command), None) + stderr=composer_not_command)) assert match(Command('composer pdate', - stderr=composer_not_command_one_of_this), None) - assert not match(Command('ls update', stderr=composer_not_command), - None) + stderr=composer_not_command_one_of_this)) + assert not match(Command('ls update', stderr=composer_not_command)) def test_get_new_command(composer_not_command, composer_not_command_one_of_this): assert get_new_command(Command('composer udpate', - stderr=composer_not_command), None) \ + stderr=composer_not_command)) \ == 'composer update' assert get_new_command( - Command('composer pdate', stderr=composer_not_command_one_of_this), - None) == 'composer selfupdate' + Command('composer pdate', stderr=composer_not_command_one_of_this)) \ + == 'composer selfupdate' diff --git a/tests/rules/test_cp_omitting_directory.py b/tests/rules/test_cp_omitting_directory.py index cdd5d16d..364e9276 100644 --- a/tests/rules/test_cp_omitting_directory.py +++ b/tests/rules/test_cp_omitting_directory.py @@ -7,7 +7,7 @@ from tests.utils import Command ('cp dir', 'cp: dor: is a directory'), ('cp dir', "cp: omitting directory 'dir'")]) def test_match(script, stderr): - assert match(Command(script, stderr=stderr), None) + assert match(Command(script, stderr=stderr)) @pytest.mark.parametrize('script, stderr', [ @@ -15,8 +15,8 @@ def test_match(script, stderr): ('some dir', "cp: omitting directory 'dir'"), ('cp dir', '')]) def test_not_match(script, stderr): - assert not match(Command(script, stderr=stderr), None) + assert not match(Command(script, stderr=stderr)) def test_get_new_command(): - assert get_new_command(Command(script='cp dir'), None) == 'cp -a dir' + assert get_new_command(Command(script='cp dir')) == 'cp -a dir' diff --git a/tests/rules/test_dirty_untar.py b/tests/rules/test_dirty_untar.py index 9b2e4aee..d5f5e21a 100644 --- a/tests/rules/test_dirty_untar.py +++ b/tests/rules/test_dirty_untar.py @@ -45,14 +45,14 @@ parametrize_script = pytest.mark.parametrize('script, fixed', [ @parametrize_script def test_match(tar_error, filename, script, fixed): tar_error(filename) - assert match(Command(script=script.format(filename)), None) + assert match(Command(script=script.format(filename))) @parametrize_filename @parametrize_script def test_side_effect(tar_error, filename, script, fixed): tar_error(filename) - side_effect(Command(script=script.format(filename)), None, None) + side_effect(Command(script=script.format(filename)), None) assert(os.listdir('.') == [filename]) @@ -60,4 +60,4 @@ def test_side_effect(tar_error, filename, script, fixed): @parametrize_script def test_get_new_command(tar_error, filename, script, fixed): tar_error(filename) - assert get_new_command(Command(script=script.format(filename)), None) == fixed.format(filename) + assert get_new_command(Command(script=script.format(filename))) == fixed.format(filename) diff --git a/tests/rules/test_dirty_unzip.py b/tests/rules/test_dirty_unzip.py index 74dabf3f..58375a12 100644 --- a/tests/rules/test_dirty_unzip.py +++ b/tests/rules/test_dirty_unzip.py @@ -27,14 +27,14 @@ def zip_error(tmpdir): 'unzip foo', 'unzip foo.zip']) def test_match(zip_error, script): - assert match(Command(script=script), None) + assert match(Command(script=script)) @pytest.mark.parametrize('script', [ 'unzip foo', 'unzip foo.zip']) def test_side_effect(zip_error, script): - side_effect(Command(script=script), None, None) + side_effect(Command(script=script), None) assert(os.listdir('.') == ['foo.zip']) @@ -42,4 +42,4 @@ def test_side_effect(zip_error, script): ('unzip foo', 'unzip foo -d foo'), ('unzip foo.zip', 'unzip foo.zip -d foo')]) def test_get_new_command(zip_error, script, fixed): - assert get_new_command(Command(script=script), None) == fixed + assert get_new_command(Command(script=script)) == fixed diff --git a/tests/rules/test_django_south_ghost.py b/tests/rules/test_django_south_ghost.py index 70fc8903..83656467 100644 --- a/tests/rules/test_django_south_ghost.py +++ b/tests/rules/test_django_south_ghost.py @@ -41,13 +41,13 @@ south.exceptions.GhostMigrations: def test_match(stderr): - assert match(Command('./manage.py migrate', stderr=stderr), None) - assert match(Command('python manage.py migrate', stderr=stderr), None) - assert not match(Command('./manage.py migrate'), None) - assert not match(Command('app migrate', stderr=stderr), None) - assert not match(Command('./manage.py test', stderr=stderr), None) + assert match(Command('./manage.py migrate', stderr=stderr)) + assert match(Command('python manage.py migrate', stderr=stderr)) + assert not match(Command('./manage.py migrate')) + assert not match(Command('app migrate', stderr=stderr)) + assert not match(Command('./manage.py test', stderr=stderr)) def test_get_new_command(): - assert get_new_command(Command('./manage.py migrate auth'), None)\ + assert get_new_command(Command('./manage.py migrate auth'))\ == './manage.py migrate auth --delete-ghost-migrations' diff --git a/tests/rules/test_django_south_merge.py b/tests/rules/test_django_south_merge.py index c0426122..13f7dbaf 100644 --- a/tests/rules/test_django_south_merge.py +++ b/tests/rules/test_django_south_merge.py @@ -31,13 +31,13 @@ The following options are available: def test_match(stderr): - assert match(Command('./manage.py migrate', stderr=stderr), None) - assert match(Command('python manage.py migrate', stderr=stderr), None) - assert not match(Command('./manage.py migrate'), None) - assert not match(Command('app migrate', stderr=stderr), None) - assert not match(Command('./manage.py test', stderr=stderr), None) + assert match(Command('./manage.py migrate', stderr=stderr)) + assert match(Command('python manage.py migrate', stderr=stderr)) + assert not match(Command('./manage.py migrate')) + assert not match(Command('app migrate', stderr=stderr)) + assert not match(Command('./manage.py test', stderr=stderr)) def test_get_new_command(): - assert get_new_command(Command('./manage.py migrate auth'), None) \ + assert get_new_command(Command('./manage.py migrate auth')) \ == './manage.py migrate auth --merge' diff --git a/tests/rules/test_docker_not_command.py b/tests/rules/test_docker_not_command.py index 5e9a7811..172f1bf6 100644 --- a/tests/rules/test_docker_not_command.py +++ b/tests/rules/test_docker_not_command.py @@ -110,14 +110,14 @@ def stderr(cmd): def test_match(): - assert match(Command('docker pes', stderr=stderr('pes')), None) + assert match(Command('docker pes', stderr=stderr('pes'))) @pytest.mark.parametrize('script, stderr', [ ('docker ps', ''), ('cat pes', stderr('pes'))]) def test_not_match(script, stderr): - assert not match(Command(script, stderr=stderr), None) + assert not match(Command(script, stderr=stderr)) @pytest.mark.usefixtures('docker_help') @@ -126,4 +126,4 @@ def test_not_match(script, stderr): ('tags', ['tag', 'stats', 'images'])]) def test_get_new_command(wrong, fixed): command = Command('docker {}'.format(wrong), stderr=stderr(wrong)) - assert get_new_command(command, None) == ['docker {}'.format(x) for x in fixed] + assert get_new_command(command) == ['docker {}'.format(x) for x in fixed] diff --git a/tests/rules/test_dry.py b/tests/rules/test_dry.py index 757866df..ce46865d 100644 --- a/tests/rules/test_dry.py +++ b/tests/rules/test_dry.py @@ -7,11 +7,11 @@ from tests.utils import Command Command(script='cd cd foo'), Command(script='git git push origin/master')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command, new_command', [ (Command('cd cd foo'), 'cd foo'), (Command('git git push origin/master'), 'git push origin/master')]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_fix_alt_space.py b/tests/rules/test_fix_alt_space.py index c27d3d2b..c5b03c50 100644 --- a/tests/rules/test_fix_alt_space.py +++ b/tests/rules/test_fix_alt_space.py @@ -11,12 +11,12 @@ def test_match(): """ assert match(Command(u'ps -ef | grep foo', - stderr=u'-bash:  grep: command not found'), None) - assert not match(Command('ps -ef | grep foo'), None) - assert not match(Command(), None) + stderr=u'-bash:  grep: command not found')) + assert not match(Command('ps -ef | grep foo')) + assert not match(Command()) def test_get_new_command(): """ Replace the Alt+Space character by a simple space """ - assert get_new_command(Command(u'ps -ef | grep foo'), None)\ + assert get_new_command(Command(u'ps -ef | grep foo'))\ == 'ps -ef | grep foo' diff --git a/tests/rules/test_fix_file.py b/tests/rules/test_fix_file.py index 9b84a1a4..24d7bc17 100644 --- a/tests/rules/test_fix_file.py +++ b/tests/rules/test_fix_file.py @@ -184,7 +184,7 @@ E NameError: name 'mocker' is not defined def test_match(mocker, monkeypatch, test): mocker.patch('os.path.isfile', return_value=True) monkeypatch.setenv('EDITOR', 'dummy_editor') - assert match(Command(stdout=test[4], stderr=test[5]), None) + assert match(Command(stdout=test[4], stderr=test[5])) @pytest.mark.parametrize('test', tests) @@ -194,7 +194,7 @@ def test_no_editor(mocker, monkeypatch, test): if 'EDITOR' in os.environ: monkeypatch.delenv('EDITOR') - assert not match(Command(stdout=test[4], stderr=test[5]), None) + assert not match(Command(stdout=test[4], stderr=test[5])) @pytest.mark.parametrize('test', tests) @@ -203,7 +203,7 @@ def test_not_file(mocker, monkeypatch, test): mocker.patch('os.path.isfile', return_value=False) monkeypatch.setenv('EDITOR', 'dummy_editor') - assert not match(Command(stdout=test[4], stderr=test[5]), None) + assert not match(Command(stdout=test[4], stderr=test[5])) @pytest.mark.parametrize('test', tests) @@ -219,16 +219,16 @@ def test_get_new_command(mocker, monkeypatch, test): @pytest.mark.parametrize('test', tests) @pytest.mark.usefixtures('no_memoize') -def test_get_new_command_with_settings(mocker, monkeypatch, test): +def test_get_new_command_with_settings(mocker, monkeypatch, test, settings): mocker.patch('os.path.isfile', return_value=True) monkeypatch.setenv('EDITOR', 'dummy_editor') cmd = Command(script=test[0], stdout=test[4], stderr=test[5]) - settings = Settings({'fixcolcmd': '{editor} {file} +{line}:{col}'}) + settings.fixcolcmd = '{editor} {file} +{line}:{col}' if test[3]: - assert (get_new_command(cmd, settings) == + assert (get_new_command(cmd) == 'dummy_editor {} +{}:{} && {}'.format(test[1], test[2], test[3], test[0])) else: - assert (get_new_command(cmd, settings) == + assert (get_new_command(cmd) == 'dummy_editor {} +{} && {}'.format(test[1], test[2], test[0])) diff --git a/tests/rules/test_git_add.py b/tests/rules/test_git_add.py index 8bad9bb6..c5b7c251 100644 --- a/tests/rules/test_git_add.py +++ b/tests/rules/test_git_add.py @@ -18,7 +18,7 @@ def did_not_match(target, did_you_forget=True): Command(script='git commit unknown', stderr=did_not_match('unknown'))]) # Older versions of Git def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -27,7 +27,7 @@ def test_match(command): Command(script='git commit unknown', # Newer versions of Git stderr=did_not_match('unknown', False))]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, new_command', [ @@ -36,4 +36,4 @@ def test_not_match(command): (Command('git commit unknown', stderr=did_not_match('unknown')), # Old Git 'git add -- unknown && git commit unknown')]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_git_branch_delete.py b/tests/rules/test_git_branch_delete.py index 44a8f449..bfcc5d17 100644 --- a/tests/rules/test_git_branch_delete.py +++ b/tests/rules/test_git_branch_delete.py @@ -12,11 +12,11 @@ If you are sure you want to delete it, run 'git branch -D branch'. def test_match(stderr): - assert match(Command('git branch -d branch', stderr=stderr), None) - assert not match(Command('git branch -d branch'), None) - assert not match(Command('ls', stderr=stderr), None) + assert match(Command('git branch -d branch', stderr=stderr)) + assert not match(Command('git branch -d branch')) + assert not match(Command('ls', stderr=stderr)) def test_get_new_command(stderr): - assert get_new_command(Command('git branch -d branch', stderr=stderr), None)\ + assert get_new_command(Command('git branch -d branch', stderr=stderr))\ == "git branch -D branch" diff --git a/tests/rules/test_git_branch_list.py b/tests/rules/test_git_branch_list.py index 4d77e1a4..79302c58 100644 --- a/tests/rules/test_git_branch_list.py +++ b/tests/rules/test_git_branch_list.py @@ -4,16 +4,16 @@ from tests.utils import Command def test_match(): - assert match(Command('git branch list'), None) + assert match(Command('git branch list')) def test_not_match(): - assert not match(Command(), None) - assert not match(Command('git commit'), None) - assert not match(Command('git branch'), None) - assert not match(Command('git stash list'), None) + assert not match(Command()) + assert not match(Command('git commit')) + assert not match(Command('git branch')) + assert not match(Command('git stash list')) def test_get_new_command(): - assert (get_new_command(Command('git branch list'), None) == + assert (get_new_command(Command('git branch list')) == shells.and_('git branch --delete list', 'git branch')) diff --git a/tests/rules/test_git_checkout.py b/tests/rules/test_git_checkout.py index e460fff1..e4381cdb 100644 --- a/tests/rules/test_git_checkout.py +++ b/tests/rules/test_git_checkout.py @@ -21,7 +21,7 @@ def get_branches(mocker): Command(script='git checkout unknown', stderr=did_not_match('unknown')), Command(script='git commit unknown', stderr=did_not_match('unknown'))]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -30,7 +30,7 @@ def test_match(command): Command(script='git checkout known', stderr=('')), Command(script='git commit known', stderr=(''))]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('branches, command, new_command', [ @@ -50,4 +50,4 @@ def test_not_match(command): 'git commit test-random-branch-123')]) def test_get_new_command(branches, command, new_command, get_branches): get_branches.return_value = branches - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_git_diff_staged.py b/tests/rules/test_git_diff_staged.py index 364cb267..9ff673bd 100644 --- a/tests/rules/test_git_diff_staged.py +++ b/tests/rules/test_git_diff_staged.py @@ -7,7 +7,7 @@ from tests.utils import Command Command(script='git diff foo'), Command(script='git diff')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -16,11 +16,11 @@ def test_match(command): Command(script='git branch'), Command(script='git log')]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, new_command', [ (Command('git diff'), 'git diff --staged'), (Command('git diff foo'), 'git diff --staged foo')]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_git_fix_stash.py b/tests/rules/test_git_fix_stash.py index 6eac71d9..206d54fc 100644 --- a/tests/rules/test_git_fix_stash.py +++ b/tests/rules/test_git_fix_stash.py @@ -20,7 +20,7 @@ usage: git stash list [] 'git stash Some message', 'git stash saev Some message']) def test_match(wrong): - assert match(Command(wrong, stderr=git_stash_err), None) + assert match(Command(wrong, stderr=git_stash_err)) @pytest.mark.parametrize('wrong,fixed', [ @@ -28,4 +28,4 @@ def test_match(wrong): ('git stash Some message', 'git stash save Some message'), ('git stash saev Some message', 'git stash save Some message')]) def test_get_new_command(wrong, fixed): - assert get_new_command(Command(wrong, stderr=git_stash_err), None) == fixed + assert get_new_command(Command(wrong, stderr=git_stash_err)) == fixed diff --git a/tests/rules/test_git_not_command.py b/tests/rules/test_git_not_command.py index 80aa5c92..6257767e 100644 --- a/tests/rules/test_git_not_command.py +++ b/tests/rules/test_git_not_command.py @@ -41,17 +41,17 @@ def git_command(): def test_match(git_not_command, git_command, git_not_command_one_of_this): - assert match(Command('git brnch', stderr=git_not_command), None) - assert match(Command('git st', stderr=git_not_command_one_of_this), None) - assert not match(Command('ls brnch', stderr=git_not_command), None) - assert not match(Command('git branch', stderr=git_command), None) + assert match(Command('git brnch', stderr=git_not_command)) + assert match(Command('git st', stderr=git_not_command_one_of_this)) + assert not match(Command('ls brnch', stderr=git_not_command)) + assert not match(Command('git branch', stderr=git_command)) def test_get_new_command(git_not_command, git_not_command_one_of_this, git_not_command_closest): - assert get_new_command(Command('git brnch', stderr=git_not_command), None) \ + assert get_new_command(Command('git brnch', stderr=git_not_command)) \ == ['git branch'] - assert get_new_command(Command('git st', stderr=git_not_command_one_of_this), - None) == ['git stats', 'git stash', 'git stage'] - assert get_new_command(Command('git tags', stderr=git_not_command_closest), - None) == ['git tag', 'git stage'] + assert get_new_command(Command('git st', stderr=git_not_command_one_of_this)) \ + == ['git stats', 'git stash', 'git stage'] + assert get_new_command(Command('git tags', stderr=git_not_command_closest)) \ + == ['git tag', 'git stage'] diff --git a/tests/rules/test_git_pull.py b/tests/rules/test_git_pull.py index 87725f5a..11c50171 100644 --- a/tests/rules/test_git_pull.py +++ b/tests/rules/test_git_pull.py @@ -19,11 +19,11 @@ If you wish to set tracking information for this branch you can do so with: def test_match(stderr): - assert match(Command('git pull', stderr=stderr), None) - assert not match(Command('git pull'), None) - assert not match(Command('ls', stderr=stderr), None) + assert match(Command('git pull', stderr=stderr)) + assert not match(Command('git pull')) + assert not match(Command('ls', stderr=stderr)) def test_get_new_command(stderr): - assert get_new_command(Command('git pull', stderr=stderr), None) \ + assert get_new_command(Command('git pull', stderr=stderr)) \ == "git branch --set-upstream-to=origin/master master && git pull" diff --git a/tests/rules/test_git_pull_clone.py b/tests/rules/test_git_pull_clone.py index 07249fae..9e7875bb 100644 --- a/tests/rules/test_git_pull_clone.py +++ b/tests/rules/test_git_pull_clone.py @@ -12,10 +12,10 @@ Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set). @pytest.mark.parametrize('command', [ Command(script='git pull git@github.com:mcarton/thefuck.git', stderr=git_err)]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command, output', [ (Command(script='git pull git@github.com:mcarton/thefuck.git', stderr=git_err), 'git clone git@github.com:mcarton/thefuck.git')]) def test_get_new_command(command, output): - assert get_new_command(command, None) == output + assert get_new_command(command) == output diff --git a/tests/rules/test_git_push.py b/tests/rules/test_git_push.py index 2625c0d9..65b210a1 100644 --- a/tests/rules/test_git_push.py +++ b/tests/rules/test_git_push.py @@ -14,11 +14,11 @@ To push the current branch and set the remote as upstream, use def test_match(stderr): - assert match(Command('git push master', stderr=stderr), None) - assert not match(Command('git push master'), None) - assert not match(Command('ls', stderr=stderr), None) + assert match(Command('git push master', stderr=stderr)) + assert not match(Command('git push master')) + assert not match(Command('ls', stderr=stderr)) def test_get_new_command(stderr): - assert get_new_command(Command('git push', stderr=stderr), None)\ + assert get_new_command(Command('git push', stderr=stderr))\ == "git push --set-upstream origin master" diff --git a/tests/rules/test_git_push_force.py b/tests/rules/test_git_push_force.py index c55e77d0..b7420ffc 100644 --- a/tests/rules/test_git_push_force.py +++ b/tests/rules/test_git_push_force.py @@ -30,7 +30,7 @@ To /tmp/bar Command(script='git push nvbn', stderr=git_err), Command(script='git push nvbn master', stderr=git_err)]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -41,7 +41,7 @@ def test_match(command): Command(script='git push nvbn', stderr=git_ok), Command(script='git push nvbn master', stderr=git_uptodate)]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, output', [ @@ -49,4 +49,4 @@ def test_not_match(command): (Command(script='git push nvbn', stderr=git_err), 'git push --force nvbn'), (Command(script='git push nvbn master', stderr=git_err), 'git push --force nvbn master')]) def test_get_new_command(command, output): - assert get_new_command(command, None) == output + assert get_new_command(command) == output diff --git a/tests/rules/test_git_push_pull.py b/tests/rules/test_git_push_pull.py index dedcfc79..0e950df4 100644 --- a/tests/rules/test_git_push_pull.py +++ b/tests/rules/test_git_push_pull.py @@ -30,7 +30,7 @@ To /tmp/bar Command(script='git push nvbn', stderr=git_err), Command(script='git push nvbn master', stderr=git_err)]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -41,7 +41,7 @@ def test_match(command): Command(script='git push nvbn', stderr=git_ok), Command(script='git push nvbn master', stderr=git_uptodate)]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, output', [ @@ -51,4 +51,4 @@ def test_not_match(command): (Command(script='git push nvbn master', stderr=git_err), 'git pull nvbn master && git push nvbn master')]) def test_get_new_command(command, output): - assert get_new_command(command, None) == output + assert get_new_command(command) == output diff --git a/tests/rules/test_git_stash.py b/tests/rules/test_git_stash.py index 2aa06277..da9aded7 100644 --- a/tests/rules/test_git_stash.py +++ b/tests/rules/test_git_stash.py @@ -18,14 +18,14 @@ rebase_error = ( Command(script='git cherry-pick a1b2c3d', stderr=cherry_pick_error), Command(script='git rebase -i HEAD~7', stderr=rebase_error)]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ Command(script='git cherry-pick a1b2c3d', stderr=('')), Command(script='git rebase -i HEAD~7', stderr=(''))]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, new_command', [ @@ -34,4 +34,4 @@ def test_not_match(command): (Command('git rebase -i HEAD~7', stderr=rebase_error), 'git stash && git rebase -i HEAD~7')]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_go_run.py b/tests/rules/test_go_run.py index 9b5baf6f..f0ae90cc 100644 --- a/tests/rules/test_go_run.py +++ b/tests/rules/test_go_run.py @@ -7,11 +7,11 @@ from tests.utils import Command Command(script='go run foo'), Command(script='go run bar')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command, new_command', [ (Command('go run foo'), 'go run foo.go'), (Command('go run bar'), 'go run bar.go')]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_grep_recursive.py b/tests/rules/test_grep_recursive.py index 0e3dae1d..6c459496 100644 --- a/tests/rules/test_grep_recursive.py +++ b/tests/rules/test_grep_recursive.py @@ -3,10 +3,10 @@ from tests.utils import Command def test_match(): - assert match(Command('grep blah .', stderr='grep: .: Is a directory'), None) - assert not match(Command(), None) + assert match(Command('grep blah .', stderr='grep: .: Is a directory')) + assert not match(Command()) def test_get_new_command(): assert get_new_command( - Command('grep blah .'), None) == 'grep -r blah .' + Command('grep blah .')) == 'grep -r blah .' diff --git a/tests/rules/test_gulp_not_task.py b/tests/rules/test_gulp_not_task.py index 980e0f73..cf76750f 100644 --- a/tests/rules/test_gulp_not_task.py +++ b/tests/rules/test_gulp_not_task.py @@ -11,18 +11,18 @@ def stdout(task): def test_match(): - assert match(Command('gulp srve', stdout('srve')), None) + assert match(Command('gulp srve', stdout('srve'))) @pytest.mark.parametrize('script, stdout', [ ('gulp serve', ''), ('cat srve', stdout('srve'))]) def test_not_march(script, stdout): - assert not match(Command(script, stdout), None) + assert not match(Command(script, stdout)) def test_get_new_command(mocker): mocker.patch('thefuck.rules.gulp_not_task.get_gulp_tasks', return_value=[ 'serve', 'build', 'default']) command = Command('gulp srve', stdout('srve')) - assert get_new_command(command, None) == ['gulp serve', 'gulp default'] + assert get_new_command(command) == ['gulp serve', 'gulp default'] diff --git a/tests/rules/test_has_exists_script.py b/tests/rules/test_has_exists_script.py index e00c4bdd..6a94760c 100644 --- a/tests/rules/test_has_exists_script.py +++ b/tests/rules/test_has_exists_script.py @@ -4,17 +4,14 @@ from thefuck.rules.has_exists_script import match, get_new_command def test_match(): with patch('os.path.exists', return_value=True): - assert match(Mock(script='main', stderr='main: command not found'), - None) + assert match(Mock(script='main', stderr='main: command not found')) assert match(Mock(script='main --help', - stderr='main: command not found'), - None) - assert not match(Mock(script='main', stderr=''), None) + stderr='main: command not found')) + assert not match(Mock(script='main', stderr='')) with patch('os.path.exists', return_value=False): - assert not match(Mock(script='main', stderr='main: command not found'), - None) + assert not match(Mock(script='main', stderr='main: command not found')) def test_get_new_command(): - assert get_new_command(Mock(script='main --help'), None) == './main --help' + assert get_new_command(Mock(script='main --help')) == './main --help' diff --git a/tests/rules/test_heroku_not_command.py b/tests/rules/test_heroku_not_command.py index c1f5a761..b322fec4 100644 --- a/tests/rules/test_heroku_not_command.py +++ b/tests/rules/test_heroku_not_command.py @@ -16,14 +16,14 @@ no_suggest_stderr = ''' ! `aaaaa` is not a heroku command. @pytest.mark.parametrize('cmd', ['log', 'pge']) def test_match(cmd): assert match( - Command('heroku {}'.format(cmd), stderr=suggest_stderr(cmd)), None) + Command('heroku {}'.format(cmd), stderr=suggest_stderr(cmd))) @pytest.mark.parametrize('script, stderr', [ ('cat log', suggest_stderr('log')), ('heroku aaa', no_suggest_stderr)]) def test_not_match(script, stderr): - assert not match(Command(script, stderr=stderr), None) + assert not match(Command(script, stderr=stderr)) @pytest.mark.parametrize('cmd, result', [ @@ -31,4 +31,4 @@ def test_not_match(script, stderr): ('pge', ['heroku pg', 'heroku logs'])]) def test_get_new_command(cmd, result): command = Command('heroku {}'.format(cmd), stderr=suggest_stderr(cmd)) - assert get_new_command(command, None) == result + assert get_new_command(command) == result diff --git a/tests/rules/test_history.py b/tests/rules/test_history.py index 9f9c3b24..6422743b 100644 --- a/tests/rules/test_history.py +++ b/tests/rules/test_history.py @@ -25,13 +25,13 @@ def callables(mocker): @pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias') @pytest.mark.parametrize('script', ['ls cet', 'daff x']) def test_match(script): - assert match(Command(script=script), None) + assert match(Command(script=script)) @pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias') @pytest.mark.parametrize('script', ['apt-get', 'nocommand y']) def test_not_match(script): - assert not match(Command(script=script), None) + assert not match(Command(script=script)) @pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias') @@ -39,4 +39,4 @@ def test_not_match(script): ('ls cet', 'ls cat'), ('daff x', 'diff x')]) def test_get_new_command(script, result): - assert get_new_command(Command(script), None) == result + assert get_new_command(Command(script)) == result diff --git a/tests/rules/test_java.py b/tests/rules/test_java.py index 928ac69d..a7194ec3 100644 --- a/tests/rules/test_java.py +++ b/tests/rules/test_java.py @@ -7,11 +7,11 @@ from tests.utils import Command Command(script='java foo.java'), Command(script='java bar.java')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command, new_command', [ (Command('java foo.java'), 'java foo'), (Command('java bar.java'), 'java bar')]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_javac.py b/tests/rules/test_javac.py index 47000384..a83b0821 100644 --- a/tests/rules/test_javac.py +++ b/tests/rules/test_javac.py @@ -7,11 +7,11 @@ from tests.utils import Command Command(script='javac foo'), Command(script='javac bar')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command, new_command', [ (Command('javac foo'), 'javac foo.java'), (Command('javac bar'), 'javac bar.java')]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_lein_not_task.py b/tests/rules/test_lein_not_task.py index 9069fcd0..b4fc4498 100644 --- a/tests/rules/test_lein_not_task.py +++ b/tests/rules/test_lein_not_task.py @@ -14,10 +14,10 @@ Did you mean this? def test_match(is_not_task): - assert match(Command(script='lein rpl', stderr=is_not_task), None) - assert not match(Command(script='ls', stderr=is_not_task), None) + assert match(Command(script='lein rpl', stderr=is_not_task)) + assert not match(Command(script='ls', stderr=is_not_task)) def test_get_new_command(is_not_task): - assert get_new_command(Command(script='lein rpl --help', stderr=is_not_task), - None) == ['lein repl --help', 'lein jar --help'] + assert get_new_command(Command(script='lein rpl --help', stderr=is_not_task)) \ + == ['lein repl --help', 'lein jar --help'] diff --git a/tests/rules/test_ls_lah.py b/tests/rules/test_ls_lah.py index 97325258..3766f87c 100644 --- a/tests/rules/test_ls_lah.py +++ b/tests/rules/test_ls_lah.py @@ -3,14 +3,14 @@ from tests.utils import Command def test_match(): - assert match(Command(script='ls'), None) - assert match(Command(script='ls file.py'), None) - assert match(Command(script='ls /opt'), None) - assert not match(Command(script='ls -lah /opt'), None) - assert not match(Command(script='pacman -S binutils'), None) - assert not match(Command(script='lsof'), None) + assert match(Command(script='ls')) + assert match(Command(script='ls file.py')) + assert match(Command(script='ls /opt')) + assert not match(Command(script='ls -lah /opt')) + assert not match(Command(script='pacman -S binutils')) + assert not match(Command(script='lsof')) def test_get_new_command(): - assert get_new_command(Command(script='ls file.py'), None) == 'ls -lah file.py' - assert get_new_command(Command(script='ls'), None) == 'ls -lah' + assert get_new_command(Command(script='ls file.py')) == 'ls -lah file.py' + assert get_new_command(Command(script='ls')) == 'ls -lah' diff --git a/tests/rules/test_man.py b/tests/rules/test_man.py index 7be54687..01eab173 100644 --- a/tests/rules/test_man.py +++ b/tests/rules/test_man.py @@ -12,14 +12,14 @@ from tests.utils import Command Command('man -s 2 read'), Command('man -s 3 read')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ Command('man'), Command('man ')]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, new_command', [ @@ -31,4 +31,4 @@ def test_not_match(command): (Command('man -s 2 read'), 'man -s 3 read'), (Command('man -s 3 read'), 'man -s 2 read')]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_man_no_space.py b/tests/rules/test_man_no_space.py index 669ebb85..949acf08 100644 --- a/tests/rules/test_man_no_space.py +++ b/tests/rules/test_man_no_space.py @@ -3,10 +3,10 @@ from tests.utils import Command def test_match(): - assert match(Command('mandiff', stderr='mandiff: command not found'), None) - assert not match(Command(), None) + assert match(Command('mandiff', stderr='mandiff: command not found')) + assert not match(Command()) def test_get_new_command(): assert get_new_command( - Command('mandiff'), None) == 'man diff' + Command('mandiff')) == 'man diff' diff --git a/tests/rules/test_mercurial.py b/tests/rules/test_mercurial.py index 8ef25198..75fdf836 100644 --- a/tests/rules/test_mercurial.py +++ b/tests/rules/test_mercurial.py @@ -37,7 +37,7 @@ from thefuck.rules.mercurial import ( )), ]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -63,7 +63,7 @@ def test_match(command): )), ]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, possibilities', [ @@ -131,4 +131,4 @@ def test_extract_possibilities(command, possibilities): )), 'hg rebase re'), ]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_mkdir_p.py b/tests/rules/test_mkdir_p.py index b04a53ae..8d8a3bfe 100644 --- a/tests/rules/test_mkdir_p.py +++ b/tests/rules/test_mkdir_p.py @@ -9,7 +9,7 @@ from tests.utils import Command Command('hdfs dfs -mkdir foo/bar/baz', stderr='mkdir: `foo/bar/baz\': No such file or directory') ]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -19,7 +19,7 @@ def test_match(command): Command('./bin/hdfs dfs -mkdir foo/bar/baz'), Command()]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, new_command', [ @@ -27,5 +27,5 @@ def test_not_match(command): (Command('hdfs dfs -mkdir foo/bar/baz'), 'hdfs dfs -mkdir -p foo/bar/baz'), (Command('./bin/hdfs dfs -mkdir foo/bar/baz'), './bin/hdfs dfs -mkdir -p foo/bar/baz')]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_mvn_no_command.py b/tests/rules/test_mvn_no_command.py index 5725adde..1bb57d87 100644 --- a/tests/rules/test_mvn_no_command.py +++ b/tests/rules/test_mvn_no_command.py @@ -6,7 +6,7 @@ from tests.utils import Command @pytest.mark.parametrize('command', [ Command(script='mvn', stdout='[ERROR] No goals have been specified for this build. You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -30,11 +30,11 @@ def test_match(command): Command(script='mvn -v') ]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, new_command', [ (Command(script='mvn', stdout='[ERROR] No goals have been specified for this build. You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]'), ['mvn clean package', 'mvn clean install']), (Command(script='mvn -N', stdout='[ERROR] No goals have been specified for this build. You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]'), ['mvn -N clean package', 'mvn -N clean install'])]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_mvn_unknown_lifecycle_phase.py b/tests/rules/test_mvn_unknown_lifecycle_phase.py index 421325d1..0c2b1a17 100644 --- a/tests/rules/test_mvn_unknown_lifecycle_phase.py +++ b/tests/rules/test_mvn_unknown_lifecycle_phase.py @@ -6,7 +6,7 @@ from tests.utils import Command @pytest.mark.parametrize('command', [ Command(script='mvn cle', stdout='[ERROR] Unknown lifecycle phase "cle". You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -30,11 +30,11 @@ def test_match(command): Command(script='mvn -v') ]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, new_command', [ (Command(script='mvn cle', stdout='[ERROR] Unknown lifecycle phase "cle". You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]'), ['mvn clean', 'mvn compile']), (Command(script='mvn claen package', stdout='[ERROR] Unknown lifecycle phase "claen". You must specify a valid lifecycle phase or a goal in the format : or :[:]:. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]'), ['mvn clean package'])]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_no_command.py b/tests/rules/test_no_command.py index a44c1fd9..a8a21cb8 100644 --- a/tests/rules/test_no_command.py +++ b/tests/rules/test_no_command.py @@ -11,19 +11,17 @@ def get_all_executables(mocker): @pytest.mark.usefixtures('no_memoize') def test_match(): - assert match(Command(stderr='vom: not found', script='vom file.py'), None) - assert match(Command(stderr='fucck: not found', script='fucck'), None) - assert not match(Command(stderr='qweqwe: not found', script='qweqwe'), None) - assert not match(Command(stderr='some text', script='vom file.py'), None) + assert match(Command(stderr='vom: not found', script='vom file.py')) + assert match(Command(stderr='fucck: not found', script='fucck')) + assert not match(Command(stderr='qweqwe: not found', script='qweqwe')) + assert not match(Command(stderr='some text', script='vom file.py')) @pytest.mark.usefixtures('no_memoize') def test_get_new_command(): assert get_new_command( Command(stderr='vom: not found', - script='vom file.py'), - None) == ['vim file.py'] + script='vom file.py')) == ['vim file.py'] assert get_new_command( Command(stderr='fucck: not found', - script='fucck'), - Command) == ['fsck'] + script='fucck')) == ['fsck'] diff --git a/tests/rules/test_no_such_file.py b/tests/rules/test_no_such_file.py index 3b251529..0735cd6b 100644 --- a/tests/rules/test_no_such_file.py +++ b/tests/rules/test_no_such_file.py @@ -8,7 +8,7 @@ from tests.utils import Command Command(script='mv foo bar/', stderr="mv: cannot move 'foo' to 'bar/': No such file or directory"), ]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -16,7 +16,7 @@ def test_match(command): Command(script='mv foo bar/foo', stderr="mv: permission denied"), ]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, new_command', [ @@ -24,4 +24,4 @@ def test_not_match(command): (Command(script='mv foo bar/', stderr="mv: cannot move 'foo' to 'bar/': No such file or directory"), 'mkdir -p bar && mv foo bar/'), ]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_open.py b/tests/rules/test_open.py index 3c660ead..c80eeca8 100644 --- a/tests/rules/test_open.py +++ b/tests/rules/test_open.py @@ -14,7 +14,7 @@ from tests.utils import Command Command(script='gnome-open foo.com'), Command(script='kde-open foo.com')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command, new_command', [ @@ -28,4 +28,4 @@ def test_match(command): (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, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_pacman.py b/tests/rules/test_pacman.py index f0e00515..956497f5 100644 --- a/tests/rules/test_pacman.py +++ b/tests/rules/test_pacman.py @@ -23,7 +23,7 @@ extra/vim-python3 7.4.712-1 \t/usr/bin/vim''' Command(script='vim', stderr='vim: command not found'), Command(script='sudo vim', stderr='sudo: vim: command not found')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command, return_value', [ @@ -33,14 +33,14 @@ def test_match(command): @patch.multiple(pacman, create=True, pacman=pacman_cmd) def test_match_mocked(subp_mock, command, return_value): subp_mock.check_output.return_value = return_value - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ Command(script='vim', stderr=''), Command(), Command(script='sudo vim', stderr=''), Command()]) def test_not_match(command): - assert not match(command, None) + assert not match(command) sudo_vim_possibilities = ['{} -S extra/gvim && sudo vim', @@ -66,7 +66,7 @@ vim_possibilities = [s.format(pacman_cmd) for s in vim_possibilities] (Command('convert'), ['{} -S extra/imagemagick && convert'.format(pacman_cmd)]), (Command('sudo convert'), ['{} -S extra/imagemagick && sudo convert'.format(pacman_cmd)])]) def test_get_new_command(command, new_command, mocker): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command @pytest.mark.parametrize('command, new_command, return_value', [ @@ -79,4 +79,4 @@ def test_get_new_command(command, new_command, mocker): @patch.multiple(pacman, create=True, pacman=pacman_cmd) def test_get_new_command_mocked(subp_mock, command, new_command, return_value): subp_mock.check_output.return_value = return_value - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_pacman_not_found.py b/tests/rules/test_pacman_not_found.py index c1c4a8da..b98dc335 100644 --- a/tests/rules/test_pacman_not_found.py +++ b/tests/rules/test_pacman_not_found.py @@ -15,7 +15,7 @@ extra/llvm35 3.5.2-13/usr/bin/llc''' Command(script='pacman llc', stderr='error: target not found: llc'), Command(script='sudo pacman llc', stderr='error: target not found: llc')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -25,7 +25,7 @@ def test_match(command): @patch('thefuck.specific.archlinux.subprocess') def test_match_mocked(subp_mock, command): subp_mock.check_output.return_value = PKGFILE_OUTPUT_LLC - assert match(command, None) + assert match(command) @pytest.mark.skipif(not getattr(pacman_not_found, 'enabled_by_default', True), @@ -35,7 +35,7 @@ def test_match_mocked(subp_mock, command): (Command(script='pacman -S llc', stderr='error: target not found: llc'), ['pacman -S extra/llvm', 'pacman -S extra/llvm35']), (Command(script='sudo pacman -S llc', stderr='error: target not found: llc'), ['sudo pacman -S extra/llvm', 'sudo pacman -S extra/llvm35'])]) def test_get_new_command(command, fixed): - assert get_new_command(command, None) == fixed + assert get_new_command(command) == fixed @pytest.mark.parametrize('command, fixed', [ @@ -45,4 +45,4 @@ def test_get_new_command(command, fixed): @patch('thefuck.specific.archlinux.subprocess') def test_get_new_command_mocked(subp_mock, command, fixed): subp_mock.check_output.return_value = PKGFILE_OUTPUT_LLC - assert get_new_command(command, None) == fixed + assert get_new_command(command) == fixed diff --git a/tests/rules/test_pip_unknown_command.py b/tests/rules/test_pip_unknown_command.py index 58ba2bb8..79fa2f2c 100644 --- a/tests/rules/test_pip_unknown_command.py +++ b/tests/rules/test_pip_unknown_command.py @@ -14,12 +14,11 @@ def pip_unknown_cmd_without_recommend(): def test_match(pip_unknown_cmd, pip_unknown_cmd_without_recommend): - assert match(Command('pip instatl', stderr=pip_unknown_cmd), None) + assert match(Command('pip instatl', stderr=pip_unknown_cmd)) assert not match(Command('pip i', - stderr=pip_unknown_cmd_without_recommend), - None) + stderr=pip_unknown_cmd_without_recommend)) def test_get_new_command(pip_unknown_cmd): - assert get_new_command(Command('pip instatl', stderr=pip_unknown_cmd), - None) == 'pip install' + assert get_new_command(Command('pip instatl', + stderr=pip_unknown_cmd)) == 'pip install' diff --git a/tests/rules/test_python_command.py b/tests/rules/test_python_command.py index 54be39a2..053457da 100644 --- a/tests/rules/test_python_command.py +++ b/tests/rules/test_python_command.py @@ -3,10 +3,10 @@ from tests.utils import Command def test_match(): - assert match(Command('temp.py', stderr='Permission denied'), None) - assert not match(Command(), None) + assert match(Command('temp.py', stderr='Permission denied')) + assert not match(Command()) def test_get_new_command(): - assert get_new_command(Command('./test_sudo.py'), None)\ + assert get_new_command(Command('./test_sudo.py'))\ == 'python ./test_sudo.py' diff --git a/tests/rules/test_python_execute.py b/tests/rules/test_python_execute.py index 168cb59b..0f4a2222 100644 --- a/tests/rules/test_python_execute.py +++ b/tests/rules/test_python_execute.py @@ -7,11 +7,11 @@ from tests.utils import Command Command(script='python foo'), Command(script='python bar')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command, new_command', [ (Command('python foo'), 'python foo.py'), (Command('python bar'), 'python bar.py')]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_quotation_marks.py b/tests/rules/test_quotation_marks.py index 7c654e77..27b6699d 100644 --- a/tests/rules/test_quotation_marks.py +++ b/tests/rules/test_quotation_marks.py @@ -8,7 +8,7 @@ from tests.utils import Command Command(script="git commit -am \"Mismatched Quotation Marks\'"), Command(script="echo \"hello\'")]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command, new_command', [ @@ -16,4 +16,4 @@ def test_match(command): (Command("git commit -am \"Mismatched Quotation Marks\'"), "git commit -am \"Mismatched Quotation Marks\""), (Command("echo \"hello\'"), "echo \"hello\"")]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_rm_dir.py b/tests/rules/test_rm_dir.py index ae8c92f1..65b41bd6 100644 --- a/tests/rules/test_rm_dir.py +++ b/tests/rules/test_rm_dir.py @@ -10,7 +10,7 @@ from tests.utils import Command Command('./bin/hdfs dfs -rm foo', stderr='rm: `foo`: Is a directory') ]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -19,13 +19,13 @@ def test_match(command): Command('./bin/hdfs dfs -rm foo'), Command()]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, new_command', [ (Command('rm foo'), 'rm -rf foo'), (Command('hdfs dfs -rm foo'), 'hdfs dfs -rm -r foo')]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_rm_root.py b/tests/rules/test_rm_root.py index f56595fc..47c243f8 100644 --- a/tests/rules/test_rm_root.py +++ b/tests/rules/test_rm_root.py @@ -5,7 +5,7 @@ from tests.utils import Command def test_match(): assert match(Command(script='rm -rf /', - stderr='add --no-preserve-root'), None) + stderr='add --no-preserve-root')) @pytest.mark.parametrize('command', [ @@ -13,9 +13,9 @@ def test_match(): Command(script='rm --no-preserve-root /', stderr='add --no-preserve-root'), Command(script='rm -rf /', stderr='')]) def test_not_match(command): - assert not match(command, None) + assert not match(command) def test_get_new_command(): - assert get_new_command(Command(script='rm -rf /'), None) \ + assert get_new_command(Command(script='rm -rf /')) \ == 'rm -rf / --no-preserve-root' diff --git a/tests/rules/test_sed_unterminated_s.py b/tests/rules/test_sed_unterminated_s.py index dabfeeeb..0c890efd 100644 --- a/tests/rules/test_sed_unterminated_s.py +++ b/tests/rules/test_sed_unterminated_s.py @@ -9,20 +9,20 @@ def sed_unterminated_s(): def test_match(sed_unterminated_s): - assert match(Command('sed -e s/foo/bar', stderr=sed_unterminated_s), None) - assert match(Command('sed -es/foo/bar', stderr=sed_unterminated_s), None) - assert match(Command('sed -e s/foo/bar -e s/baz/quz', stderr=sed_unterminated_s), None) - assert not match(Command('sed -e s/foo/bar'), None) - assert not match(Command('sed -es/foo/bar'), None) - assert not match(Command('sed -e s/foo/bar -e s/baz/quz'), None) + assert match(Command('sed -e s/foo/bar', stderr=sed_unterminated_s)) + assert match(Command('sed -es/foo/bar', stderr=sed_unterminated_s)) + assert match(Command('sed -e s/foo/bar -e s/baz/quz', stderr=sed_unterminated_s)) + assert not match(Command('sed -e s/foo/bar')) + assert not match(Command('sed -es/foo/bar')) + assert not match(Command('sed -e s/foo/bar -e s/baz/quz')) def test_get_new_command(sed_unterminated_s): - assert get_new_command(Command('sed -e s/foo/bar', stderr=sed_unterminated_s), None) \ + assert get_new_command(Command('sed -e s/foo/bar', stderr=sed_unterminated_s)) \ == 'sed -e s/foo/bar/' - assert get_new_command(Command('sed -es/foo/bar', stderr=sed_unterminated_s), None) \ + assert get_new_command(Command('sed -es/foo/bar', stderr=sed_unterminated_s)) \ == 'sed -es/foo/bar/' - assert get_new_command(Command(r"sed -e 's/\/foo/bar'", stderr=sed_unterminated_s), None) \ + assert get_new_command(Command(r"sed -e 's/\/foo/bar'", stderr=sed_unterminated_s)) \ == r"sed -e 's/\/foo/bar/'" - assert get_new_command(Command(r"sed -e s/foo/bar -es/baz/quz", stderr=sed_unterminated_s), None) \ + assert get_new_command(Command(r"sed -e s/foo/bar -es/baz/quz", stderr=sed_unterminated_s)) \ == r"sed -e s/foo/bar/ -es/baz/quz/" diff --git a/tests/rules/test_sl_ls.py b/tests/rules/test_sl_ls.py index 4ed7499e..a1c46d8d 100644 --- a/tests/rules/test_sl_ls.py +++ b/tests/rules/test_sl_ls.py @@ -4,9 +4,9 @@ from tests.utils import Command def test_match(): - assert match(Command('sl'), None) - assert not match(Command('ls'), None) + assert match(Command('sl')) + assert not match(Command('ls')) def test_get_new_command(): - assert get_new_command(Command('sl'), None) == 'ls' + assert get_new_command(Command('sl')) == 'ls' diff --git a/tests/rules/test_ssh_known_host.py b/tests/rules/test_ssh_known_host.py index 7d201f3a..d713acc5 100644 --- a/tests/rules/test_ssh_known_host.py +++ b/tests/rules/test_ssh_known_host.py @@ -44,23 +44,23 @@ Host key verification failed.""".format(path, '98.765.432.321') def test_match(ssh_error): errormsg, _, _, _ = ssh_error - assert match(Command('ssh', stderr=errormsg), None) - assert match(Command('ssh', stderr=errormsg), None) - assert match(Command('scp something something', stderr=errormsg), None) - assert match(Command('scp something something', stderr=errormsg), None) - assert not match(Command(stderr=errormsg), None) - assert not match(Command('notssh', stderr=errormsg), None) - assert not match(Command('ssh'), None) + assert match(Command('ssh', stderr=errormsg)) + assert match(Command('ssh', stderr=errormsg)) + assert match(Command('scp something something', stderr=errormsg)) + assert match(Command('scp something something', stderr=errormsg)) + assert not match(Command(stderr=errormsg)) + assert not match(Command('notssh', stderr=errormsg)) + assert not match(Command('ssh')) def test_side_effect(ssh_error): errormsg, path, reset, known_hosts = ssh_error command = Command('ssh user@host', stderr=errormsg) - side_effect(command, None, None) + side_effect(command, None) expected = ['123.234.567.890 asdjkasjdakjsd\n', '111.222.333.444 qwepoiwqepoiss\n'] assert known_hosts(path) == expected def test_get_new_command(ssh_error, monkeypatch): errormsg, _, _, _ = ssh_error - assert get_new_command(Command('ssh user@host', stderr=errormsg), None) == 'ssh user@host' + assert get_new_command(Command('ssh user@host', stderr=errormsg)) == 'ssh user@host' diff --git a/tests/rules/test_sudo.py b/tests/rules/test_sudo.py index 001132f0..fb6692bc 100644 --- a/tests/rules/test_sudo.py +++ b/tests/rules/test_sudo.py @@ -14,11 +14,11 @@ from tests.utils import Command ('You don\'t have access to the history DB.', ''), ('', "error: [Errno 13] Permission denied: '/usr/local/lib/python2.7/dist-packages/ipaddr.py'")]) def test_match(stderr, stdout): - assert match(Command(stderr=stderr, stdout=stdout), None) + assert match(Command(stderr=stderr, stdout=stdout)) def test_not_match(): - assert not match(Command(), None) + assert not match(Command()) @pytest.mark.parametrize('before, after', [ @@ -26,4 +26,4 @@ def test_not_match(): ('echo a > b', 'sudo sh -c "echo a > b"'), ('echo "a" >> b', 'sudo sh -c "echo \\"a\\" >> b"')]) def test_get_new_command(before, after): - assert get_new_command(Command(before), None) == after + assert get_new_command(Command(before)) == after diff --git a/tests/rules/test_switch_lang.py b/tests/rules/test_switch_lang.py index 71c2c731..1a048acd 100644 --- a/tests/rules/test_switch_lang.py +++ b/tests/rules/test_switch_lang.py @@ -9,7 +9,7 @@ from tests.utils import Command Command(stderr='command not found: фзе-пуе', script=u'фзе-пуе'), Command(stderr='command not found: λσ', script=u'λσ')]) def test_match(command): - assert switch_lang.match(command, None) + assert switch_lang.match(command) @pytest.mark.parametrize('command', [ @@ -18,11 +18,11 @@ def test_match(command): Command(stderr='command not found: агсл', script=u'агсл'), Command(stderr='some info', script=u'фзе-пуе')]) def test_not_match(command): - assert not switch_lang.match(command, None) + assert not switch_lang.match(command) @pytest.mark.parametrize('command, new_command', [ (Command(u'фзе-пуе штыефдд мшь'), 'apt-get install vim'), (Command(u'λσ -λα'), 'ls -la')]) def test_get_new_command(command, new_command): - assert switch_lang.get_new_command(command, None) == new_command + assert switch_lang.get_new_command(command) == new_command diff --git a/tests/rules/test_systemctl.py b/tests/rules/test_systemctl.py index 89619d84..c16b1eb4 100644 --- a/tests/rules/test_systemctl.py +++ b/tests/rules/test_systemctl.py @@ -4,15 +4,15 @@ from tests.utils import Command def test_match(): - assert match(Command('systemctl nginx start', stderr='Unknown operation \'nginx\'.'), None) - assert match(Command('sudo systemctl nginx start', stderr='Unknown operation \'nginx\'.'), None) - assert not match(Command('systemctl start nginx'), None) - assert not match(Command('systemctl start nginx'), None) - assert not match(Command('sudo systemctl nginx', stderr='Unknown operation \'nginx\'.'), None) - assert not match(Command('systemctl nginx', stderr='Unknown operation \'nginx\'.'), None) - assert not match(Command('systemctl start wtf', stderr='Failed to start wtf.service: Unit wtf.service failed to load: No such file or directory.'), None) + assert match(Command('systemctl nginx start', stderr='Unknown operation \'nginx\'.')) + assert match(Command('sudo systemctl nginx start', stderr='Unknown operation \'nginx\'.')) + assert not match(Command('systemctl start nginx')) + assert not match(Command('systemctl start nginx')) + assert not match(Command('sudo systemctl nginx', stderr='Unknown operation \'nginx\'.')) + assert not match(Command('systemctl nginx', stderr='Unknown operation \'nginx\'.')) + assert not match(Command('systemctl start wtf', stderr='Failed to start wtf.service: Unit wtf.service failed to load: No such file or directory.')) def test_get_new_command(): - assert get_new_command(Command('systemctl nginx start'), None) == "systemctl start nginx" - assert get_new_command(Command('sudo systemctl nginx start'), None) == "sudo systemctl start nginx" + assert get_new_command(Command('systemctl nginx start')) == "systemctl start nginx" + assert get_new_command(Command('sudo systemctl nginx start')) == "sudo systemctl start nginx" diff --git a/tests/rules/test_tmux.py b/tests/rules/test_tmux.py index 963e9f1d..51357b62 100644 --- a/tests/rules/test_tmux.py +++ b/tests/rules/test_tmux.py @@ -11,9 +11,9 @@ def tmux_ambiguous(): def test_match(tmux_ambiguous): - assert match(Command('tmux list', stderr=tmux_ambiguous), None) + assert match(Command('tmux list', stderr=tmux_ambiguous)) def test_get_new_command(tmux_ambiguous): - assert get_new_command(Command('tmux list', stderr=tmux_ambiguous), None)\ + assert get_new_command(Command('tmux list', stderr=tmux_ambiguous))\ == ['tmux list-keys', 'tmux list-panes', 'tmux list-windows'] diff --git a/tests/rules/test_tsuru_login.py b/tests/rules/test_tsuru_login.py index a1a7aa14..6a2059b6 100644 --- a/tests/rules/test_tsuru_login.py +++ b/tests/rules/test_tsuru_login.py @@ -15,7 +15,7 @@ error_msg = ( Command(script='tsuru app-log -f', stderr=error_msg[1]), ]) def test_match(command): - assert match(command, {}) + assert match(command) @pytest.mark.parametrize('command', [ @@ -24,7 +24,7 @@ def test_match(command): Command(script='tsuru app-log -f', stderr=('Error: unparseable data')), ]) def test_not_match(command): - assert not match(command, {}) + assert not match(command) @pytest.mark.parametrize('command, new_command', [ @@ -34,4 +34,4 @@ def test_not_match(command): 'tsuru login && tsuru app-log -f'), ]) def test_get_new_command(command, new_command): - assert get_new_command(command, {}) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_tsuru_not_command.py b/tests/rules/test_tsuru_not_command.py index 80baf7e4..635c922d 100644 --- a/tests/rules/test_tsuru_not_command.py +++ b/tests/rules/test_tsuru_not_command.py @@ -30,7 +30,7 @@ from thefuck.rules.tsuru_not_command import match, get_new_command )), ]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -58,7 +58,7 @@ def test_match(command): Command('tsuru env-get', stderr='Error: App thefuck not found.'), ]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, new_commands', [ @@ -87,4 +87,4 @@ def test_not_match(command): )), ['tsuru target-list']), ]) def test_get_new_command(command, new_commands): - assert get_new_command(command, None) == new_commands + assert get_new_command(command) == new_commands diff --git a/tests/rules/test_unknown_command.py b/tests/rules/test_unknown_command.py index a31c9d9c..3416b33a 100644 --- a/tests/rules/test_unknown_command.py +++ b/tests/rules/test_unknown_command.py @@ -9,7 +9,7 @@ from tests.utils import Command stderr='ls: Unknown command\nDid you mean -ls? This command begins with a dash.'), Command(script='hdfs dfs ls /foo/bar', stderr='ls: Unknown command\nDid you mean -ls? This command begins with a dash.')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -18,7 +18,7 @@ def test_match(command): Command(script='hdfs dfs -ls -R /foo/bar', stderr=''), Command()]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, new_command', [ @@ -31,5 +31,5 @@ def test_not_match(command): (Command('./bin/hdfs dfs -Dtest=fred ls -R /foo/bar', stderr='ls: Unknown command\nDid you mean -ls? This command begins with a dash.'), ['./bin/hdfs dfs -Dtest=fred -ls -R /foo/bar'])]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_vagrant_up.py b/tests/rules/test_vagrant_up.py index 1a7c9022..66f2ebfa 100644 --- a/tests/rules/test_vagrant_up.py +++ b/tests/rules/test_vagrant_up.py @@ -11,7 +11,7 @@ from tests.utils import Command Command(script='vagrant rdp devbox', stderr='VM must be created before running this command. Run `vagrant up` first.')]) def test_match(command): - assert match(command, None) + assert match(command) @pytest.mark.parametrize('command', [ @@ -20,7 +20,7 @@ def test_match(command): Command(script='vagrant ssh', stderr='A Vagrant environment or target machine is required to run this command. Run `vagrant init` to create a new Vagrant environment. Or, get an ID of a target machine from `vagrant global-status` to run this command on. A final option is to change to a directory with a Vagrantfile and to try again.'), Command()]) def test_not_match(command): - assert not match(command, None) + assert not match(command) @pytest.mark.parametrize('command, new_command', [ @@ -31,5 +31,5 @@ def test_not_match(command): (Command(script='vagrant rdp devbox', stderr='VM must be created before running this command. Run `vagrant up` first.'), ['vagrant up devbox && vagrant rdp devbox', 'vagrant up && vagrant rdp devbox'])]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/rules/test_whois.py b/tests/rules/test_whois.py index bb64b1b8..2426c9fe 100644 --- a/tests/rules/test_whois.py +++ b/tests/rules/test_whois.py @@ -8,11 +8,11 @@ from tests.utils import Command Command(script='whois https://en.wikipedia.org/'), Command(script='whois meta.unix.stackexchange.com')]) def test_match(command): - assert match(command, None) + assert match(command) def test_not_match(): - assert not match(Command(script='whois'), None) + assert not match(Command(script='whois')) # `whois com` actually makes sense @@ -23,4 +23,4 @@ def test_not_match(): 'whois stackexchange.com', 'whois com'])]) def test_get_new_command(command, new_command): - assert get_new_command(command, None) == new_command + assert get_new_command(command) == new_command diff --git a/tests/specific/test_git.py b/tests/specific/test_git.py index 549ce61c..8e1e61bc 100644 --- a/tests/specific/test_git.py +++ b/tests/specific/test_git.py @@ -9,9 +9,10 @@ from tests.utils import Command "19:23:25.470911 git.c:282 trace: alias expansion: com => 'commit' '--verbose'")]) def test_git_support(called, command, stderr): @git_support - def fn(command, settings): return command.script + def fn(command): + return command.script - assert fn(Command(script=called, stderr=stderr), None) == command + assert fn(Command(script=called, stderr=stderr)) == command @pytest.mark.parametrize('command, is_git', [ @@ -24,6 +25,7 @@ def test_git_support(called, command, stderr): ('cat hub', False)]) def test_git_support_match(command, is_git): @git_support - def fn(command, settings): return True + def fn(command): + return True - assert fn(Command(script=command), None) == is_git + assert fn(Command(script=command)) == is_git diff --git a/tests/specific/test_sudo.py b/tests/specific/test_sudo.py index b8243322..bbc3d360 100644 --- a/tests/specific/test_sudo.py +++ b/tests/specific/test_sudo.py @@ -13,8 +13,8 @@ from tests.utils import Command (False, 'sudo ls', 'ls', False), (False, 'ls', 'ls', False)]) def test_sudo_support(return_value, command, called, result): - def fn(command, settings): + def fn(command): assert command == Command(called) return return_value - assert sudo_support(fn)(Command(command), None) == result + assert sudo_support(fn)(Command(command)) == result diff --git a/tests/test_corrector.py b/tests/test_corrector.py index f84e7d18..61a6d06b 100644 --- a/tests/test_corrector.py +++ b/tests/test_corrector.py @@ -61,10 +61,10 @@ class TestGetRules(object): class TestIsRuleMatch(object): def test_no_match(self): assert not corrector.is_rule_match( - Command('ls'), Rule('', lambda *_: False)) + Command('ls'), Rule('', lambda _: False)) def test_match(self): - rule = Rule('', lambda x, _: x.script == 'cd ..') + rule = Rule('', lambda x: x.script == 'cd ..') assert corrector.is_rule_match(Command('cd ..'), rule) @pytest.mark.usefixtures('no_colors') @@ -77,25 +77,25 @@ class TestIsRuleMatch(object): class TestMakeCorrectedCommands(object): def test_with_rule_returns_list(self): - rule = Rule(get_new_command=lambda x, _: [x.script + '!', x.script + '@'], + rule = Rule(get_new_command=lambda x: [x.script + '!', x.script + '@'], priority=100) assert list(make_corrected_commands(Command(script='test'), rule)) \ == [CorrectedCommand(script='test!', priority=100), CorrectedCommand(script='test@', priority=200)] def test_with_rule_returns_command(self): - rule = Rule(get_new_command=lambda x, _: x.script + '!', + rule = Rule(get_new_command=lambda x: x.script + '!', priority=100) assert list(make_corrected_commands(Command(script='test'), rule)) \ == [CorrectedCommand(script='test!', priority=100)] def test_get_corrected_commands(mocker): command = Command('test', 'test', 'test') - rules = [Rule(match=lambda *_: False), - Rule(match=lambda *_: True, - get_new_command=lambda x, _: x.script + '!', priority=100), - Rule(match=lambda *_: True, - get_new_command=lambda x, _: [x.script + '@', x.script + ';'], + rules = [Rule(match=lambda _: False), + Rule(match=lambda _: True, + get_new_command=lambda x: x.script + '!', priority=100), + Rule(match=lambda _: True, + get_new_command=lambda x: [x.script + '@', x.script + ';'], priority=60)] mocker.patch('thefuck.corrector.get_rules', return_value=rules) assert [cmd.script for cmd in get_corrected_commands(command, None)] \ diff --git a/tests/test_utils.py b/tests/test_utils.py index 3566b4f5..13bd99ab 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3,8 +3,7 @@ from mock import Mock import six from thefuck.utils import default_settings, \ memoize, get_closest, get_all_executables, replace_argument, \ - get_all_matched_commands, is_app, for_app, cache -from thefuck.types import Settings + get_all_matched_commands, is_app, for_app, cache, compatibility_call from tests.utils import Command @@ -12,9 +11,12 @@ from tests.utils import Command ({'key': 'val'}, {}, {'key': 'val'}), ({'key': 'new-val'}, {'key': 'val'}, {'key': 'val'}), ({'key': 'new-val', 'unset': 'unset'}, {'key': 'val'}, {'key': 'val', 'unset': 'unset'})]) -def test_default_settings(override, old, new): - fn = lambda _, settings: settings - assert default_settings(override)(fn)(None, Settings(old)) == new +def test_default_settings(settings, override, old, new): + settings.clear() + settings.update(old) + fn = lambda _: _ + default_settings(override)(fn)(None) + assert settings == new def test_memoize(): @@ -112,10 +114,10 @@ def test_is_app(script, names, result): ('hg diff', ['git', 'hub'], False)]) def test_for_app(script, names, result): @for_app(*names) - def match(command, settings): + def match(command): return True - assert match(Command(script), None) == result + assert match(Command(script)) == result class TestCache(object): @@ -180,3 +182,50 @@ class TestCache(object): shelve.update({key: {'etag': '-1', 'value': 'old-value'}}) assert fn() == 'test' assert shelve == {key: {'etag': '0', 'value': 'test'}} + + +class TestCompatibilityCall(object): + def test_match(self): + def match(command): + assert command == Command() + return True + + assert compatibility_call(match, Command()) + + def test_old_match(self, settings): + def match(command, _settings): + assert command == Command() + assert settings == _settings + return True + + assert compatibility_call(match, Command()) + + def test_get_new_command(self): + def get_new_command(command): + assert command == Command() + return True + + assert compatibility_call(get_new_command, Command()) + + def test_old_get_new_command(self, settings): + def get_new_command(command, _settings): + assert command == Command() + assert settings == _settings + return True + + assert compatibility_call(get_new_command, Command()) + + def test_side_effect(self): + def side_effect(command, new_command): + assert command == Command() == new_command + return True + + assert compatibility_call(side_effect, Command(), Command()) + + def test_old_side_effect(self, settings): + def side_effect(command, new_command, _settings): + assert command == Command() == new_command + assert settings == _settings + return True + + assert compatibility_call(side_effect, Command(), Command()) diff --git a/thefuck/corrector.py b/thefuck/corrector.py index c1477a57..14257684 100644 --- a/thefuck/corrector.py +++ b/thefuck/corrector.py @@ -3,6 +3,7 @@ from imp import load_source from pathlib import Path from .conf import settings, DEFAULT_PRIORITY from .types import Rule, CorrectedCommand, SortedCorrectedCommandsSequence +from .utils import compatibility_call from . import logs @@ -49,14 +50,14 @@ def is_rule_match(command, rule): try: with logs.debug_time(u'Trying rule: {};'.format(rule.name)): - if rule.match(command, settings): + if compatibility_call(rule.match, command): return True except Exception: logs.rule_failed(rule, sys.exc_info()) def make_corrected_commands(command, rule): - new_commands = rule.get_new_command(command, settings) + new_commands = compatibility_call(rule.get_new_command, command) if not isinstance(new_commands, list): new_commands = (new_commands,) for n, new_command in enumerate(new_commands): diff --git a/thefuck/main.py b/thefuck/main.py index 262dcee5..497d97e7 100644 --- a/thefuck/main.py +++ b/thefuck/main.py @@ -13,6 +13,7 @@ import six from . import logs, types, shells from .conf import initialize_settings_file, init_settings, settings from .corrector import get_corrected_commands +from .utils import compatibility_call from .ui import select_command @@ -77,7 +78,7 @@ def get_command(args): def run_command(old_cmd, command): """Runs command from rule for passed command.""" if command.side_effect: - command.side_effect(old_cmd, command.script, settings) + compatibility_call(command.side_effect, old_cmd, command.script) shells.put_to_history(command.script) print(command.script) diff --git a/thefuck/rules/apt_get.py b/thefuck/rules/apt_get.py index 7dc306f2..883687c3 100644 --- a/thefuck/rules/apt_get.py +++ b/thefuck/rules/apt_get.py @@ -20,11 +20,11 @@ def get_package(command): return None -def match(command, settings): +def match(command): return 'not found' in command.stderr and get_package(command.script) -def get_new_command(command, settings): +def get_new_command(command): name = get_package(command.script) formatme = shells.and_('sudo apt-get install {}', '{}') return formatme.format(name, command.script) diff --git a/thefuck/rules/apt_get_search.py b/thefuck/rules/apt_get_search.py index 4454e85f..edc89376 100644 --- a/thefuck/rules/apt_get_search.py +++ b/thefuck/rules/apt_get_search.py @@ -3,9 +3,9 @@ from thefuck.utils import for_app @for_app('apt-get') -def match(command, settings): +def match(command): return command.script.startswith('apt-get search') -def get_new_command(command, settings): +def get_new_command(command): return re.sub(r'^apt-get', 'apt-cache', command.script) diff --git a/thefuck/rules/brew_install.py b/thefuck/rules/brew_install.py index a1432b03..3a42fdd4 100644 --- a/thefuck/rules/brew_install.py +++ b/thefuck/rules/brew_install.py @@ -24,7 +24,7 @@ def _get_similar_formula(formula_name): return get_closest(formula_name, _get_formulas(), 1, 0.85) -def match(command, settings): +def match(command): is_proper_command = ('brew install' in command.script and 'No available formula' in command.stderr) @@ -35,7 +35,7 @@ def match(command, settings): return False -def get_new_command(command, settings): +def get_new_command(command): not_exist_formula = re.findall(r'Error: No available formula for ([a-z]+)', command.stderr)[0] exist_formula = _get_similar_formula(not_exist_formula) diff --git a/thefuck/rules/brew_unknown_command.py b/thefuck/rules/brew_unknown_command.py index d798a7be..29fe6dc8 100644 --- a/thefuck/rules/brew_unknown_command.py +++ b/thefuck/rules/brew_unknown_command.py @@ -63,7 +63,7 @@ def _brew_commands(): 'doctor', 'create', 'edit'] -def match(command, settings): +def match(command): is_proper_command = ('brew' in command.script and 'Unknown command' in command.stderr) @@ -74,7 +74,7 @@ def match(command, settings): return False -def get_new_command(command, settings): +def get_new_command(command): broken_cmd = re.findall(r'Error: Unknown command: ([a-z]+)', command.stderr)[0] return replace_command(command, broken_cmd, _brew_commands()) diff --git a/thefuck/rules/brew_upgrade.py b/thefuck/rules/brew_upgrade.py index 2022088c..d188943b 100644 --- a/thefuck/rules/brew_upgrade.py +++ b/thefuck/rules/brew_upgrade.py @@ -6,9 +6,9 @@ # It currently upgrades all formula but this will soon change to require '--all'. -def match(command, settings): +def match(command): return command.script == 'brew upgrade' -def get_new_command(command, settings): +def get_new_command(command): return command.script + ' --all' diff --git a/thefuck/rules/cargo.py b/thefuck/rules/cargo.py index e8cee600..3a0418d1 100644 --- a/thefuck/rules/cargo.py +++ b/thefuck/rules/cargo.py @@ -1,6 +1,6 @@ -def match(command, settings): +def match(command): return command.script == 'cargo' -def get_new_command(command, settings): +def get_new_command(command): return 'cargo build' diff --git a/thefuck/rules/cargo_no_command.py b/thefuck/rules/cargo_no_command.py index ce77fe6e..76eec3cb 100644 --- a/thefuck/rules/cargo_no_command.py +++ b/thefuck/rules/cargo_no_command.py @@ -3,12 +3,12 @@ from thefuck.utils import replace_argument, for_app @for_app('cargo') -def match(command, settings): +def match(command): return ('No such subcommand' in command.stderr and 'Did you mean' in command.stderr) -def get_new_command(command, settings): +def get_new_command(command): broken = command.script.split()[1] fix = re.findall(r'Did you mean `([^`]*)`', command.stderr)[0] diff --git a/thefuck/rules/cd_correction.py b/thefuck/rules/cd_correction.py index 33c4fc30..24c70067 100644 --- a/thefuck/rules/cd_correction.py +++ b/thefuck/rules/cd_correction.py @@ -18,7 +18,7 @@ def _get_sub_dirs(parent): @sudo_support @for_app('cd') -def match(command, settings): +def match(command): """Match function copied from cd_mkdir.py""" return (command.script.startswith('cd ') and ('no such file or directory' in command.stderr.lower() @@ -26,7 +26,7 @@ def match(command, settings): @sudo_support -def get_new_command(command, settings): +def get_new_command(command): """ Attempt to rebuild the path string by spellchecking the directories. If it fails (i.e. no directories are a close enough match), then it @@ -47,7 +47,7 @@ def get_new_command(command, settings): if best_matches: cwd = os.path.join(cwd, best_matches[0]) else: - return cd_mkdir.get_new_command(command, settings) + return cd_mkdir.get_new_command(command) return 'cd "{0}"'.format(cwd) diff --git a/thefuck/rules/cd_mkdir.py b/thefuck/rules/cd_mkdir.py index 2262af74..e47479e2 100644 --- a/thefuck/rules/cd_mkdir.py +++ b/thefuck/rules/cd_mkdir.py @@ -6,12 +6,12 @@ from thefuck.specific.sudo import sudo_support @sudo_support @for_app('cd') -def match(command, settings): +def match(command): return (('no such file or directory' in command.stderr.lower() or 'cd: can\'t cd to' in command.stderr.lower())) @sudo_support -def get_new_command(command, settings): +def get_new_command(command): repl = shells.and_('mkdir -p \\1', 'cd \\1') return re.sub(r'^cd (.*)', repl, command.script) diff --git a/thefuck/rules/cd_parent.py b/thefuck/rules/cd_parent.py index 7b1faedb..f808f0df 100644 --- a/thefuck/rules/cd_parent.py +++ b/thefuck/rules/cd_parent.py @@ -8,9 +8,9 @@ # cd..: command not found -def match(command, settings): +def match(command): return command.script == 'cd..' -def get_new_command(command, settings): +def get_new_command(command): return 'cd ..' diff --git a/thefuck/rules/composer_not_command.py b/thefuck/rules/composer_not_command.py index abea7b34..e96e349a 100644 --- a/thefuck/rules/composer_not_command.py +++ b/thefuck/rules/composer_not_command.py @@ -3,12 +3,12 @@ from thefuck.utils import replace_argument, for_app @for_app('composer') -def match(command, settings): +def match(command): return (('did you mean this?' in command.stderr.lower() or 'did you mean one of these?' in command.stderr.lower())) -def get_new_command(command, settings): +def get_new_command(command): broken_cmd = re.findall(r"Command \"([^']*)\" is not defined", command.stderr)[0] new_cmd = re.findall(r'Did you mean this\?[^\n]*\n\s*([^\n]*)', command.stderr) if not new_cmd: diff --git a/thefuck/rules/cp_omitting_directory.py b/thefuck/rules/cp_omitting_directory.py index 51fa1294..66c7f8db 100644 --- a/thefuck/rules/cp_omitting_directory.py +++ b/thefuck/rules/cp_omitting_directory.py @@ -5,11 +5,11 @@ from thefuck.utils import for_app @sudo_support @for_app('cp') -def match(command, settings): +def match(command): stderr = command.stderr.lower() return 'omitting directory' in stderr or 'is a directory' in stderr @sudo_support -def get_new_command(command, settings): +def get_new_command(command): return re.sub(r'^cp', 'cp -a', command.script) diff --git a/thefuck/rules/cpp11.py b/thefuck/rules/cpp11.py index 200bf4d9..c30012ac 100644 --- a/thefuck/rules/cpp11.py +++ b/thefuck/rules/cpp11.py @@ -2,11 +2,11 @@ from thefuck.utils import for_app @for_app(['g++', 'clang++']) -def match(command, settings): +def match(command): return ('This file requires compiler and library support for the ' 'ISO C++ 2011 standard.' in command.stderr or '-Wc++11-extensions' in command.stderr) -def get_new_command(command, settings): +def get_new_command(command): return command.script + ' -std=c++11' diff --git a/thefuck/rules/dirty_untar.py b/thefuck/rules/dirty_untar.py index 4fdf4cf6..7e6a2719 100644 --- a/thefuck/rules/dirty_untar.py +++ b/thefuck/rules/dirty_untar.py @@ -25,18 +25,18 @@ def _tar_file(cmd): @for_app('tar') -def match(command, settings): +def match(command): return ('-C' not in command.script and _is_tar_extract(command.script) and _tar_file(command.script) is not None) -def get_new_command(command, settings): +def get_new_command(command): return shells.and_('mkdir -p {dir}', '{cmd} -C {dir}') \ .format(dir=_tar_file(command.script)[1], cmd=command.script) -def side_effect(old_cmd, command, settings): +def side_effect(old_cmd, command): with tarfile.TarFile(_tar_file(old_cmd.script)[0]) as archive: for file in archive.getnames(): os.remove(file) diff --git a/thefuck/rules/dirty_unzip.py b/thefuck/rules/dirty_unzip.py index bd2d5945..99f4bf5a 100644 --- a/thefuck/rules/dirty_unzip.py +++ b/thefuck/rules/dirty_unzip.py @@ -22,16 +22,16 @@ def _zip_file(command): @for_app('unzip') -def match(command, settings): +def match(command): return ('-d' not in command.script and _is_bad_zip(_zip_file(command))) -def get_new_command(command, settings): +def get_new_command(command): return '{} -d {}'.format(command.script, _zip_file(command)[:-4]) -def side_effect(old_cmd, command, settings): +def side_effect(old_cmd, command): with zipfile.ZipFile(_zip_file(old_cmd), 'r') as archive: for file in archive.namelist(): os.remove(file) diff --git a/thefuck/rules/django_south_ghost.py b/thefuck/rules/django_south_ghost.py index d3290c4a..fda221c6 100644 --- a/thefuck/rules/django_south_ghost.py +++ b/thefuck/rules/django_south_ghost.py @@ -1,8 +1,8 @@ -def match(command, settings): +def match(command): return 'manage.py' in command.script and \ 'migrate' in command.script \ and 'or pass --delete-ghost-migrations' in command.stderr -def get_new_command(command, settings): +def get_new_command(command): return u'{} --delete-ghost-migrations'.format(command.script) diff --git a/thefuck/rules/django_south_merge.py b/thefuck/rules/django_south_merge.py index bef05970..572d9daf 100644 --- a/thefuck/rules/django_south_merge.py +++ b/thefuck/rules/django_south_merge.py @@ -1,8 +1,8 @@ -def match(command, settings): +def match(command): return 'manage.py' in command.script and \ 'migrate' in command.script \ and '--merge: will just attempt the migration' in command.stderr -def get_new_command(command, settings): +def get_new_command(command): return u'{} --merge'.format(command.script) diff --git a/thefuck/rules/docker_not_command.py b/thefuck/rules/docker_not_command.py index 44578e39..52003cf5 100644 --- a/thefuck/rules/docker_not_command.py +++ b/thefuck/rules/docker_not_command.py @@ -7,7 +7,7 @@ from thefuck.specific.sudo import sudo_support @sudo_support @for_app('docker') -def match(command, settings): +def match(command): return 'is not a docker command' in command.stderr @@ -21,7 +21,7 @@ def get_docker_commands(): @sudo_support -def get_new_command(command, settings): +def get_new_command(command): wrong_command = re.findall( r"docker: '(\w+)' is not a docker command.", command.stderr)[0] return replace_command(command, wrong_command, get_docker_commands()) diff --git a/thefuck/rules/dry.py b/thefuck/rules/dry.py index f0954ea6..075df04f 100644 --- a/thefuck/rules/dry.py +++ b/thefuck/rules/dry.py @@ -1,10 +1,10 @@ -def match(command, settings): +def match(command): split_command = command.script.split() return len(split_command) >= 2 and split_command[0] == split_command[1] -def get_new_command(command, settings): +def get_new_command(command): return command.script[command.script.find(' ')+1:] # it should be rare enough to actually have to type twice the same word, so diff --git a/thefuck/rules/fix_alt_space.py b/thefuck/rules/fix_alt_space.py index f807c198..2e64ec97 100644 --- a/thefuck/rules/fix_alt_space.py +++ b/thefuck/rules/fix_alt_space.py @@ -5,11 +5,11 @@ from thefuck.specific.sudo import sudo_support @sudo_support -def match(command, settings): +def match(command): return ('command not found' in command.stderr.lower() and u' ' in command.script) @sudo_support -def get_new_command(command, settings): +def get_new_command(command): return re.sub(u' ', ' ', command.script) diff --git a/thefuck/rules/fix_file.py b/thefuck/rules/fix_file.py index 895244d8..7177c270 100644 --- a/thefuck/rules/fix_file.py +++ b/thefuck/rules/fix_file.py @@ -1,6 +1,7 @@ import re import os from thefuck.utils import memoize, default_settings +from thefuck.conf import settings from thefuck import shells @@ -50,7 +51,7 @@ def _search(stderr): return m -def match(command, settings): +def match(command): if 'EDITOR' not in os.environ: return False @@ -58,8 +59,8 @@ def match(command, settings): @default_settings({'fixlinecmd': '{editor} {file} +{line}', - 'fixcolcmd': None}) -def get_new_command(command, settings): + 'fixcolcmd': None}) +def get_new_command(command): m = _search(command.stderr) or _search(command.stdout) # Note: there does not seem to be a standard for columns, so they are just diff --git a/thefuck/rules/git_add.py b/thefuck/rules/git_add.py index 617491f3..452797b0 100644 --- a/thefuck/rules/git_add.py +++ b/thefuck/rules/git_add.py @@ -4,13 +4,13 @@ from thefuck.specific.git import git_support @git_support -def match(command, settings): +def match(command): return ('did not match any file(s) known to git.' in command.stderr and "Did you forget to 'git add'?" in command.stderr) @git_support -def get_new_command(command, settings): +def get_new_command(command): missing_file = re.findall( r"error: pathspec '([^']*)' " r"did not match any file\(s\) known to git.", command.stderr)[0] diff --git a/thefuck/rules/git_branch_delete.py b/thefuck/rules/git_branch_delete.py index d334c08b..2fb83d01 100644 --- a/thefuck/rules/git_branch_delete.py +++ b/thefuck/rules/git_branch_delete.py @@ -3,11 +3,11 @@ from thefuck.specific.git import git_support @git_support -def match(command, settings): +def match(command): return ('branch -d' in command.script and 'If you are sure you want to delete it' in command.stderr) @git_support -def get_new_command(command, settings): +def get_new_command(command): return replace_argument(command.script, '-d', '-D') diff --git a/thefuck/rules/git_branch_list.py b/thefuck/rules/git_branch_list.py index 25e4aaac..d40ef98d 100644 --- a/thefuck/rules/git_branch_list.py +++ b/thefuck/rules/git_branch_list.py @@ -3,11 +3,11 @@ from thefuck.specific.git import git_support @git_support -def match(command, settings): +def match(command): # catches "git branch list" in place of "git branch" return command.script.split()[1:] == 'branch list'.split() @git_support -def get_new_command(command, settings): +def get_new_command(command): return shells.and_('git branch --delete list', 'git branch') diff --git a/thefuck/rules/git_checkout.py b/thefuck/rules/git_checkout.py index de165fa5..d0869c10 100644 --- a/thefuck/rules/git_checkout.py +++ b/thefuck/rules/git_checkout.py @@ -6,7 +6,7 @@ from thefuck.specific.git import git_support @git_support -def match(command, settings): +def match(command): return ('did not match any file(s) known to git.' in command.stderr and "Did you forget to 'git add'?" not in command.stderr) @@ -25,7 +25,7 @@ def get_branches(): @git_support -def get_new_command(command, settings): +def get_new_command(command): missing_file = re.findall( r"error: pathspec '([^']*)' " r"did not match any file\(s\) known to git.", command.stderr)[0] diff --git a/thefuck/rules/git_diff_staged.py b/thefuck/rules/git_diff_staged.py index bdb3ba60..8afd29f7 100644 --- a/thefuck/rules/git_diff_staged.py +++ b/thefuck/rules/git_diff_staged.py @@ -3,11 +3,11 @@ from thefuck.specific.git import git_support @git_support -def match(command, settings): +def match(command): return ('diff' in command.script and '--staged' not in command.script) @git_support -def get_new_command(command, settings): +def get_new_command(command): return replace_argument(command.script, 'diff', 'diff --staged') diff --git a/thefuck/rules/git_fix_stash.py b/thefuck/rules/git_fix_stash.py index b83b539e..8cf6ae32 100644 --- a/thefuck/rules/git_fix_stash.py +++ b/thefuck/rules/git_fix_stash.py @@ -4,7 +4,7 @@ from thefuck.specific.git import git_support @git_support -def match(command, settings): +def match(command): return (command.script.split()[1] == 'stash' and 'usage:' in command.stderr) @@ -21,7 +21,7 @@ stash_commands = ( @git_support -def get_new_command(command, settings): +def get_new_command(command): stash_cmd = command.script.split()[2] fixed = utils.get_closest(stash_cmd, stash_commands, fallback_to_first=False) diff --git a/thefuck/rules/git_not_command.py b/thefuck/rules/git_not_command.py index 6c6675ff..cfc8106b 100644 --- a/thefuck/rules/git_not_command.py +++ b/thefuck/rules/git_not_command.py @@ -4,13 +4,13 @@ from thefuck.specific.git import git_support @git_support -def match(command, settings): +def match(command): return (" is not a git command. See 'git --help'." in command.stderr and 'Did you mean' in command.stderr) @git_support -def get_new_command(command, settings): +def get_new_command(command): broken_cmd = re.findall(r"git: '([^']*)' is not a git command", command.stderr)[0] matched = get_all_matched_commands(command.stderr) diff --git a/thefuck/rules/git_pull.py b/thefuck/rules/git_pull.py index 426e3420..3c04f7d2 100644 --- a/thefuck/rules/git_pull.py +++ b/thefuck/rules/git_pull.py @@ -3,13 +3,13 @@ from thefuck.specific.git import git_support @git_support -def match(command, settings): +def match(command): return ('pull' in command.script and 'set-upstream' in command.stderr) @git_support -def get_new_command(command, settings): +def get_new_command(command): line = command.stderr.split('\n')[-3].strip() branch = line.split(' ')[-1] set_upstream = line.replace('', 'origin')\ diff --git a/thefuck/rules/git_pull_clone.py b/thefuck/rules/git_pull_clone.py index a2d1cfb2..7d513294 100644 --- a/thefuck/rules/git_pull_clone.py +++ b/thefuck/rules/git_pull_clone.py @@ -3,11 +3,11 @@ from thefuck.specific.git import git_support @git_support -def match(command, settings): +def match(command): return ('fatal: Not a git repository' in command.stderr and "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)." in command.stderr) @git_support -def get_new_command(command, settings): +def get_new_command(command): return replace_argument(command.script, 'pull', 'clone') diff --git a/thefuck/rules/git_push.py b/thefuck/rules/git_push.py index badce6d6..22237105 100644 --- a/thefuck/rules/git_push.py +++ b/thefuck/rules/git_push.py @@ -2,11 +2,11 @@ from thefuck.specific.git import git_support @git_support -def match(command, settings): +def match(command): return ('push' in command.script and 'set-upstream' in command.stderr) @git_support -def get_new_command(command, settings): +def get_new_command(command): return command.stderr.split('\n')[-3].strip() diff --git a/thefuck/rules/git_push_force.py b/thefuck/rules/git_push_force.py index 45a3085a..1bb23f89 100644 --- a/thefuck/rules/git_push_force.py +++ b/thefuck/rules/git_push_force.py @@ -3,7 +3,7 @@ from thefuck.specific.git import git_support @git_support -def match(command, settings): +def match(command): return ('push' in command.script and '! [rejected]' in command.stderr and 'failed to push some refs to' in command.stderr @@ -11,7 +11,7 @@ def match(command, settings): @git_support -def get_new_command(command, settings): +def get_new_command(command): return replace_argument(command.script, 'push', 'push --force') diff --git a/thefuck/rules/git_push_pull.py b/thefuck/rules/git_push_pull.py index 897406b7..8560198e 100644 --- a/thefuck/rules/git_push_pull.py +++ b/thefuck/rules/git_push_pull.py @@ -4,7 +4,7 @@ from thefuck.specific.git import git_support @git_support -def match(command, settings): +def match(command): return ('push' in command.script and '! [rejected]' in command.stderr and 'failed to push some refs to' in command.stderr @@ -12,6 +12,6 @@ def match(command, settings): @git_support -def get_new_command(command, settings): +def get_new_command(command): return shells.and_(replace_argument(command.script, 'push', 'pull'), command.script) diff --git a/thefuck/rules/git_stash.py b/thefuck/rules/git_stash.py index c0bad412..df7f3850 100644 --- a/thefuck/rules/git_stash.py +++ b/thefuck/rules/git_stash.py @@ -3,13 +3,13 @@ from thefuck.specific.git import git_support @git_support -def match(command, settings): +def match(command): # catches "Please commit or stash them" and "Please, commit your changes or # stash them before you can switch branches." return 'or stash them' in command.stderr @git_support -def get_new_command(command, settings): +def get_new_command(command): formatme = shells.and_('git stash', '{}') return formatme.format(command.script) diff --git a/thefuck/rules/go_run.py b/thefuck/rules/go_run.py index b009324b..30db699b 100644 --- a/thefuck/rules/go_run.py +++ b/thefuck/rules/go_run.py @@ -7,10 +7,10 @@ from thefuck.utils import for_app @for_app('go') -def match(command, settings): +def match(command): return (command.script.startswith('go run ') and not command.script.endswith('.go')) -def get_new_command(command, settings): +def get_new_command(command): return command.script + '.go' diff --git a/thefuck/rules/grep_recursive.py b/thefuck/rules/grep_recursive.py index 94547b26..af92300d 100644 --- a/thefuck/rules/grep_recursive.py +++ b/thefuck/rules/grep_recursive.py @@ -2,9 +2,9 @@ from thefuck.utils import for_app @for_app('grep') -def match(command, settings): +def match(command): return 'is a directory' in command.stderr.lower() -def get_new_command(command, settings): +def get_new_command(command): return 'grep -r {}'.format(command.script[5:]) diff --git a/thefuck/rules/gulp_not_task.py b/thefuck/rules/gulp_not_task.py index 853fa609..8d207079 100644 --- a/thefuck/rules/gulp_not_task.py +++ b/thefuck/rules/gulp_not_task.py @@ -4,7 +4,7 @@ from thefuck.utils import replace_command, for_app @for_app('gulp') -def match(command, script): +def match(command): return 'is not in your gulpfile' in command.stdout @@ -15,7 +15,7 @@ def get_gulp_tasks(): for line in proc.stdout.readlines()] -def get_new_command(command, script): +def get_new_command(command): wrong_task = re.findall(r"Task '(\w+)' is not in your gulpfile", command.stdout)[0] return replace_command(command, wrong_task, get_gulp_tasks()) diff --git a/thefuck/rules/has_exists_script.py b/thefuck/rules/has_exists_script.py index 3413237f..0ffc0860 100644 --- a/thefuck/rules/has_exists_script.py +++ b/thefuck/rules/has_exists_script.py @@ -3,11 +3,11 @@ from thefuck.specific.sudo import sudo_support @sudo_support -def match(command, settings): +def match(command): return os.path.exists(command.script.split()[0]) \ and 'command not found' in command.stderr @sudo_support -def get_new_command(command, settings): +def get_new_command(command): return u'./{}'.format(command.script) diff --git a/thefuck/rules/heroku_not_command.py b/thefuck/rules/heroku_not_command.py index a01e1577..48c322a0 100644 --- a/thefuck/rules/heroku_not_command.py +++ b/thefuck/rules/heroku_not_command.py @@ -3,7 +3,7 @@ from thefuck.utils import replace_command, for_app @for_app('heroku') -def match(command, settings): +def match(command): return 'is not a heroku command' in command.stderr and \ 'Perhaps you meant' in command.stderr @@ -14,6 +14,6 @@ def _get_suggests(stderr): return re.findall(r'`([^`]+)`', line) -def get_new_command(command, settings): +def get_new_command(command): wrong = re.findall(r'`(\w+)` is not a heroku command', command.stderr)[0] return replace_command(command, wrong, _get_suggests(command.stderr)) diff --git a/thefuck/rules/history.py b/thefuck/rules/history.py index 1fd011b0..0f187d99 100644 --- a/thefuck/rules/history.py +++ b/thefuck/rules/history.py @@ -24,12 +24,12 @@ def _history_of_exists_without_current(command): and line.split(' ')[0] in executables] -def match(command, settings): +def match(command): return len(get_close_matches(command.script, _history_of_exists_without_current(command))) -def get_new_command(command, settings): +def get_new_command(command): return get_closest(command.script, _history_of_exists_without_current(command)) diff --git a/thefuck/rules/java.py b/thefuck/rules/java.py index f7a33c74..264be432 100644 --- a/thefuck/rules/java.py +++ b/thefuck/rules/java.py @@ -9,9 +9,9 @@ from thefuck.utils import for_app @for_app('java') -def match(command, settings): +def match(command): return command.script.endswith('.java') -def get_new_command(command, settings): +def get_new_command(command): return command.script[:-5] diff --git a/thefuck/rules/javac.py b/thefuck/rules/javac.py index be40a5e7..07c08172 100644 --- a/thefuck/rules/javac.py +++ b/thefuck/rules/javac.py @@ -10,9 +10,9 @@ from thefuck.utils import for_app @for_app('javac') -def match(command, settings): +def match(command): return not command.script.endswith('.java') -def get_new_command(command, settings): +def get_new_command(command): return command.script + '.java' diff --git a/thefuck/rules/lein_not_task.py b/thefuck/rules/lein_not_task.py index 3849ac55..7118080e 100644 --- a/thefuck/rules/lein_not_task.py +++ b/thefuck/rules/lein_not_task.py @@ -5,14 +5,14 @@ from thefuck.specific.sudo import sudo_support @sudo_support @for_app('lein') -def match(command, settings): +def match(command): return (command.script.startswith('lein') and "is not a task. See 'lein help'" in command.stderr and 'Did you mean this?' in command.stderr) @sudo_support -def get_new_command(command, settings): +def get_new_command(command): broken_cmd = re.findall(r"'([^']*)' is not a task", command.stderr)[0] new_cmds = get_all_matched_commands(command.stderr, 'Did you mean this?') diff --git a/thefuck/rules/ls_lah.py b/thefuck/rules/ls_lah.py index b8e6590b..54b6d651 100644 --- a/thefuck/rules/ls_lah.py +++ b/thefuck/rules/ls_lah.py @@ -2,11 +2,11 @@ from thefuck.utils import for_app @for_app('ls') -def match(command, settings): +def match(command): return 'ls -' not in command.script -def get_new_command(command, settings): +def get_new_command(command): command = command.script.split(' ') command[0] = 'ls -lah' return ' '.join(command) diff --git a/thefuck/rules/man.py b/thefuck/rules/man.py index 869350c7..84427e79 100644 --- a/thefuck/rules/man.py +++ b/thefuck/rules/man.py @@ -1,8 +1,8 @@ -def match(command, settings): +def match(command): return command.script.strip().startswith('man ') -def get_new_command(command, settings): +def get_new_command(command): if '3' in command.script: return command.script.replace("3", "2") if '2' in command.script: diff --git a/thefuck/rules/man_no_space.py b/thefuck/rules/man_no_space.py index 17513522..c5869345 100644 --- a/thefuck/rules/man_no_space.py +++ b/thefuck/rules/man_no_space.py @@ -1,9 +1,9 @@ -def match(command, settings): +def match(command): return (command.script.startswith(u'man') and u'command not found' in command.stderr.lower()) -def get_new_command(command, settings): +def get_new_command(command): return u'man {}'.format(command.script[3:]) priority = 2000 diff --git a/thefuck/rules/mercurial.py b/thefuck/rules/mercurial.py index 338629aa..c8290fb5 100644 --- a/thefuck/rules/mercurial.py +++ b/thefuck/rules/mercurial.py @@ -13,14 +13,14 @@ def extract_possibilities(command): @for_app('hg') -def match(command, settings): +def match(command): return ('hg: unknown command' in command.stderr and '(did you mean one of ' in command.stderr or "hg: command '" in command.stderr and "' is ambiguous:" in command.stderr) -def get_new_command(command, settings): +def get_new_command(command): script = command.script.split(' ') possibilities = extract_possibilities(command) script[1] = get_closest(script[1], possibilities) diff --git a/thefuck/rules/mkdir_p.py b/thefuck/rules/mkdir_p.py index 013bfc16..40f59728 100644 --- a/thefuck/rules/mkdir_p.py +++ b/thefuck/rules/mkdir_p.py @@ -3,11 +3,11 @@ from thefuck.specific.sudo import sudo_support @sudo_support -def match(command, settings): +def match(command): return ('mkdir' in command.script and 'No such file or directory' in command.stderr) @sudo_support -def get_new_command(command, settings): +def get_new_command(command): return re.sub('\\bmkdir (.*)', 'mkdir -p \\1', command.script) diff --git a/thefuck/rules/mvn_no_command.py b/thefuck/rules/mvn_no_command.py index 7113c574..e489d00b 100644 --- a/thefuck/rules/mvn_no_command.py +++ b/thefuck/rules/mvn_no_command.py @@ -2,10 +2,10 @@ from thefuck.utils import for_app @for_app('mvn') -def match(command, settings): +def match(command): return 'No goals have been specified for this build' in command.stdout -def get_new_command(command, settings): +def get_new_command(command): return [command.script + ' clean package', command.script + ' clean install'] diff --git a/thefuck/rules/mvn_unknown_lifecycle_phase.py b/thefuck/rules/mvn_unknown_lifecycle_phase.py index c4a7ee17..eb0ed52f 100644 --- a/thefuck/rules/mvn_unknown_lifecycle_phase.py +++ b/thefuck/rules/mvn_unknown_lifecycle_phase.py @@ -14,13 +14,13 @@ def _getavailable_lifecycles(command): @for_app('mvn') -def match(command, settings): +def match(command): failed_lifecycle = _get_failed_lifecycle(command) available_lifecycles = _getavailable_lifecycles(command) return available_lifecycles and failed_lifecycle -def get_new_command(command, settings): +def get_new_command(command): failed_lifecycle = _get_failed_lifecycle(command) available_lifecycles = _getavailable_lifecycles(command) if available_lifecycles and failed_lifecycle: diff --git a/thefuck/rules/no_command.py b/thefuck/rules/no_command.py index 2e77594f..5318568c 100644 --- a/thefuck/rules/no_command.py +++ b/thefuck/rules/no_command.py @@ -4,14 +4,14 @@ from thefuck.specific.sudo import sudo_support @sudo_support -def match(command, settings): +def match(command): return 'not found' in command.stderr and \ bool(get_close_matches(command.script.split(' ')[0], get_all_executables())) @sudo_support -def get_new_command(command, settings): +def get_new_command(command): old_command = command.script.split(' ')[0] new_cmds = get_close_matches(old_command, get_all_executables(), cutoff=0.1) return [' '.join([new_command] + command.script.split(' ')[1:]) diff --git a/thefuck/rules/no_such_file.py b/thefuck/rules/no_such_file.py index 44572f19..68a989b5 100644 --- a/thefuck/rules/no_such_file.py +++ b/thefuck/rules/no_such_file.py @@ -10,7 +10,7 @@ patterns = ( ) -def match(command, settings): +def match(command): for pattern in patterns: if re.search(pattern, command.stderr): return True @@ -18,7 +18,7 @@ def match(command, settings): return False -def get_new_command(command, settings): +def get_new_command(command): for pattern in patterns: file = re.findall(pattern, command.stderr) diff --git a/thefuck/rules/open.py b/thefuck/rules/open.py index 6de2c963..b46f497d 100644 --- a/thefuck/rules/open.py +++ b/thefuck/rules/open.py @@ -9,7 +9,7 @@ from thefuck.utils import for_app @for_app('open', 'xdg-open', 'gnome-open', 'kde-open') -def match(command, settings): +def match(command): return ('.com' in command.script or '.net' in command.script or '.org' in command.script @@ -22,5 +22,5 @@ def match(command, settings): or 'www.' in command.script) -def get_new_command(command, settings): +def get_new_command(command): return command.script.replace('open ', 'open http://') diff --git a/thefuck/rules/pacman.py b/thefuck/rules/pacman.py index 94257797..67b5bd22 100644 --- a/thefuck/rules/pacman.py +++ b/thefuck/rules/pacman.py @@ -2,11 +2,11 @@ from thefuck.specific.archlinux import get_pkgfile, archlinux_env from thefuck import shells -def match(command, settings): +def match(command): return 'not found' in command.stderr and get_pkgfile(command.script) -def get_new_command(command, settings): +def get_new_command(command): packages = get_pkgfile(command.script) formatme = shells.and_('{} -S {}', '{}') diff --git a/thefuck/rules/pacman_not_found.py b/thefuck/rules/pacman_not_found.py index d8159b26..9875935b 100644 --- a/thefuck/rules/pacman_not_found.py +++ b/thefuck/rules/pacman_not_found.py @@ -10,12 +10,12 @@ from thefuck.utils import replace_command from thefuck.specific.archlinux import get_pkgfile, archlinux_env -def match(command, settings): +def match(command): return (command.script.startswith(('pacman', 'sudo pacman', 'yaourt')) and 'error: target not found:' in command.stderr) -def get_new_command(command, settings): +def get_new_command(command): pgr = command.script.split()[-1] return replace_command(command, pgr, get_pkgfile(pgr)) diff --git a/thefuck/rules/pip_unknown_command.py b/thefuck/rules/pip_unknown_command.py index 61293f80..af6a4dc6 100644 --- a/thefuck/rules/pip_unknown_command.py +++ b/thefuck/rules/pip_unknown_command.py @@ -5,13 +5,13 @@ from thefuck.specific.sudo import sudo_support @sudo_support @for_app('pip') -def match(command, settings): +def match(command): return ('pip' in command.script and 'unknown command' in command.stderr and 'maybe you meant' in command.stderr) -def get_new_command(command, settings): +def get_new_command(command): broken_cmd = re.findall(r'ERROR: unknown command \"([a-z]+)\"', command.stderr)[0] new_cmd = re.findall(r'maybe you meant \"([a-z]+)\"', command.stderr)[0] diff --git a/thefuck/rules/python_command.py b/thefuck/rules/python_command.py index 42cf18db..95bbf021 100644 --- a/thefuck/rules/python_command.py +++ b/thefuck/rules/python_command.py @@ -5,7 +5,7 @@ from thefuck.specific.sudo import sudo_support @sudo_support -def match(command, settings): +def match(command): toks = command.script.split() return (len(toks) > 0 and toks[0].endswith('.py') @@ -14,5 +14,5 @@ def match(command, settings): @sudo_support -def get_new_command(command, settings): +def get_new_command(command): return 'python ' + command.script diff --git a/thefuck/rules/python_execute.py b/thefuck/rules/python_execute.py index 2de751e9..23321e75 100644 --- a/thefuck/rules/python_execute.py +++ b/thefuck/rules/python_execute.py @@ -7,9 +7,9 @@ from thefuck.utils import for_app @for_app('python') -def match(command, settings): +def match(command): return not command.script.endswith('.py') -def get_new_command(command, settings): +def get_new_command(command): return command.script + '.py' diff --git a/thefuck/rules/quotation_marks.py b/thefuck/rules/quotation_marks.py index c6d5e27a..6ba491cf 100644 --- a/thefuck/rules/quotation_marks.py +++ b/thefuck/rules/quotation_marks.py @@ -4,9 +4,9 @@ # > git commit -m 'My Message" -def match(command, settings): +def match(command): return '\'' in command.script and '\"' in command.script -def get_new_command(command, settings): +def get_new_command(command): return command.script.replace('\'', '\"') diff --git a/thefuck/rules/rm_dir.py b/thefuck/rules/rm_dir.py index 9a8baa77..51cdff8f 100644 --- a/thefuck/rules/rm_dir.py +++ b/thefuck/rules/rm_dir.py @@ -3,13 +3,13 @@ from thefuck.specific.sudo import sudo_support @sudo_support -def match(command, settings): +def match(command): return ('rm' in command.script and 'is a directory' in command.stderr.lower()) @sudo_support -def get_new_command(command, settings): +def get_new_command(command): arguments = '-rf' if 'hdfs' in command.script: arguments = '-r' diff --git a/thefuck/rules/rm_root.py b/thefuck/rules/rm_root.py index 9c89f3a7..28e522f5 100644 --- a/thefuck/rules/rm_root.py +++ b/thefuck/rules/rm_root.py @@ -4,12 +4,12 @@ enabled_by_default = False @sudo_support -def match(command, settings): +def match(command): return ({'rm', '/'}.issubset(command.script.split()) and '--no-preserve-root' not in command.script and '--no-preserve-root' in command.stderr) @sudo_support -def get_new_command(command, settings): +def get_new_command(command): return u'{} --no-preserve-root'.format(command.script) diff --git a/thefuck/rules/sed_unterminated_s.py b/thefuck/rules/sed_unterminated_s.py index 5a8ea6d3..718d6940 100644 --- a/thefuck/rules/sed_unterminated_s.py +++ b/thefuck/rules/sed_unterminated_s.py @@ -3,11 +3,11 @@ from thefuck.utils import quote, for_app @for_app('sed') -def match(command, settings): +def match(command): return "unterminated `s' command" in command.stderr -def get_new_command(command, settings): +def get_new_command(command): script = shlex.split(command.script) for (i, e) in enumerate(script): diff --git a/thefuck/rules/sl_ls.py b/thefuck/rules/sl_ls.py index 0b3d017a..e384df7d 100644 --- a/thefuck/rules/sl_ls.py +++ b/thefuck/rules/sl_ls.py @@ -6,9 +6,9 @@ I often fuck up 'ls' and type 'sl'. No more! """ -def match(command, settings): +def match(command): return command.script == 'sl' -def get_new_command(command, settings): +def get_new_command(command): return 'ls' diff --git a/thefuck/rules/ssh_known_hosts.py b/thefuck/rules/ssh_known_hosts.py index c6aec195..e4495cda 100644 --- a/thefuck/rules/ssh_known_hosts.py +++ b/thefuck/rules/ssh_known_hosts.py @@ -5,7 +5,7 @@ commands = ('ssh', 'scp') @for_app(*commands) -def match(command, settings): +def match(command): if not command.script: return False if not command.script.startswith(commands): @@ -20,11 +20,11 @@ def match(command, settings): return any(re.findall(pattern, command.stderr) for pattern in patterns) -def get_new_command(command, settings): +def get_new_command(command): return command.script -def side_effect(old_cmd, command, settings): +def side_effect(old_cmd, command): offending_pattern = re.compile( r'(?:Offending (?:key for IP|\S+ key)|Matching host key) in ([^:]+):(\d+)', re.MULTILINE) diff --git a/thefuck/rules/sudo.py b/thefuck/rules/sudo.py index 63e53c9f..6c5e8052 100644 --- a/thefuck/rules/sudo.py +++ b/thefuck/rules/sudo.py @@ -19,7 +19,7 @@ patterns = ['permission denied', 'authentication is required'] -def match(command, settings): +def match(command): for pattern in patterns: if pattern.lower() in command.stderr.lower()\ or pattern.lower() in command.stdout.lower(): @@ -27,7 +27,7 @@ def match(command, settings): return False -def get_new_command(command, settings): +def get_new_command(command): if '>' in command.script: return u'sudo sh -c "{}"'.format(command.script.replace('"', '\\"')) else: diff --git a/thefuck/rules/switch_lang.py b/thefuck/rules/switch_lang.py index 2ecd6325..8952eb05 100644 --- a/thefuck/rules/switch_lang.py +++ b/thefuck/rules/switch_lang.py @@ -28,7 +28,7 @@ def _switch_command(command, layout): return ''.join(_switch(ch, layout) for ch in command.script) -def match(command, settings): +def match(command): if 'not found' not in command.stderr: return False matched_layout = _get_matched_layout(command) @@ -36,6 +36,6 @@ def match(command, settings): _switch_command(command, matched_layout) != thefuck_alias() -def get_new_command(command, settings): +def get_new_command(command): matched_layout = _get_matched_layout(command) return _switch_command(command, matched_layout) diff --git a/thefuck/rules/systemctl.py b/thefuck/rules/systemctl.py index 3edca9fc..771206d7 100644 --- a/thefuck/rules/systemctl.py +++ b/thefuck/rules/systemctl.py @@ -7,7 +7,7 @@ from thefuck.utils import for_app @sudo_support @for_app('systemctl') -def match(command, settings): +def match(command): # Catches 'Unknown operation 'service'.' when executing systemctl with # misordered arguments cmd = command.script.split() @@ -16,7 +16,7 @@ def match(command, settings): @sudo_support -def get_new_command(command, settings): +def get_new_command(command): cmd = command.script.split() cmd[-1], cmd[-2] = cmd[-2], cmd[-1] return ' '.join(cmd) diff --git a/thefuck/rules/test.py.py b/thefuck/rules/test.py.py index 4d232db2..82810a80 100644 --- a/thefuck/rules/test.py.py +++ b/thefuck/rules/test.py.py @@ -1,8 +1,8 @@ -def match(command, settings): +def match(command): return command.script == 'test.py' and 'not found' in command.stderr -def get_new_command(command, settings): +def get_new_command(command): return 'py.test' diff --git a/thefuck/rules/tmux.py b/thefuck/rules/tmux.py index 09acb578..0aeaee28 100644 --- a/thefuck/rules/tmux.py +++ b/thefuck/rules/tmux.py @@ -3,12 +3,12 @@ from thefuck.utils import replace_command, for_app @for_app('tmux') -def match(command, settings): +def match(command): return ('ambiguous command:' in command.stderr and 'could be:' in command.stderr) -def get_new_command(command, settings): +def get_new_command(command): cmd = re.match(r"ambiguous command: (.*), could be: (.*)", command.stderr) diff --git a/thefuck/rules/tsuru_login.py b/thefuck/rules/tsuru_login.py index fc917159..ce4de2dd 100644 --- a/thefuck/rules/tsuru_login.py +++ b/thefuck/rules/tsuru_login.py @@ -3,10 +3,10 @@ from thefuck.utils import for_app @for_app('tsuru') -def match(command, settings): +def match(command): return ('not authenticated' in command.stderr and 'session has expired' in command.stderr) -def get_new_command(command, settings): +def get_new_command(command): return shells.and_('tsuru login', command.script) diff --git a/thefuck/rules/tsuru_not_command.py b/thefuck/rules/tsuru_not_command.py index 498a4930..cae67826 100644 --- a/thefuck/rules/tsuru_not_command.py +++ b/thefuck/rules/tsuru_not_command.py @@ -3,12 +3,12 @@ from thefuck.utils import get_all_matched_commands, replace_command, for_app @for_app('tsuru') -def match(command, settings): +def match(command): return (' is not a tsuru command. See "tsuru help".' in command.stderr and '\nDid you mean?\n\t' in command.stderr) -def get_new_command(command, settings): +def get_new_command(command): broken_cmd = re.findall(r'tsuru: "([^"]*)" is not a tsuru command', command.stderr)[0] return replace_command(command, broken_cmd, diff --git a/thefuck/rules/unknown_command.py b/thefuck/rules/unknown_command.py index 3cd9a9a7..c0048cc1 100644 --- a/thefuck/rules/unknown_command.py +++ b/thefuck/rules/unknown_command.py @@ -1,12 +1,12 @@ import re from thefuck.utils import replace_command -def match(command, settings): +def match(command): return (re.search(r"([^:]*): Unknown command.*", command.stderr) != None and re.search(r"Did you mean ([^?]*)?", command.stderr) != None) -def get_new_command(command, settings): +def get_new_command(command): broken_cmd = re.findall(r"([^:]*): Unknown command.*", command.stderr)[0] matched = re.findall(r"Did you mean ([^?]*)?", command.stderr) return replace_command(command, broken_cmd, matched) diff --git a/thefuck/rules/vagrant_up.py b/thefuck/rules/vagrant_up.py index 7830f301..44093972 100644 --- a/thefuck/rules/vagrant_up.py +++ b/thefuck/rules/vagrant_up.py @@ -3,11 +3,11 @@ from thefuck.utils import for_app @for_app('vagrant') -def match(command, settings): +def match(command): return 'run `vagrant up`' in command.stderr.lower() -def get_new_command(command, settings): +def get_new_command(command): cmds = command.script.split(' ') machine = None if len(cmds) >= 3: diff --git a/thefuck/rules/whois.py b/thefuck/rules/whois.py index 79487c38..141c7332 100644 --- a/thefuck/rules/whois.py +++ b/thefuck/rules/whois.py @@ -2,7 +2,7 @@ from six.moves.urllib.parse import urlparse -def match(command, settings): +def match(command): """ What the `whois` command returns depends on the 'Whois server' it contacted and is not consistent through different servers. But there can be only two @@ -22,7 +22,7 @@ def match(command, settings): return 'whois ' in command.script.strip() -def get_new_command(command, settings): +def get_new_command(command): url = command.script.split()[1] if '/' in command.script: diff --git a/thefuck/specific/git.py b/thefuck/specific/git.py index b8420573..7b423f2c 100644 --- a/thefuck/specific/git.py +++ b/thefuck/specific/git.py @@ -6,7 +6,7 @@ from ..utils import quote, is_app @decorator -def git_support(fn, command, settings): +def git_support(fn, command): """Resolves git aliases and supports testing for both git and hub.""" # supports GitHub's `hub` command # which is recommended to be used with `alias git=hub` @@ -29,4 +29,4 @@ def git_support(fn, command, settings): command = Command._replace(command, script=new_script) - return fn(command, settings) + return fn(command) diff --git a/thefuck/specific/sudo.py b/thefuck/specific/sudo.py index 8e5d30cd..63dc2255 100644 --- a/thefuck/specific/sudo.py +++ b/thefuck/specific/sudo.py @@ -4,15 +4,14 @@ from ..types import Command @decorator -def sudo_support(fn, command, settings): +def sudo_support(fn, command): """Removes sudo before calling fn and adds it after.""" if not command.script.startswith('sudo '): - return fn(command, settings) + return fn(command) result = fn(Command(command.script[5:], command.stdout, - command.stderr), - settings) + command.stderr)) if result and isinstance(result, six.string_types): return u'sudo {}'.format(result) diff --git a/thefuck/utils.py b/thefuck/utils.py index 66d6b15b..b533e6e6 100644 --- a/thefuck/utils.py +++ b/thefuck/utils.py @@ -1,6 +1,7 @@ from difflib import get_close_matches from functools import wraps import shelve +from warnings import warn from decorator import decorator from contextlib import closing import tempfile @@ -8,12 +9,12 @@ import tempfile import os import pickle import re +from inspect import getargspec from pathlib import Path import pkg_resources import six -from .types import Settings - +from .conf import settings DEVNULL = open(os.devnull, 'w') @@ -70,11 +71,11 @@ def default_settings(params): print(settings.apt) """ - def _wrap_settings(fn, command, settings): + def _default_settings(fn, command): for k, w in params.items(): settings.setdefault(k, w) - return fn(command, settings) - return decorator(_wrap_settings) + return fn(command) + return decorator(_default_settings) def get_closest(word, possibilities, n=3, cutoff=0.6, fallback_to_first=True): @@ -156,9 +157,9 @@ def is_app(command, *app_names): def for_app(*app_names): """Specifies that matching script is for on of app names.""" - def _for_app(fn, command, settings): + def _for_app(fn, command): if is_app(command, *app_names): - return fn(command, settings) + return fn(command) else: return False @@ -202,3 +203,24 @@ def cache(*depends_on): return value return _cache cache.disabled = False + + +def compatibility_call(fn, *args): + """Special call for compatibility with user-defined old-style rules + with `settings` param. + + """ + fn_args_count = len(getargspec(fn).args) + if fn.__name__ in ('match', 'get_new_command') and fn_args_count == 2: + warn("Two arguments `{}` from rule `{}` is deprecated, please " + "remove `settings` argument and use " + "`from thefuck.conf import settings` instead." + .format(fn.__name__, fn.__module__)) + args += (settings,) + if fn.__name__ == 'side_effect' and fn_args_count == 3: + warn("Three arguments `side_effect` from rule `{}` is deprecated, " + "please remove `settings` argument and use `from thefuck.conf " + "import settings` instead." + .format(fn.__name__, fn.__module__)) + args += (settings,) + return fn(*args) From f3525e9fe03b5cf1bfeb3b54af2461ecdaeb4d78 Mon Sep 17 00:00:00 2001 From: nvbn Date: Mon, 7 Sep 2015 18:59:10 +0300 Subject: [PATCH 12/14] #364 Attach `user_dir` to settings --- tests/conftest.py | 8 +++++++- tests/test_corrector.py | 6 +++--- thefuck/conf.py | 2 ++ thefuck/corrector.py | 8 ++++---- thefuck/main.py | 2 +- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index e54acfc0..2579594e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,3 +1,4 @@ +from pathlib import Path import pytest from thefuck import conf @@ -16,7 +17,12 @@ def no_memoize(monkeypatch): @pytest.fixture(autouse=True) def settings(request): - request.addfinalizer(lambda: conf.settings.update(conf.DEFAULT_SETTINGS)) + def _reset_settings(): + conf.settings.clear() + conf.settings.update(conf.DEFAULT_SETTINGS) + + request.addfinalizer(_reset_settings) + conf.settings.user_dir = Path('~/.thefuck') return conf.settings diff --git a/tests/test_corrector.py b/tests/test_corrector.py index 61a6d06b..f6bf2c7c 100644 --- a/tests/test_corrector.py +++ b/tests/test_corrector.py @@ -25,7 +25,7 @@ class TestGetRules(object): @pytest.fixture def glob(self, mocker): results = {} - mocker.patch('thefuck.corrector.Path.glob', + mocker.patch('pathlib.Path.glob', new_callable=lambda: lambda *_: results.pop('value', [])) return lambda value: results.update({'value': value}) @@ -54,7 +54,7 @@ class TestGetRules(object): settings.update(rules=self._prepare_rules(conf_rules), priority={}, exclude_rules=self._prepare_rules(exclude_rules)) - rules = corrector.get_rules(Path('~')) + rules = corrector.get_rules() self._compare_names(rules, loaded_rules) @@ -98,5 +98,5 @@ def test_get_corrected_commands(mocker): get_new_command=lambda x: [x.script + '@', x.script + ';'], priority=60)] mocker.patch('thefuck.corrector.get_rules', return_value=rules) - assert [cmd.script for cmd in get_corrected_commands(command, None)] \ + assert [cmd.script for cmd in get_corrected_commands(command)] \ == ['test!', 'test@', 'test;'] diff --git a/thefuck/conf.py b/thefuck/conf.py index 2b8178ba..bd7e476b 100644 --- a/thefuck/conf.py +++ b/thefuck/conf.py @@ -109,6 +109,8 @@ def init_settings(user_dir): """Fills `settings` with values from `settings.py` and env.""" from .logs import exception + settings.user_dir = user_dir + try: settings.update(_settings_from_file(user_dir)) except Exception: diff --git a/thefuck/corrector.py b/thefuck/corrector.py index 14257684..b9433624 100644 --- a/thefuck/corrector.py +++ b/thefuck/corrector.py @@ -31,12 +31,12 @@ def get_loaded_rules(rules): yield loaded_rule -def get_rules(user_dir): +def get_rules(): """Returns all enabled rules.""" bundled = Path(__file__).parent \ .joinpath('rules') \ .glob('*.py') - user = user_dir.joinpath('rules').glob('*.py') + user = settings.user_dir.joinpath('rules').glob('*.py') return sorted(get_loaded_rules(sorted(bundled) + sorted(user)), key=lambda rule: rule.priority) @@ -66,9 +66,9 @@ def make_corrected_commands(command, rule): priority=(n + 1) * rule.priority) -def get_corrected_commands(command, user_dir): +def get_corrected_commands(command): corrected_commands = ( - corrected for rule in get_rules(user_dir) + corrected for rule in get_rules() if is_rule_match(command, rule) for corrected in make_corrected_commands(command, rule)) return SortedCorrectedCommandsSequence(corrected_commands) diff --git a/thefuck/main.py b/thefuck/main.py index 497d97e7..541ac928 100644 --- a/thefuck/main.py +++ b/thefuck/main.py @@ -98,7 +98,7 @@ def fix_command(): logs.debug('Empty command, nothing to do') return - corrected_commands = get_corrected_commands(command, user_dir) + corrected_commands = get_corrected_commands(command) selected_command = select_command(corrected_commands) if selected_command: run_command(command, selected_command) From 7339a97b90a4cdf86a5784428ad683ce718b2397 Mon Sep 17 00:00:00 2001 From: nvbn Date: Mon, 7 Sep 2015 19:05:38 +0300 Subject: [PATCH 13/14] #364 Make aliases cache persistent --- thefuck/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thefuck/utils.py b/thefuck/utils.py index b533e6e6..0292cb76 100644 --- a/thefuck/utils.py +++ b/thefuck/utils.py @@ -187,7 +187,7 @@ def cache(*depends_on): if cache.disabled: return fn(*args, **kwargs) - cache_path = os.path.join(tempfile.gettempdir(), '.thefuck-cache') + cache_path = settings.user_dir.joinpath('.thefuck-cache').as_posix() # A bit obscure, but simplest way to generate unique key for # functions and methods in python 2 and 3: key = '{}.{}'.format(fn.__module__, repr(fn).split('at')[0]) From 3d56204f92255827bdef257cf2b5a79bdd9947fc Mon Sep 17 00:00:00 2001 From: nvbn Date: Mon, 7 Sep 2015 19:13:17 +0300 Subject: [PATCH 14/14] Update readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f24018eb..ebb12c1b 100644 --- a/README.md +++ b/README.md @@ -245,18 +245,18 @@ and optional `enabled_by_default`, `requires_output` and `priority` variables. Simple example of the rule for running script with `sudo`: ```python -def match(command, settings): +def match(command): return ('permission denied' in command.stderr.lower() or 'EACCES' in command.stderr) -def get_new_command(command, settings): +def get_new_command(command): return 'sudo {}'.format(command.script) # Optional: enabled_by_default = True -def side_effect(command, settings): +def side_effect(command, fixed_command): subprocess.call('chmod 777 .', shell=True) priority = 1000 # Lower first, default is 1000