1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-11-02 08:02:04 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
nvbn
0fcc35c227 #380 merge cd_mkdir and cd_correction rules 2015-10-12 17:52:44 +08:00
149 changed files with 1243 additions and 2664 deletions

View File

@@ -5,23 +5,23 @@ python:
- "3.4"
- "3.3"
- "2.7"
services:
- docker
addons:
apt:
sources:
- fish-shell/release-2
packages:
- python-commandnotfound
- python3-commandnotfound
- bash
- zsh
- fish
- tcsh
- pandoc
- git
install:
- pip install -U pip
- pip install -U coveralls
- pip install -Ur requirements.txt
- pip install coveralls
- pip install -r requirements.txt
- python setup.py develop
- rm -rf build
script:
- export COVERAGE_PYTHON_VERSION=python-${TRAVIS_PYTHON_VERSION:0:1}
- export RUN_TESTS="coverage run --source=thefuck,tests -m py.test -v --capture=sys"
- if [[ $TRAVIS_PYTHON_VERSION == 3.5 ]]; then $RUN_TESTS --enable-functional; fi
- if [[ $TRAVIS_PYTHON_VERSION != 3.5 ]]; then $RUN_TESTS; fi
after_success:
- coveralls
- coverage run --source=thefuck,tests -m py.test -v --capture=sys --run-without-docker --enable-functional
after_success: coveralls

View File

@@ -1,25 +0,0 @@
# Report issues
If you have any issue with The Fuck, sorry about that, but we will do what we
can to fix that. Actually, maybe we already have, so first thing to do is to
update The Fuck and see if the bug is still there.
If it is (sorry again), check if the problem has not already been reported and
if not, just open an issue on [GitHub](https://github.com/nvbn/thefuck) with
the following basic information:
- the output of `thefuck --version` (something like `The Fuck 3.1 using
Python 3.5.0`);
- your shell and its version (`bash`, `zsh`, *Windows PowerShell*, etc.);
- your system (Debian 7, ArchLinux, Windows, etc.);
- how to reproduce the bug;
- the output of The Fuck with `THEFUCK_DEBUG=true` exported (typically execute
`export THEFUCK_DEBUG=true` in your shell before The Fuck);
- if the bug only appears with a specific application, the output of that
application and its version;
- anything else you think is relevant.
It's only with enough information that we can do something to fix the problem.
# Make a pull request
We gladly accept pull request on the [official
repository](https://github.com/nvbn/thefuck) for new rules, new features, bug
fixes, etc.

View File

@@ -1,10 +1,10 @@
# The Fuck [![Version][version-badge]][version-link] [![Build Status][travis-badge]][travis-link] [![Windows Build Status][appveyor-badge]][appveyor-link] [![Coverage][coverage-badge]][coverage-link] [![MIT License][license-badge]](LICENSE.md)
# The Fuck [![Build Status](https://travis-ci.org/nvbn/thefuck.svg?branch=master)](https://travis-ci.org/nvbn/thefuck)
Magnificent app which corrects your previous console command,
inspired by a [@liamosaur](https://twitter.com/liamosaur/)
[tweet](https://twitter.com/liamosaur/status/506975850596536320).
[![gif with examples][examples-link]][examples-link]
[![gif with examples](https://raw.githubusercontent.com/nvbn/thefuck/master/example.gif)](https://raw.githubusercontent.com/nvbn/thefuck/master/example.gif)
Few more examples:
@@ -94,26 +94,20 @@ Reading package lists... Done
- pip
- python-dev
## Installation
## Installation [*experimental*]
On OS X you can install `The Fuck` with [Homebrew][homebrew]:
On Ubuntu and OS X you can install `The Fuck` with installation script:
```bash
brew install thefuck
wget -O - https://raw.githubusercontent.com/nvbn/thefuck/master/install.sh | sh - && $0
```
On Ubuntu you can install `The Fuck` with:
## Manual installation
Install `The Fuck` with `pip`:
```bash
sudo apt update
sudo apt install python3-dev python3-pip
sudo -H pip3 install thefuck
```
On other systems you can install `The Fuck` with `pip`:
```bash
sudo -H pip install thefuck
sudo pip install thefuck
```
[Or using an OS package manager (OS X, Ubuntu, Arch).](https://github.com/nvbn/thefuck/wiki/Installation)
@@ -147,10 +141,9 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `cargo` – runs `cargo build` instead of `cargo`;
* `cargo_no_command` – fixes wrongs commands like `cargo buid`;
* `cd_correction` – spellchecks and correct failed cd commands;
* `cd_mkdir` – creates directories before cd'ing into them;
* `cd_correction` – spellchecks and correct failed cd commands, when it's not possible
creates directories before cd'ing into them;
* `cd_parent` – changes `cd..` to `cd ..`;
* `chmod_x` – add execution bit;
* `composer_not_command` – fixes composer command name;
* `cp_omitting_directory` – adds `-a` when you `cp` directory;
* `cpp11` – adds missing `-std=c++11` to `g++` or `clang++`;
@@ -162,26 +155,19 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `dry` – fixes repetitions like `git git push`;
* `fix_alt_space` – replaces Alt+Space with Space character;
* `fix_file` – opens a file with an error in your `$EDITOR`;
* `git_add` – fixes *"pathspec 'foo' did not match any file(s) known to git."*;
* `git_add` – fixes *"Did you forget to 'git add'?"*;
* `git_branch_delete` – changes `git branch -d` to `git branch -D`;
* `git_branch_exists` – offers `git branch -d foo`, `git branch -D foo` or `git checkout foo` when creating a branch that already exists;
* `git_branch_list` – catches `git branch list` in place of `git branch` and removes created branch;
* `git_checkout` – fixes branch name or creates new branch;
* `git_diff_staged` – adds `--staged` to previous `git diff` with unexpected output;
* `git_fix_stash` – fixes `git stash` commands (misspelled subcommand and missing `save`);
* `git_help_aliased` &ndash; fixes `git help <alias>` commands replacing <alias> with the aliased command;
* `git_not_command` &ndash; fixes wrong git commands like `git brnch`;
* `git_pull` &ndash; sets upstream before executing previous `git pull`;
* `git_pull_clone` &ndash; clones instead of pulling when the repo does not exist;
* `git_push` &ndash; adds `--set-upstream origin $branch` to previous failed `git push`;
* `git_push_pull` &ndash; runs `git pull` when `push` was rejected;
* `git_rebase_no_changes` &ndash; runs `git rebase --skip` instead of `git rebase --continue` when there are no changes;
* `git_rm_recursive` &ndash; adds `-r` when you try to `rm` a directory;
* `git_remote_seturl_add` &ndash; runs `git remote add` when `git remote set_url` on nonexistant remote;
* `git_stash` &ndash; stashes you local modifications before rebasing or switching branch;
* `git_two_dashes` &ndash; adds a missing dash to commands like `git commit -amend` or `git rebase -continue`;
* `go_run` &ndash; appends `.go` extension when compiling/running Go programs;
* `grep_arguments_order` &ndash; fixes grep arguments order for situations like `grep -lir . test`;
* `go_run` &ndash; appends `.go` extension when compiling/running Go programs
* `grep_recursive` &ndash; adds `-r` when you trying to `grep` directory;
* `gulp_not_task` &ndash; fixes misspelled `gulp` tasks;
* `has_exists_script` &ndash; prepends `./` when script/binary exists;
@@ -190,8 +176,6 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `java` &ndash; removes `.java` extension when running Java programs;
* `javac` &ndash; appends missing `.java` when compiling Java files;
* `lein_not_task` &ndash; fixes wrong `lein` tasks like `lein rpl`;
* `ln_no_hard_link` &ndash; catches hard link creation on directories, suggest symbolic link;
* `ln_s_order` &ndash; fixes `ln -s` arguments order;
* `ls_lah` &ndash; adds `-lah` to `ls`;
* `man` &ndash; changes manual section;
* `man_no_space` &ndash; fixes man commands without spaces, for example `mandiff`;
@@ -199,7 +183,6 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `mkdir_p` &ndash; adds `-p` when you trying to create directory without parent;
* `mvn_no_command` &ndash; adds `clean package` to `mvn`;
* `mvn_unknown_lifecycle_phase` &ndash; fixes misspelled lifecycle phases with `mvn`;
* `npm_wrong_command` &ndash; fixes wrong npm commands like `npm urgrade`;
* `no_command` &ndash; fixes wrong console commands, for example `vom/vim`;
* `no_such_file` &ndash; creates missing directories with `mv` and `cp` commands;
* `open` &ndash; prepends `http` to address passed to `open`;
@@ -227,23 +210,21 @@ Enabled by default only on specific platforms:
* `apt_get` &ndash; installs app from apt if it not installed (requires `python-commandnotfound` / `python3-commandnotfound`);
* `apt_get_search` &ndash; changes trying to search using `apt-get` with searching using `apt-cache`;
* `apt_invalid_operation` &ndash; fixes invalid `apt` and `apt-get` calls, like `apt-get isntall vim`;
* `brew_install` &ndash; fixes formula name for `brew install`;
* `brew_unknown_command` &ndash; fixes wrong brew commands, for example `brew docto/brew doctor`;
* `brew_update_formula` &ndash; turns `brew update <formula>` into `brew upgrade <formula>`;
* `brew_upgrade` &ndash; appends `--all` to `brew upgrade` as per Homebrew's new behaviour;
* `pacman` &ndash; installs app with `pacman` if it is not installed (uses `yaourt` if available);
* `pacman_not_found` &ndash; fixes package name with `pacman` or `yaourt`.
Bundled, but not enabled by default:
* `git_push_force` &ndash; adds `--force-with-lease` to a `git push` (may conflict with `git_push_pull`);
* `git_push_force` &ndash; adds `--force` to a `git push` (may conflict with `git_push_pull`);
* `rm_root` &ndash; adds `--no-preserve-root` to `rm -rf /` command.
## Creating your own rules
For adding your own rule you should create `your-rule-name.py`
in `~/.config/thefuck/rules`. The rule should contain two functions:
in `~/.thefuck/rules`. The rule should contain two functions:
```python
match(command: Command) -> bool
@@ -260,7 +241,7 @@ and optional `enabled_by_default`, `requires_output` and `priority` variables.
`Command` has three attributes: `script`, `stdout` and `stderr`.
*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 `~/.config/thefuck/settings.py` and values from env ([see more below](#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`:
@@ -290,7 +271,7 @@ requires_output = True
## Settings
The Fuck has a few settings parameters which can be changed in `$XDG_CONFIG_HOME/thefuck/settings.py` (`$XDG_CONFIG_HOME` defaults to `~/.config`):
The Fuck has a few settings parameters which can be changed in `~/.thefuck/settings.py`:
* `rules` &ndash; list of enabled rules, by default `thefuck.conf.DEFAULT_RULES`;
* `exclude_rules` &ndash; list of disabled rules, by default `[]`;
@@ -298,9 +279,7 @@ The Fuck has a few settings parameters which can be changed in `$XDG_CONFIG_HOME
* `wait_command` &ndash; max amount of time in seconds for getting previous command output;
* `no_colors` &ndash; disable colored output;
* `priority` &ndash; dict with rules priorities, rule with lower `priority` will be matched first;
* `debug` &ndash; enables debug output, by default `False`;
* `history_limit` &ndash; numeric value of how many history commands will be scanned, like `2000`;
* `alter_history` &ndash; push fixed command to history, by default `True`.
* `debug` &ndash; enables debug output, by default `False`.
Example of `settings.py`:
@@ -323,9 +302,7 @@ Or via environment variables:
* `THEFUCK_NO_COLORS` &ndash; disable colored output, `true/false`;
* `THEFUCK_PRIORITY` &ndash; priority of the rules, like `no_command=9999:apt_get=100`,
rule with lower `priority` will be matched first;
* `THEFUCK_DEBUG` &ndash; enables debug output, `true/false`;
* `THEFUCK_HISTORY_LIMIT` &ndash; how many history commands will be scanned, like `2000`;
* `THEFUCK_ALTER_HISTORY` &ndash; push fixed command to history `true/false`.
* `THEFUCK_DEBUG` &ndash; enables debug output, `true/false`.
For example:
@@ -336,7 +313,6 @@ export THEFUCK_REQUIRE_CONFIRMATION='true'
export THEFUCK_WAIT_COMMAND=10
export THEFUCK_NO_COLORS='false'
export THEFUCK_PRIORITY='no_command=9999:apt_get=100'
export THEFUCK_HISTORY_LIMIT='2000'
```
## Developing
@@ -369,16 +345,3 @@ sudo apt-get install pandoc
## License MIT
Project License can be found [here](LICENSE.md).
[version-badge]: https://img.shields.io/pypi/v/thefuck.svg?label=version
[version-link]: https://pypi.python.org/pypi/thefuck/
[travis-badge]: https://img.shields.io/travis/nvbn/thefuck.svg
[travis-link]: https://travis-ci.org/nvbn/thefuck
[appveyor-badge]: https://img.shields.io/appveyor/ci/nvbn/thefuck.svg?label=windows%20build
[appveyor-link]: https://ci.appveyor.com/project/nvbn/thefuck
[coverage-badge]: https://img.shields.io/coveralls/nvbn/thefuck.svg
[coverage-link]: https://coveralls.io/github/nvbn/thefuck
[license-badge]: https://img.shields.io/badge/license-MIT-007EC7.svg
[examples-link]: https://raw.githubusercontent.com/nvbn/thefuck/master/example.gif
[homebrew]: http://brew.sh/

View File

@@ -1,22 +0,0 @@
build: false
environment:
matrix:
- PYTHON: "C:/Python27"
- PYTHON: "C:/Python33"
- PYTHON: "C:/Python34"
- PYTHON: "C:/Python35"
init:
- "ECHO %PYTHON%"
- ps: "ls C:/Python*"
install:
- ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:/get-pip.py')
- "%PYTHON%/python.exe C:/get-pip.py"
- "%PYTHON%/Scripts/pip.exe install -U setuptools"
- "%PYTHON%/python.exe setup.py develop"
- "%PYTHON%/Scripts/pip.exe install -U -r requirements.txt"
test_script:
- "%PYTHON%/Scripts/py.test.exe -sv"

View File

@@ -1,4 +1,57 @@
#!/bin/sh
echo "Installation script is deprecated!"
echo "For installation instruction please visit https://github.com/nvbn/thefuck"
should_add_alias () {
[ -f $1 ] && ! grep -q thefuck $1
}
installed () {
hash $1 2>/dev/null
}
# Install os dependencies:
if installed apt-get; then
# Debian/ubuntu:
sudo apt-get update -yy
sudo apt-get install -yy python-pip python-dev command-not-found
if [[ -n $(apt-cache search python-commandnotfound) ]]; then
# In case of different python versions:
sudo apt-get install -yy python-commandnotfound
fi
else
if installed brew; then
# OS X:
brew update
brew install python
else
# Genreic way:
wget https://bootstrap.pypa.io/get-pip.py
sudo python get-pip.py
rm get-pip.py
fi
fi
# thefuck requires fresh versions of setuptools and pip:
sudo pip install -U pip setuptools
sudo pip install -U thefuck
# Setup aliases:
if should_add_alias ~/.bashrc; then
echo 'eval $(thefuck --alias)' >> ~/.bashrc
fi
if should_add_alias ~/.bash_profile; then
echo 'eval $(thefuck --alias)' >> ~/.bash_profile
fi
if should_add_alias ~/.zshrc; then
echo 'eval $(thefuck --alias)' >> ~/.zshrc
fi
if should_add_alias ~/.config/fish/config.fish; then
thefuck --alias >> ~/.config/fish/config.fish
fi
if should_add_alias ~/.tcshrc; then
echo 'eval `thefuck --alias`' >> ~/.tcshrc
fi

View File

@@ -1,4 +1,3 @@
pip
pytest
mock
pytest-mock

View File

@@ -1,14 +1,8 @@
#!/usr/bin/env python
from setuptools import setup, find_packages
import pip
import sys
import os
if int(pip.__version__.split('.')[0]) < 6:
print('pip older than 6.0 not supported, please upgrade pip with:\n\n'
' pip install -U pip')
sys.exit(-1)
if os.environ.get('CONVERT_README'):
import pypandoc
@@ -26,11 +20,10 @@ elif (3, 0) < version < (3, 3):
' ({}.{} detected).'.format(*version))
sys.exit(-1)
VERSION = '3.10'
VERSION = '3.1'
install_requires = ['psutil', 'colorama', 'six', 'decorator']
extras_require = {':python_version<"3.4"': ['pathlib2'],
":sys_platform=='win32'": ['win_unicode_console']}
extras_require = {':python_version<"3.4"': ['pathlib']}
setup(name='thefuck',
version=VERSION,
@@ -41,7 +34,7 @@ setup(name='thefuck',
url='https://github.com/nvbn/thefuck',
license='MIT',
packages=find_packages(exclude=['ez_setup', 'examples',
'tests', 'tests.*', 'release']),
'tests', 'release']),
include_package_data=True,
zip_safe=False,
install_requires=install_requires,

View File

@@ -1,12 +1,6 @@
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
import pytest
from thefuck import shells
from thefuck import conf, const
shells.shell = shells.Generic()
from thefuck import conf
def pytest_addoption(parser):
@@ -25,7 +19,7 @@ def no_memoize(monkeypatch):
def settings(request):
def _reset_settings():
conf.settings.clear()
conf.settings.update(const.DEFAULT_SETTINGS)
conf.settings.update(conf.DEFAULT_SETTINGS)
request.addfinalizer(_reset_settings)
conf.settings.user_dir = Path('~/.thefuck')
@@ -52,14 +46,3 @@ def functional(request):
@pytest.fixture
def source_root():
return Path(__file__).parent.parent.resolve()
@pytest.fixture
def set_shell(monkeypatch, request):
def _set(cls):
shell = cls()
monkeypatch.setattr('thefuck.shells.shell', shell)
request.addfinalizer()
return shell
return _set

View File

@@ -29,25 +29,25 @@ def proc(request, spawnu):
@pytest.mark.functional
@pytest.mark.once_without_docker
@pytest.mark.skip_without_docker
def test_with_confirmation(proc, TIMEOUT):
with_confirmation(proc, TIMEOUT)
@pytest.mark.functional
@pytest.mark.once_without_docker
@pytest.mark.skip_without_docker
def test_select_command_with_arrows(proc, TIMEOUT):
select_command_with_arrows(proc, TIMEOUT)
@pytest.mark.functional
@pytest.mark.once_without_docker
@pytest.mark.skip_without_docker
def test_refuse_with_confirmation(proc, TIMEOUT):
refuse_with_confirmation(proc, TIMEOUT)
@pytest.mark.functional
@pytest.mark.once_without_docker
@pytest.mark.skip_without_docker
def test_without_confirmation(proc, TIMEOUT):
without_confirmation(proc, TIMEOUT)

View File

@@ -0,0 +1,25 @@
import pytest
from thefuck.utils import get_installation_info
envs = ((u'bash', 'thefuck/ubuntu-bash', u'''
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -yy bash
'''), (u'bash', 'thefuck/generic-bash', u'''
FROM fedora:latest
RUN dnf install -yy python-devel sudo wget gcc
'''))
@pytest.mark.functional
@pytest.mark.skip_without_docker
@pytest.mark.parametrize('shell, tag, dockerfile', envs)
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')
version = get_installation_info().version
assert proc.expect([TIMEOUT, u'thefuck {}'.format(version)],
timeout=600)
proc.sendline(u'fuck')
assert proc.expect([TIMEOUT, u'No fucks given'])

View File

@@ -40,7 +40,7 @@ def plot(proc, TIMEOUT):
@pytest.mark.functional
@pytest.mark.once_without_docker
@pytest.mark.skip_without_docker
@pytest.mark.benchmark(min_rounds=10)
def test_performance(spawnu, TIMEOUT, benchmark):
proc = spawnu(u'thefuck/ubuntu-python3-bash-performance',

View File

@@ -25,7 +25,6 @@ def proc(request, spawnu, run_without_docker):
if not run_without_docker:
proc.sendline(u'pip install /src')
proc.sendline(u'tcsh')
proc.sendline(u'setenv PYTHONIOENCODING utf8')
proc.sendline(u'eval `thefuck --alias`')
return proc

6
tests/rules/conftest.py Normal file
View File

@@ -0,0 +1,6 @@
import pytest
@pytest.fixture(autouse=True)
def generic_shell(monkeypatch):
monkeypatch.setattr('thefuck.shells.and_', lambda *x: ' && '.join(x))

View File

@@ -29,27 +29,12 @@ def test_match_mocked(cmdnf_mock, command, return_value):
assert get_packages.called
# python-commandnotfound is available in ubuntu 14.04+
@pytest.mark.skipif(not getattr(apt_get, 'enabled_by_default', True),
reason='Skip if python-commandnotfound is not available')
@pytest.mark.parametrize('command', [
Command(script='a_bad_cmd', stderr='a_bad_cmd: command not found'),
Command(script='vim', stderr=''), Command()])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('command, return_value', [
(Command(script='a_bad_cmd', stderr='a_bad_cmd: command not found'), []),
(Command(script='vim', stderr=''), []), (Command(), [])])
@patch('thefuck.rules.apt_get.CommandNotFound', create=True)
@patch.multiple(apt_get, create=True, apt_get='apt_get')
def test_not_match_mocked(cmdnf_mock, command, return_value):
get_packages = Mock(return_value=return_value)
cmdnf_mock.CommandNotFound.return_value = Mock(getPackages=get_packages)
assert not match(command)
# python-commandnotfound is available in ubuntu 14.04+
@pytest.mark.skipif(not getattr(apt_get, 'enabled_by_default', True),
reason='Skip if python-commandnotfound is not available')

View File

@@ -1,122 +0,0 @@
from io import BytesIO
import pytest
from tests.utils import Command
from thefuck.rules.apt_invalid_operation import match, get_new_command, \
_get_operations
invalid_operation = 'E: Invalid operation {}'.format
apt_help = b'''apt 1.0.10.2ubuntu1 for amd64 compiled on Oct 5 2015 15:55:05
Usage: apt [options] command
CLI for apt.
Basic commands:
list - list packages based on package names
search - search in package descriptions
show - show package details
update - update list of available packages
install - install packages
remove - remove packages
upgrade - upgrade the system by installing/upgrading packages
full-upgrade - upgrade the system by removing/installing/upgrading packages
edit-sources - edit the source information file
'''
apt_operations = ['list', 'search', 'show', 'update', 'install', 'remove',
'upgrade', 'full-upgrade', 'edit-sources']
apt_get_help = b'''apt 1.0.10.2ubuntu1 for amd64 compiled on Oct 5 2015 15:55:05
Usage: apt-get [options] command
apt-get [options] install|remove pkg1 [pkg2 ...]
apt-get [options] source pkg1 [pkg2 ...]
apt-get is a simple command line interface for downloading and
installing packages. The most frequently used commands are update
and install.
Commands:
update - Retrieve new lists of packages
upgrade - Perform an upgrade
install - Install new packages (pkg is libc6 not libc6.deb)
remove - Remove packages
autoremove - Remove automatically all unused packages
purge - Remove packages and config files
source - Download source archives
build-dep - Configure build-dependencies for source packages
dist-upgrade - Distribution upgrade, see apt-get(8)
dselect-upgrade - Follow dselect selections
clean - Erase downloaded archive files
autoclean - Erase old downloaded archive files
check - Verify that there are no broken dependencies
changelog - Download and display the changelog for the given package
download - Download the binary package into the current directory
Options:
-h This help text.
-q Loggable output - no progress indicator
-qq No output except for errors
-d Download only - do NOT install or unpack archives
-s No-act. Perform ordering simulation
-y Assume Yes to all queries and do not prompt
-f Attempt to correct a system with broken dependencies in place
-m Attempt to continue if archives are unlocatable
-u Show a list of upgraded packages as well
-b Build the source package after fetching it
-V Show verbose version numbers
-c=? Read this configuration file
-o=? Set an arbitrary configuration option, eg -o dir::cache=/tmp
See the apt-get(8), sources.list(5) and apt.conf(5) manual
pages for more information and options.
This APT has Super Cow Powers.
'''
apt_get_operations = ['update', 'upgrade', 'install', 'remove', 'autoremove',
'purge', 'source', 'build-dep', 'dist-upgrade',
'dselect-upgrade', 'clean', 'autoclean', 'check',
'changelog', 'download']
@pytest.mark.parametrize('script, stderr', [
('apt', invalid_operation('saerch')),
('apt-get', invalid_operation('isntall')),
('apt-cache', invalid_operation('rumove'))])
def test_match(script, stderr):
assert match(Command(script, stderr=stderr))
@pytest.mark.parametrize('script, stderr', [
('vim', invalid_operation('vim')),
('apt-get', "")])
def test_not_match(script, stderr):
assert not match(Command(script, stderr=stderr))
@pytest.fixture
def set_help(mocker):
mock = mocker.patch('subprocess.Popen')
def _set_text(text):
mock.return_value.stdout = BytesIO(text)
return _set_text
@pytest.mark.parametrize('app, help_text, operations', [
('apt', apt_help, apt_operations),
('apt-get', apt_get_help, apt_get_operations)
])
def test_get_operations(set_help, app, help_text, operations):
set_help(help_text)
assert _get_operations(app) == operations
@pytest.mark.parametrize('script, stderr, help_text, result', [
('apt-get isntall vim', invalid_operation('isntall'),
apt_get_help, 'apt-get install vim'),
('apt saerch vim', invalid_operation('saerch'),
apt_help, 'apt search vim'),
])
def test_get_new_command(set_help, stderr, script, help_text, result):
set_help(help_text)
assert get_new_command(Command(script, stderr=stderr))[0] == result

View File

@@ -31,7 +31,8 @@ def test_match(brew_no_available_formula, brew_already_installed,
stderr=brew_no_available_formula))
assert not match(Command('brew install git',
stderr=brew_already_installed))
assert not match(Command('brew install', stderr=brew_install_no_argument))
assert not match(Command('brew install', stderr=brew_install_no_argument),
None)
@pytest.mark.skipif(_is_not_okay_to_test(),
@@ -42,5 +43,5 @@ def test_get_new_command(brew_no_available_formula):
== 'brew install elasticsearch'
assert get_new_command(Command('brew install aa',
stderr=brew_no_available_formula))\
!= 'brew install aha'
stderr=brew_no_available_formula),
None) != 'brew install aha'

View File

@@ -1,30 +0,0 @@
import pytest
from tests.utils import Command
from thefuck.rules.brew_update_formula import get_new_command, match
@pytest.fixture
def stderr():
return ("Error: This command updates brew itself, and does not take formula"
" names.\nUse 'brew upgrade <formula>'.")
@pytest.fixture
def new_command(formula):
return 'brew upgrade {}'.format(formula)
@pytest.mark.parametrize('script', ['brew update foo', 'brew update bar zap'])
def test_match(stderr, script):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script', ['brew upgrade foo', 'brew update'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('script, formula, ', [
('brew update foo', 'foo'), ('brew update bar zap', 'bar zap')])
def test_get_new_command(stderr, new_command, script, formula):
assert get_new_command(Command(script=script, stderr=stderr)) == new_command

View File

@@ -1,5 +1,5 @@
import pytest
from thefuck.rules.cd_mkdir import match, get_new_command
from thefuck.rules.cd_correction import match, get_new_command
from tests.utils import Command

View File

@@ -1,39 +0,0 @@
import pytest
from tests.utils import Command
from thefuck.rules.chmod_x import match, get_new_command
@pytest.fixture
def file_exists(mocker):
return mocker.patch('os.path.exists', return_value=True)
@pytest.fixture
def file_access(mocker):
return mocker.patch('os.access', return_value=False)
@pytest.mark.usefixtures('file_exists', 'file_access')
@pytest.mark.parametrize('script, stderr', [
('./gradlew build', 'gradlew: Permission denied'),
('./install.sh --help', 'install.sh: permission denied')])
def test_match(script, stderr):
assert match(Command(script, stderr=stderr))
@pytest.mark.parametrize('script, stderr, exists, callable', [
('./gradlew build', 'gradlew: Permission denied', True, True),
('./gradlew build', 'gradlew: Permission denied', False, False),
('./gradlew build', 'gradlew: error', True, False),
('gradlew build', 'gradlew: Permission denied', True, False)])
def test_not_match(file_exists, file_access, script, stderr, exists, callable):
file_exists.return_value = exists
file_access.return_value = callable
assert not match(Command(script, stderr=stderr))
@pytest.mark.parametrize('script, result', [
('./gradlew build', 'chmod +x gradlew && ./gradlew build'),
('./install.sh --help', 'chmod +x install.sh && ./install.sh --help')])
def test_get_new_command(script, result):
assert get_new_command(Command(script)) == result

View File

@@ -1,8 +1,7 @@
import os
import pytest
import tarfile
from thefuck.rules.dirty_untar import match, get_new_command, side_effect, \
tar_extensions
from thefuck.rules.dirty_untar import match, get_new_command, side_effect
from tests.utils import Command
@@ -33,41 +32,34 @@ def tar_error(tmpdir):
return fixture
parametrize_extensions = pytest.mark.parametrize('ext', tar_extensions)
# (filename as typed by the user, unquoted filename, quoted filename as per shells.quote)
parametrize_filename = pytest.mark.parametrize('filename, unquoted, quoted', [
('foo{}', 'foo{}', 'foo{}'),
('foo\ bar{}', 'foo bar{}', "'foo bar{}'"),
('"foo bar{}"', 'foo bar{}', "'foo bar{}'")])
parametrize_filename = pytest.mark.parametrize('filename', [
'foo.tar',
'foo.tar.gz',
'foo.tgz'])
parametrize_script = pytest.mark.parametrize('script, fixed', [
('tar xvf {}', 'mkdir -p {dir} && tar xvf {filename} -C {dir}'),
('tar -xvf {}', 'mkdir -p {dir} && tar -xvf {filename} -C {dir}'),
('tar --extract -f {}', 'mkdir -p {dir} && tar --extract -f {filename} -C {dir}')])
('tar xvf {}', 'mkdir -p foo && tar xvf {} -C foo'),
('tar -xvf {}', 'mkdir -p foo && tar -xvf {} -C foo'),
('tar --extract -f {}', 'mkdir -p foo && tar --extract -f {} -C foo')])
@parametrize_extensions
@parametrize_filename
@parametrize_script
def test_match(ext, tar_error, filename, unquoted, quoted, script, fixed):
tar_error(unquoted.format(ext))
assert match(Command(script=script.format(filename.format(ext))))
def test_match(tar_error, filename, script, fixed):
tar_error(filename)
assert match(Command(script=script.format(filename)))
@parametrize_extensions
@parametrize_filename
@parametrize_script
def test_side_effect(ext, tar_error, filename, unquoted, quoted, script, fixed):
tar_error(unquoted.format(ext))
side_effect(Command(script=script.format(filename.format(ext))), None)
assert set(os.listdir('.')) == {unquoted.format(ext), 'd'}
def test_side_effect(tar_error, filename, script, fixed):
tar_error(filename)
side_effect(Command(script=script.format(filename)), None)
assert set(os.listdir('.')) == {filename, 'd'}
@parametrize_extensions
@parametrize_filename
@parametrize_script
def test_get_new_command(ext, tar_error, filename, unquoted, quoted, script, fixed):
tar_error(unquoted.format(ext))
assert (get_new_command(Command(script=script.format(filename.format(ext))))
== fixed.format(dir=quoted.format(''), filename=filename.format(ext)))
def test_get_new_command(tar_error, filename, script, fixed):
tar_error(filename)
assert get_new_command(Command(script=script.format(filename))) == fixed.format(filename)

View File

@@ -1,17 +1,13 @@
# -*- coding: utf-8 -*-
import os
import pytest
import zipfile
from thefuck.rules.dirty_unzip import match, get_new_command, side_effect
from tests.utils import Command
from unicodedata import normalize
@pytest.fixture
def zip_error(tmpdir):
def zip_error_inner(filename):
path = os.path.join(str(tmpdir), filename)
path = os.path.join(str(tmpdir), 'foo.zip')
def reset(path):
with zipfile.ZipFile(path, 'w') as archive:
@@ -26,47 +22,27 @@ def zip_error(tmpdir):
os.chdir(str(tmpdir))
reset(path)
dir_list = os.listdir(u'.')
if filename not in dir_list:
filename = normalize('NFD', filename)
assert set(dir_list) == {filename, 'a', 'b', 'c', 'd'}
assert set(os.listdir('.')) == {'foo.zip', 'a', 'b', 'c', 'd'}
assert set(os.listdir('./d')) == {'e'}
return zip_error_inner
@pytest.mark.parametrize('script,filename', [
(u'unzip café', u'café.zip'),
(u'unzip café.zip', u'café.zip'),
(u'unzip foo', u'foo.zip'),
(u'unzip foo.zip', u'foo.zip')])
def test_match(zip_error, script, filename):
zip_error(filename)
@pytest.mark.parametrize('script', [
'unzip foo',
'unzip foo.zip'])
def test_match(zip_error, script):
assert match(Command(script=script))
@pytest.mark.parametrize('script,filename', [
(u'unzip café', u'café.zip'),
(u'unzip café.zip', u'café.zip'),
(u'unzip foo', u'foo.zip'),
(u'unzip foo.zip', u'foo.zip')])
def test_side_effect(zip_error, script, filename):
zip_error(filename)
@pytest.mark.parametrize('script', [
'unzip foo',
'unzip foo.zip'])
def test_side_effect(zip_error, script):
side_effect(Command(script=script), None)
dir_list = os.listdir(u'.')
if filename not in set(dir_list):
filename = normalize('NFD', filename)
assert set(dir_list) == {filename, 'd'}
assert set(os.listdir('.')) == {'foo.zip', 'd'}
@pytest.mark.parametrize('script,fixed,filename', [
(u'unzip café', u"unzip café -d 'café'", u'café.zip'),
(u'unzip foo', u'unzip foo -d foo', u'foo.zip'),
(u"unzip foo\\ bar.zip", u"unzip foo\\ bar.zip -d 'foo bar'", u'foo.zip'),
(u"unzip 'foo bar.zip'", u"unzip 'foo bar.zip' -d 'foo bar'", u'foo.zip'),
(u'unzip foo.zip', u'unzip foo.zip -d foo', u'foo.zip')])
def test_get_new_command(zip_error, script, fixed, filename):
zip_error(filename)
@pytest.mark.parametrize('script,fixed', [
('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)) == fixed

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
import pytest
import os
from thefuck.rules.fix_file import match, get_new_command
@@ -89,20 +87,6 @@ Traceback (most recent call last):
TypeError: first argument must be string or compiled pattern
"""),
(u'python café.py', u'café.py', 8, None, '',
u"""
Traceback (most recent call last):
File "café.py", line 8, in <module>
match("foo")
File "café.py", line 5, in match
m = re.search(None, command)
File "/usr/lib/python3.4/re.py", line 170, in search
return _compile(pattern, flags).search(string)
File "/usr/lib/python3.4/re.py", line 293, in _compile
raise TypeError("first argument must be string or compiled pattern")
TypeError: first argument must be string or compiled pattern
"""),
('ruby a.rb', 'a.rb', 3, None, '',
"""
a.rb:3: syntax error, unexpected keyword_end
@@ -243,7 +227,7 @@ def test_get_new_command_with_settings(mocker, monkeypatch, test, settings):
if test[3]:
assert (get_new_command(cmd) ==
u'dummy_editor {} +{}:{} && {}'.format(test[1], test[2], test[3], test[0]))
'dummy_editor {} +{}:{} && {}'.format(test[1], test[2], test[3], test[0]))
else:
assert (get_new_command(cmd) ==
u'dummy_editor {} +{} && {}'.format(test[1], test[2], test[0]))
'dummy_editor {} +{} && {}'.format(test[1], test[2], test[0]))

View File

@@ -4,28 +4,36 @@ from tests.utils import Command
@pytest.fixture
def stderr(target):
return ("error: pathspec '{}' did not match any "
'file(s) known to git.'.format(target))
def did_not_match(target, did_you_forget=True):
error = ("error: pathspec '{}' did not match any "
"file(s) known to git.".format(target))
if did_you_forget:
error = ("{}\nDid you forget to 'git add'?'".format(error))
return error
@pytest.mark.parametrize('script, target', [
('git submodule update unknown', 'unknown'),
('git commit unknown', 'unknown')])
def test_match(stderr, script, target):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('command', [
Command(script='git submodule update unknown',
stderr=did_not_match('unknown')),
Command(script='git commit unknown',
stderr=did_not_match('unknown'))]) # Older versions of Git
def test_match(command):
assert match(command)
@pytest.mark.parametrize('script', [
'git submodule update known', 'git commit known'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('command', [
Command(script='git submodule update known', stderr=('')),
Command(script='git commit known', stderr=('')),
Command(script='git commit unknown', # Newer versions of Git
stderr=did_not_match('unknown', False))])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('script, target, new_command', [
('git submodule update unknown', 'unknown',
@pytest.mark.parametrize('command, new_command', [
(Command('git submodule update unknown', stderr=did_not_match('unknown')),
'git add -- unknown && git submodule update unknown'),
('git commit unknown', 'unknown',
(Command('git commit unknown', stderr=did_not_match('unknown')), # Old Git
'git add -- unknown && git commit unknown')])
def test_get_new_command(stderr, script, target, new_command):
assert get_new_command(Command(script=script, stderr=stderr)) == new_command
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -1,33 +0,0 @@
import pytest
from thefuck.rules.git_branch_exists import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stderr(branch_name):
return "fatal: A branch named '{}' already exists.".format(branch_name)
@pytest.fixture
def new_command(branch_name):
return [cmd.format(branch_name) for cmd in [
'git branch -d {0} && git branch {0}',
'git branch -D {0} && git branch {0}', 'git checkout {0}']]
@pytest.mark.parametrize('script, branch_name', [
('git branch foo', 'foo'),
('git branch bar', 'bar')])
def test_match(stderr, script, branch_name):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script', ['git branch foo', 'git branch bar'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('script, branch_name, ', [
('git branch foo', 'foo'), ('git branch bar', 'bar')])
def test_get_new_command(stderr, new_command, script, branch_name):
assert get_new_command(Command(script=script, stderr=stderr)) == new_command

View File

@@ -1,5 +1,5 @@
from thefuck import shells
from thefuck.rules.git_branch_list import match, get_new_command
from thefuck.shells import shell
from tests.utils import Command
@@ -16,4 +16,4 @@ def test_not_match():
def test_get_new_command():
assert (get_new_command(Command('git branch list')) ==
shell.and_('git branch --delete list', 'git branch'))
shells.and_('git branch --delete list', 'git branch'))

View File

@@ -1,6 +1,5 @@
import pytest
from io import BytesIO
from thefuck.rules.git_checkout import match, get_branches, get_new_command
from thefuck.rules.git_checkout import match, get_new_command
from tests.utils import Command
@@ -14,10 +13,8 @@ def did_not_match(target, did_you_forget=False):
@pytest.fixture
def git_branch(mocker, branches):
mock = mocker.patch('subprocess.Popen')
mock.return_value.stdout = BytesIO(branches)
return mock
def get_branches(mocker):
return mocker.patch('thefuck.rules.git_checkout.get_branches')
@pytest.mark.parametrize('command', [
@@ -36,34 +33,21 @@ def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('branches, branch_list', [
(b'', []),
(b'* master', ['master']),
(b' remotes/origin/master', ['master']),
(b' just-another-branch', ['just-another-branch']),
(b'* master\n just-another-branch', ['master', 'just-another-branch']),
(b'* master\n remotes/origin/master\n just-another-branch',
['master', 'master', 'just-another-branch'])])
def test_get_branches(branches, branch_list, git_branch):
git_branch(branches)
assert list(get_branches()) == branch_list
@pytest.mark.parametrize('branches, command, new_command', [
(b'',
([],
Command(script='git checkout unknown', stderr=did_not_match('unknown')),
'git branch unknown && git checkout unknown'),
(b'',
([],
Command('git commit unknown', stderr=did_not_match('unknown')),
'git branch unknown && git commit unknown'),
(b' test-random-branch-123',
(['test-random-branch-123'],
Command(script='git checkout tst-rdm-brnch-123',
stderr=did_not_match('tst-rdm-brnch-123')),
'git checkout test-random-branch-123'),
(b' test-random-branch-123',
(['test-random-branch-123'],
Command(script='git commit tst-rdm-brnch-123',
stderr=did_not_match('tst-rdm-brnch-123')),
'git commit test-random-branch-123')])
def test_get_new_command(branches, command, new_command, git_branch):
git_branch(branches)
def test_get_new_command(branches, command, new_command, get_branches):
get_branches.return_value = branches
assert get_new_command(command) == new_command

View File

@@ -1,24 +0,0 @@
import pytest
from thefuck.rules.git_help_aliased import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('script, stdout', [
('git help st', "`git st' is aliased to `status'"),
('git help ds', "`git ds' is aliased to `diff --staged'")])
def test_match(script, stdout):
assert match(Command(script=script, stdout=stdout))
@pytest.mark.parametrize('script, stdout', [
('git help status', "GIT-STATUS(1)...Git Manual...GIT-STATUS(1)"),
('git help diff', "GIT-DIFF(1)...Git Manual...GIT-DIFF(1)")])
def test_not_match(script, stdout):
assert not match(Command(script=script, stdout=stdout))
@pytest.mark.parametrize('script, stdout, new_command', [
('git help st', "`git st' is aliased to `status'", 'git help status'),
('git help ds', "`git ds' is aliased to `diff --staged'", 'git help diff')])
def test_get_new_command(script, stdout, new_command):
assert get_new_command(Command(script=script, stdout=stdout)) == new_command

View File

@@ -45,8 +45,8 @@ def test_not_match(command):
@pytest.mark.parametrize('command, output', [
(Command(script='git push', stderr=git_err), 'git push --force-with-lease'),
(Command(script='git push nvbn', stderr=git_err), 'git push --force-with-lease nvbn'),
(Command(script='git push nvbn master', stderr=git_err), 'git push --force-with-lease nvbn master')])
(Command(script='git push', stderr=git_err), 'git push --force'),
(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) == output

View File

@@ -1,28 +0,0 @@
import pytest
from thefuck.rules.git_rebase_no_changes import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stdout():
return '''Applying: Test commit
No changes - did you forget to use 'git add'?
If there is nothing left to stage, chances are that something else
already introduced the same changes; you might want to skip this patch.
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
'''
def test_match(stdout):
assert match(Command('git rebase --continue', stdout=stdout))
assert not match(Command('git rebase --continue'))
assert not match(Command('git rebase --skip'))
def test_get_new_command(stdout):
assert (get_new_command(Command('git rebase --continue', stdout=stdout)) ==
'git rebase --skip')

View File

@@ -1,26 +0,0 @@
import pytest
from thefuck.rules.git_remote_seturl_add import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='git remote set-url origin url', stderr="fatal: No such remote")])
def test_match(command):
assert match(command)
@pytest.mark.parametrize('command', [
Command('git remote set-url origin url', stderr=""),
Command('git remote add origin url'),
Command('git remote remove origin'),
Command('git remote prune origin'),
Command('git remote set-branches origin branch')
])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('command, new_command', [
(Command('git remote set-url origin git@github.com:nvbn/thefuck.git'),
'git remote add origin git@github.com:nvbn/thefuck.git')])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -1,27 +0,0 @@
import pytest
from thefuck.rules.git_rm_recursive import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stderr(target):
return "fatal: not removing '{}' recursively without -r".format(target)
@pytest.mark.parametrize('script, target', [
('git rm foo', 'foo'),
('git rm foo bar', 'foo bar')])
def test_match(stderr, script, target):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script', ['git rm foo', 'git rm foo bar'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('script, target, new_command', [
('git rm foo', 'foo', 'git rm -r foo'),
('git rm foo bar', 'foo bar', 'git rm -r foo bar')])
def test_get_new_command(stderr, script, target, new_command):
assert get_new_command(Command(script=script, stderr=stderr)) == new_command

View File

@@ -1,47 +0,0 @@
import pytest
from thefuck.rules.git_two_dashes import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stderr(meant):
return 'error: did you mean `%s` (with two dashes ?)' % meant
@pytest.mark.parametrize('command', [
Command(script='git add -patch', stderr=stderr('--patch')),
Command(script='git checkout -patch', stderr=stderr('--patch')),
Command(script='git commit -amend', stderr=stderr('--amend')),
Command(script='git push -tags', stderr=stderr('--tags')),
Command(script='git rebase -continue', stderr=stderr('--continue'))])
def test_match(command):
assert match(command)
@pytest.mark.parametrize('command', [
Command(script='git add --patch'),
Command(script='git checkout --patch'),
Command(script='git commit --amend'),
Command(script='git push --tags'),
Command(script='git rebase --continue')])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('command, output', [
(Command(script='git add -patch', stderr=stderr('--patch')),
'git add --patch'),
(Command(script='git checkout -patch', stderr=stderr('--patch')),
'git checkout --patch'),
(Command(script='git checkout -patch', stderr=stderr('--patch')),
'git checkout --patch'),
(Command(script='git init -bare', stderr=stderr('--bare')),
'git init --bare'),
(Command(script='git commit -amend', stderr=stderr('--amend')),
'git commit --amend'),
(Command(script='git push -tags', stderr=stderr('--tags')),
'git push --tags'),
(Command(script='git rebase -continue', stderr=stderr('--continue')),
'git rebase --continue')])
def test_get_new_command(command, output):
assert get_new_command(command) == output

View File

@@ -1,40 +0,0 @@
import pytest
from thefuck.rules.grep_arguments_order import get_new_command, match
from tests.utils import Command
stderr = 'grep: {}: No such file or directory'.format
@pytest.fixture(autouse=True)
def os_path(monkeypatch):
monkeypatch.setattr('os.path.isfile', lambda x: not x.startswith('-'))
@pytest.mark.parametrize('script, file', [
('grep test.py test', 'test'),
('grep -lir . test', 'test'),
('egrep test.py test', 'test'),
('egrep -lir . test', 'test')])
def test_match(script, file):
assert match(Command(script, stderr=stderr(file)))
@pytest.mark.parametrize('script, stderr', [
('cat test.py', stderr('test')),
('grep test test.py', ''),
('grep -lir test .', ''),
('egrep test test.py', ''),
('egrep -lir test .', '')])
def test_not_match(script, stderr):
assert not match(Command(script, stderr=stderr))
@pytest.mark.parametrize('script, stderr, result', [
('grep test.py test', stderr('test'), 'grep test test.py'),
('grep -lir . test', stderr('test'), 'grep -lir test .'),
('grep . test -lir', stderr('test'), 'grep test -lir .'),
('egrep test.py test', stderr('test'), 'egrep test test.py'),
('egrep -lir . test', stderr('test'), 'egrep -lir test .'),
('egrep . test -lir', stderr('test'), 'egrep test -lir .')])
def test_get_new_command(script, stderr, result):
assert get_new_command(Command(script, stderr=stderr)) == result

View File

@@ -1,15 +1,12 @@
# -*- coding: utf-8 -*-
from thefuck.rules.grep_recursive import match, get_new_command
from tests.utils import Command
def test_match():
assert match(Command('grep blah .', stderr='grep: .: Is a directory'))
assert match(Command(u'grep café .', stderr='grep: .: Is a directory'))
assert not match(Command())
def test_get_new_command():
assert get_new_command(Command('grep blah .')) == 'grep -r blah .'
assert get_new_command(Command(u'grep café .')) == u'grep -r café .'
assert get_new_command(
Command('grep blah .')) == 'grep -r blah .'

View File

@@ -1,5 +1,4 @@
import pytest
from io import BytesIO
from tests.utils import Command
from thefuck.rules.gulp_not_task import match, get_new_command
@@ -23,7 +22,7 @@ def test_not_march(script, stdout):
def test_get_new_command(mocker):
mock = mocker.patch('subprocess.Popen')
mock.return_value.stdout = BytesIO(b'serve \nbuild \ndefault \n')
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) == ['gulp serve', 'gulp default']

View File

@@ -1,18 +1,17 @@
from mock import patch
from mock import Mock, patch
from thefuck.rules.has_exists_script import match, get_new_command
from ..utils import Command
def test_match():
with patch('os.path.exists', return_value=True):
assert match(Command(script='main', stderr='main: command not found'))
assert match(Command(script='main --help',
assert match(Mock(script='main', stderr='main: command not found'))
assert match(Mock(script='main --help',
stderr='main: command not found'))
assert not match(Command(script='main', stderr=''))
assert not match(Mock(script='main', stderr=''))
with patch('os.path.exists', return_value=False):
assert not match(Command(script='main', stderr='main: command not found'))
assert not match(Mock(script='main', stderr='main: command not found'))
def test_get_new_command():
assert get_new_command(Command(script='main --help')) == './main --help'
assert get_new_command(Mock(script='main --help')) == './main --help'

View File

@@ -3,23 +3,38 @@ from thefuck.rules.history import match, get_new_command
from tests.utils import Command
@pytest.fixture(autouse=True)
def history_without_current(mocker):
return mocker.patch(
'thefuck.rules.history.get_valid_history_without_current',
return_value=['ls cat', 'diff x'])
@pytest.fixture
def history(mocker):
return mocker.patch('thefuck.rules.history.get_history',
return_value=['le cat', 'fuck', 'ls cat',
'diff x', 'nocommand x'])
@pytest.fixture
def alias(mocker):
return mocker.patch('thefuck.rules.history.thefuck_alias',
return_value='fuck')
@pytest.fixture
def callables(mocker):
return mocker.patch('thefuck.rules.history.get_all_executables',
return_value=['diff', 'ls'])
@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))
@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))
@pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias')
@pytest.mark.parametrize('script, result', [
('ls cet', 'ls cat'),
('daff x', 'diff x')])

View File

@@ -1,37 +0,0 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.rules.ln_no_hard_link import match, get_new_command
from tests.utils import Command
error = "hard link not allowed for directory"
@pytest.mark.parametrize('script, stderr', [
("ln barDir barLink", "ln: barDir: {}"),
("sudo ln a b", "ln: a: {}"),
("sudo ln -nbi a b", "ln: a: {}")])
def test_match(script, stderr):
command = Command(script, stderr=stderr.format(error))
assert match(command)
@pytest.mark.parametrize('script, stderr', [
('', ''),
("ln a b", "... hard link"),
("sudo ln a b", "... hard link"),
("a b", error)])
def test_not_match(script, stderr):
command = Command(script, stderr=stderr)
assert not match(command)
@pytest.mark.parametrize('script, result', [
("ln barDir barLink", "ln -s barDir barLink"),
("sudo ln barDir barLink", "sudo ln -s barDir barLink"),
("sudo ln -nbi a b", "sudo ln -s -nbi a b"),
("ln -nbi a b && ls", "ln -s -nbi a b && ls"),
("ln a ln", "ln -s a ln"),
("sudo ln a ln", "sudo ln -s a ln")])
def test_get_new_command(script, result):
command = Command(script)
assert get_new_command(command) == result

View File

@@ -1,41 +0,0 @@
import pytest
from thefuck.rules.ln_s_order import match, get_new_command
from tests.utils import Command
@pytest.fixture
def file_exists(mocker):
return mocker.patch('os.path.exists', return_value=True)
get_stderr = "ln: failed to create symbolic link '{}': File exists".format
@pytest.mark.usefixtures('file_exists')
@pytest.mark.parametrize('script', [
'ln -s dest source',
'ln dest -s source',
'ln dest source -s'])
def test_match(script):
stderr = get_stderr('source')
assert match(Command(script, stderr=stderr))
@pytest.mark.parametrize('script, stderr, exists', [
('ln dest source', get_stderr('source'), True),
('ls -s dest source', get_stderr('source'), True),
('ln -s dest source', '', True),
('ln -s dest source', get_stderr('source'), False)])
def test_not_match(file_exists, script, stderr, exists):
file_exists.return_value = exists
assert not match(Command(script, stderr=stderr))
@pytest.mark.usefixtures('file_exists')
@pytest.mark.parametrize('script, result', [
('ln -s dest source', 'ln -s source dest'),
('ln dest -s source', 'ln -s source dest'),
('ln dest source -s', 'ln source -s dest')])
def test_match(script, result):
stderr = get_stderr('source')
assert get_new_command(Command(script, stderr=stderr)) == result

View File

@@ -17,8 +17,7 @@ def test_match(command):
Command('mkdir foo/bar/baz', stderr='foo bar baz'),
Command('hdfs dfs -mkdir foo/bar/baz'),
Command('./bin/hdfs dfs -mkdir foo/bar/baz'),
Command(),
])
Command()])
def test_not_match(command):
assert not match(command)
@@ -26,7 +25,7 @@ def test_not_match(command):
@pytest.mark.parametrize('command, new_command', [
(Command('mkdir foo/bar/baz'), 'mkdir -p foo/bar/baz'),
(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'),
])
(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) == new_command

View File

@@ -32,9 +32,9 @@ def test_match(command):
def test_not_match(command):
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 <plugin-prefix>:<goal> or <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>. 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 <plugin-prefix>:<goal> or <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>. 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) == new_command

View File

@@ -32,9 +32,9 @@ def test_match(command):
def test_not_match(command):
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 <plugin-prefix>:<goal> or <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>. 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 <plugin-prefix>:<goal> or <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>. 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) == new_command

View File

@@ -6,37 +6,22 @@ from tests.utils import Command
@pytest.fixture(autouse=True)
def get_all_executables(mocker):
mocker.patch('thefuck.rules.no_command.get_all_executables',
return_value=['vim', 'fsck', 'git', 'go'])
@pytest.fixture(autouse=True)
def history_without_current(mocker):
return mocker.patch(
'thefuck.rules.no_command.get_valid_history_without_current',
return_value=['git commit'])
return_value=['vim', 'apt-get', 'fsck'])
@pytest.mark.usefixtures('no_memoize')
@pytest.mark.parametrize('script, stderr', [
('vom file.py', 'vom: not found'),
('fucck', 'fucck: not found'),
('got commit', 'got: command not found')])
def test_match(script, stderr):
assert match(Command(script, stderr=stderr))
def test_match():
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')
@pytest.mark.parametrize('script, stderr', [
('qweqwe', 'qweqwe: not found'),
('vom file.py', 'some text')])
def test_not_match(script, stderr):
assert not match(Command(script, stderr=stderr))
@pytest.mark.usefixtures('no_memoize')
@pytest.mark.parametrize('script, result', [
('vom file.py', ['vim file.py']),
('fucck', ['fsck']),
('got commit', ['git commit', 'go commit'])])
def test_get_new_command(script, result):
assert get_new_command(Command(script)) == result
def test_get_new_command():
assert get_new_command(
Command(stderr='vom: not found',
script='vom file.py')) == ['vim file.py']
assert get_new_command(
Command(stderr='fucck: not found',
script='fucck')) == ['fsck']

View File

@@ -1,58 +0,0 @@
import pytest
from thefuck.rules.npm_wrong_command import match, get_new_command
from tests.utils import Command
stdout = '''
Usage: npm <command>
where <command> is one of:
access, add-user, adduser, apihelp, author, bin, bugs, c,
cache, completion, config, ddp, dedupe, deprecate, dist-tag,
dist-tags, docs, edit, explore, faq, find, find-dupes, get,
help, help-search, home, i, info, init, install, issues, la,
link, list, ll, ln, login, logout, ls, outdated, owner,
pack, ping, prefix, prune, publish, r, rb, rebuild, remove,
repo, restart, rm, root, run-script, s, se, search, set,
show, shrinkwrap, star, stars, start, stop, t, tag, team,
test, tst, un, uninstall, unlink, unpublish, unstar, up,
update, upgrade, v, verison, version, view, whoami
npm <cmd> -h quick help on <cmd>
npm -l display full usage info
npm faq commonly asked questions
npm help <term> search for help on <term>
npm help npm involved overview
Specify configs in the ini-formatted file:
/home/nvbn/.npmrc
or on the command line via: npm <command> --key value
Config info can be viewed via: npm help config
npm@2.14.7 /opt/node/lib/node_modules/npm
'''
@pytest.mark.parametrize('script', [
'npm urgrdae',
'npm urgrade -g',
'npm -f urgrade -g',
'npm urg'])
def test_match(script):
assert match(Command(script, stdout))
@pytest.mark.parametrize('script, stdout', [
('npm urgrade', ''),
('npm', stdout),
('test urgrade', stdout),
('npm -e', stdout)])
def test_not_match(script, stdout):
assert not match(Command(script, stdout))
@pytest.mark.parametrize('script, result', [
('npm urgrade', 'npm upgrade'),
('npm -g isntall gulp', 'npm -g install gulp'),
('npm isntall -g gulp', 'npm install -g gulp')])
def test_get_new_command(script, result):
assert get_new_command(Command(script, stdout)) == result

View File

@@ -7,7 +7,7 @@ from tests.utils import Command
Command('rm foo', stderr='rm: foo: is a directory'),
Command('rm foo', stderr='rm: foo: Is a directory'),
Command('hdfs dfs -rm foo', stderr='rm: `foo`: Is a directory'),
Command('./bin/hdfs dfs -rm foo', stderr='rm: `foo`: Is a directory'),
Command('./bin/hdfs dfs -rm foo', stderr='rm: `foo`: Is a directory')
])
def test_match(command):
assert match(command)
@@ -17,15 +17,15 @@ def test_match(command):
Command('rm foo'),
Command('hdfs dfs -rm foo'),
Command('./bin/hdfs dfs -rm foo'),
Command(),
])
Command()])
def test_not_match(command):
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'),
])
(Command('hdfs dfs -rm foo'), 'hdfs dfs -rm -r foo')])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -1,5 +1,6 @@
import os
import pytest
from mock import Mock
from thefuck.rules.ssh_known_hosts import match, get_new_command,\
side_effect
from tests.utils import Command
@@ -52,7 +53,6 @@ def test_match(ssh_error):
assert not match(Command('ssh'))
@pytest.mark.skipif(os.name == 'nt', reason='Skip if testing on Windows')
def test_side_effect(ssh_error):
errormsg, path, reset, known_hosts = ssh_error
command = Command('ssh user@host', stderr=errormsg)

View File

@@ -19,13 +19,11 @@ def test_match(stderr, stdout):
def test_not_match():
assert not match(Command())
assert not match(Command(script='sudo ls', stderr='Permission denied'))
@pytest.mark.parametrize('before, after', [
('ls', 'sudo ls'),
('echo a > b', 'sudo sh -c "echo a > b"'),
('echo "a" >> b', 'sudo sh -c "echo \\"a\\" >> b"'),
('mkdir && touch a', 'sudo sh -c "mkdir && touch a"')])
('echo "a" >> b', 'sudo sh -c "echo \\"a\\" >> b"')])
def test_get_new_command(before, after):
assert get_new_command(Command(before)) == after

View File

@@ -1,3 +1,4 @@
import pytest
from thefuck.rules.systemctl import match, get_new_command
from tests.utils import Command

View File

@@ -32,3 +32,4 @@ def test_not_match(command):
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) == new_command

View File

@@ -32,3 +32,4 @@ def test_not_match(command):
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) == new_command

View File

@@ -1,22 +0,0 @@
import pytest
@pytest.fixture
def builtins_open(mocker):
return mocker.patch('six.moves.builtins.open')
@pytest.fixture
def isfile(mocker):
return mocker.patch('os.path.isfile', return_value=True)
@pytest.fixture
@pytest.mark.usefixtures('isfile')
def history_lines(mocker):
def aux(lines):
mock = mocker.patch('io.open')
mock.return_value.__enter__ \
.return_value.readlines.return_value = lines
return aux

View File

@@ -1,58 +0,0 @@
# -*- coding: utf-8 -*-
import os
import pytest
from thefuck.shells import Bash
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
class TestBash(object):
@pytest.fixture
def shell(self):
return Bash()
@pytest.fixture(autouse=True)
def shell_aliases(self):
os.environ['TF_SHELL_ALIASES'] = (
'alias fuck=\'eval $(thefuck $(fc -ln -1))\'\n'
'alias l=\'ls -CF\'\n'
'alias la=\'ls -A\'\n'
'alias ll=\'ls -alF\'')
@pytest.mark.parametrize('before, after', [
('pwd', 'pwd'),
('fuck', 'eval $(thefuck $(fc -ln -1))'),
('awk', 'awk'),
('ll', 'ls -alF')])
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {'fuck': 'eval $(thefuck $(fc -ln -1))',
'l': 'ls -CF',
'la': 'ls -A',
'll': 'ls -alF'}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias('fuck')
assert 'alias FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'TF_ALIAS=fuck' in shell.app_alias('fuck')
assert 'PYTHONIOENCODING=utf-8' in shell.app_alias('fuck')
def test_app_alias_variables_correctly_set(self, shell):
alias = shell.app_alias('fuck')
assert "alias fuck='TF_CMD=$(TF_ALIAS" in alias
assert '$(TF_ALIAS=fuck PYTHONIOENCODING' in alias
assert 'PYTHONIOENCODING=utf-8 TF_SHELL_ALIASES' in alias
assert 'ALIASES=$(alias) thefuck' in alias
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
assert list(shell.get_history()) == ['ls', 'rm']

View File

@@ -1,97 +0,0 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.shells import Fish
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
class TestFish(object):
@pytest.fixture
def shell(self):
return Fish()
@pytest.fixture(autouse=True)
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.fish.Popen')
mock.return_value.stdout.read.return_value = (
b'cd\nfish_config\nfuck\nfunced\nfuncsave\ngrep\nhistory\nll\nls\n'
b'man\nmath\npopd\npushd\nruby')
return mock
@pytest.fixture
def os_environ(self, monkeypatch, key, value):
monkeypatch.setattr('os.environ', {key: value})
@pytest.mark.parametrize('key, value', [
('TF_OVERRIDDEN_ALIASES', 'cut,git,sed'), # legacy
('THEFUCK_OVERRIDDEN_ALIASES', 'cut,git,sed'),
('THEFUCK_OVERRIDDEN_ALIASES', 'cut, git, sed'),
('THEFUCK_OVERRIDDEN_ALIASES', ' cut,\tgit,sed\n'),
('THEFUCK_OVERRIDDEN_ALIASES', '\ncut,\n\ngit,\tsed\r')])
def test_get_overridden_aliases(self, shell, os_environ):
assert shell._get_overridden_aliases() == {'cd', 'cut', 'git', 'grep',
'ls', 'man', 'open', 'sed'}
@pytest.mark.parametrize('before, after', [
('cd', 'cd'),
('pwd', 'pwd'),
('fuck', 'fish -ic "fuck"'),
('find', 'find'),
('funced', 'fish -ic "funced"'),
('grep', 'grep'),
('awk', 'awk'),
('math "2 + 2"', r'fish -ic "math \"2 + 2\""'),
('man', 'man'),
('open', 'open'),
('vim', 'vim'),
('ll', 'fish -ic "ll"'),
('ls', 'ls')]) # Fish has no aliases but functions
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_and_(self, shell):
assert shell.and_('foo', 'bar') == 'foo; and bar'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {'fish_config': 'fish_config',
'fuck': 'fuck',
'funced': 'funced',
'funcsave': 'funcsave',
'history': 'history',
'll': 'll',
'math': 'math',
'popd': 'popd',
'pushd': 'pushd',
'ruby': 'ruby'}
def test_app_alias(self, shell):
assert 'function fuck' in shell.app_alias('fuck')
assert 'function FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
def test_app_alias_alter_history(self, settings, shell):
settings.alter_history = True
assert 'history --delete' in shell.app_alias('FUCK')
assert 'history --merge' in shell.app_alias('FUCK')
settings.alter_history = False
assert 'history --delete' not in shell.app_alias('FUCK')
assert 'history --merge' not in shell.app_alias('FUCK')
def test_get_history(self, history_lines, shell):
history_lines(['- cmd: ls', ' when: 1432613911',
'- cmd: rm', ' when: 1432613916'])
assert list(shell.get_history()) == ['ls', 'rm']
@pytest.mark.parametrize('entry, entry_utf8', [
('ls', '- cmd: ls\n when: 1430707243\n'),
(u'echo café', '- cmd: echo café\n when: 1430707243\n')])
def test_put_to_history(self, entry, entry_utf8, builtins_open, mocker, shell):
mocker.patch('thefuck.shells.fish.time', return_value=1430707243.3517463)
shell.put_to_history(entry)
builtins_open.return_value.__enter__.return_value. \
write.assert_called_once_with(entry_utf8)

View File

@@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.shells import Generic
class TestGeneric(object):
@pytest.fixture
def shell(self):
return Generic()
def test_from_shell(self, shell):
assert shell.from_shell('pwd') == 'pwd'
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias('fuck')
assert 'alias FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'TF_ALIAS=fuck PYTHONIOENCODING' in shell.app_alias('fuck')
assert 'PYTHONIOENCODING=utf-8 thefuck' in shell.app_alias('fuck')
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
# We don't know what to do in generic shell with history lines,
# so just ignore them:
assert list(shell.get_history()) == []
def test_split_command(self, shell):
assert shell.split_command('ls') == ['ls']
assert shell.split_command(u'echo café') == [u'echo', u'café']

View File

@@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.shells import Powershell
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
class TestPowershell(object):
@pytest.fixture
def shell(self):
return Powershell()
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == '(ls) -and (cd)'
def test_app_alias(self, shell):
assert 'function fuck' in shell.app_alias('fuck')
assert 'function FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')

View File

@@ -1,50 +0,0 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.shells.tcsh import Tcsh
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
class TestTcsh(object):
@pytest.fixture
def shell(self):
return Tcsh()
@pytest.fixture(autouse=True)
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.tcsh.Popen')
mock.return_value.stdout.read.return_value = (
b'fuck\teval $(thefuck $(fc -ln -1))\n'
b'l\tls -CF\n'
b'la\tls -A\n'
b'll\tls -alF')
return mock
@pytest.mark.parametrize('before, after', [
('pwd', 'pwd'),
('fuck', 'eval $(thefuck $(fc -ln -1))'),
('awk', 'awk'),
('ll', 'ls -alF')])
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {'fuck': 'eval $(thefuck $(fc -ln -1))',
'l': 'ls -CF',
'la': 'ls -A',
'll': 'ls -alF'}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias('fuck')
assert 'alias FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
assert list(shell.get_history()) == ['ls', 'rm']

View File

@@ -1,57 +0,0 @@
# -*- coding: utf-8 -*-
import os
import pytest
from thefuck.shells.zsh import Zsh
@pytest.mark.usefixtures('isfile', 'no_memoize', 'no_cache')
class TestZsh(object):
@pytest.fixture
def shell(self):
return Zsh()
@pytest.fixture(autouse=True)
def shell_aliases(self):
os.environ['TF_SHELL_ALIASES'] = (
'fuck=\'eval $(thefuck $(fc -ln -1 | tail -n 1))\'\n'
'l=\'ls -CF\'\n'
'la=\'ls -A\'\n'
'll=\'ls -alF\'')
@pytest.mark.parametrize('before, after', [
('fuck', 'eval $(thefuck $(fc -ln -1 | tail -n 1))'),
('pwd', 'pwd'),
('ll', 'ls -alF')])
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {
'fuck': 'eval $(thefuck $(fc -ln -1 | tail -n 1))',
'l': 'ls -CF',
'la': 'ls -A',
'll': 'ls -alF'}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias('fuck')
assert 'alias FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'PYTHONIOENCODING' in shell.app_alias('fuck')
def test_app_alias_variables_correctly_set(self, shell):
alias = shell.app_alias('fuck')
assert "alias fuck='TF_CMD=$(TF_ALIAS" in alias
assert '$(TF_ALIAS=fuck PYTHONIOENCODING' in alias
assert 'PYTHONIOENCODING=utf-8 TF_SHELL_ALIASES' in alias
assert 'ALIASES=$(alias) thefuck' in alias
def test_get_history(self, history_lines, shell):
history_lines([': 1432613911:0;ls', ': 1432613916:0;rm'])
assert list(shell.get_history()) == ['ls', 'rm']

View File

@@ -1,4 +1,5 @@
import pytest
from mock import Mock
from thefuck.specific.sudo import sudo_support
from tests.utils import Command

View File

@@ -1,7 +1,7 @@
import pytest
import six
from mock import Mock
from thefuck import const
from thefuck import conf
@pytest.fixture
@@ -20,7 +20,7 @@ def environ(monkeypatch):
def test_settings_defaults(load_source, settings):
load_source.return_value = object()
settings.init()
for key, val in const.DEFAULT_SETTINGS.items():
for key, val in conf.DEFAULT_SETTINGS.items():
assert getattr(settings, key) == val
@@ -42,13 +42,13 @@ class TestSettingsFromFile(object):
assert settings.exclude_rules == ['git']
def test_from_file_with_DEFAULT(self, load_source, settings):
load_source.return_value = Mock(rules=const.DEFAULT_RULES + ['test'],
load_source.return_value = Mock(rules=conf.DEFAULT_RULES + ['test'],
wait_command=10,
exclude_rules=[],
require_confirmation=True,
no_colors=True)
settings.init()
assert settings.rules == const.DEFAULT_RULES + ['test']
assert settings.rules == conf.DEFAULT_RULES + ['test']
@pytest.mark.usefixture('load_source')
@@ -71,7 +71,7 @@ class TestSettingsFromEnv(object):
def test_from_env_with_DEFAULT(self, environ, settings):
environ.update({'THEFUCK_RULES': 'DEFAULT_RULES:bash:lisp'})
settings.init()
assert settings.rules == const.DEFAULT_RULES + ['bash', 'lisp']
assert settings.rules == conf.DEFAULT_RULES + ['bash', 'lisp']
class TestInitializeSettingsFile(object):
@@ -93,7 +93,7 @@ class TestInitializeSettingsFile(object):
settings_file_contents = settings_file.getvalue()
assert settings_path_mock.is_file.call_count == 1
assert settings_path_mock.open.call_count == 1
assert const.SETTINGS_HEADER in settings_file_contents
for setting in const.DEFAULT_SETTINGS.items():
assert conf.SETTINGS_HEADER in settings_file_contents
for setting in conf.DEFAULT_SETTINGS.items():
assert '# {} = {}\n'.format(*setting) in settings_file_contents
settings_file.close()

View File

@@ -1,13 +1,6 @@
# -*- coding: utf-8 -*-
import pytest
try:
from pathlib import Path
pathlib_name = 'pathlib'
except ImportError:
from pathlib2 import Path
pathlib_name = 'pathlib2'
from thefuck import corrector, const
from pathlib import PosixPath
from thefuck import corrector, conf
from tests.utils import Rule, Command, CorrectedCommand
from thefuck.corrector import get_corrected_commands, organize_commands
@@ -16,7 +9,7 @@ class TestGetRules(object):
@pytest.fixture
def glob(self, mocker):
results = {}
mocker.patch(pathlib_name + '.Path.glob',
mocker.patch('pathlib.Path.glob',
new_callable=lambda: lambda *_: results.pop('value', []))
return lambda value: results.update({'value': value})
@@ -29,13 +22,13 @@ class TestGetRules(object):
assert {r.name for r in rules} == set(names)
@pytest.mark.parametrize('paths, conf_rules, exclude_rules, loaded_rules', [
(['git.py', 'bash.py'], const.DEFAULT_RULES, [], ['git', 'bash']),
(['git.py', 'bash.py'], conf.DEFAULT_RULES, [], ['git', 'bash']),
(['git.py', 'bash.py'], ['git'], [], ['git']),
(['git.py', 'bash.py'], const.DEFAULT_RULES, ['git'], ['bash']),
(['git.py', 'bash.py'], conf.DEFAULT_RULES, ['git'], ['bash']),
(['git.py', 'bash.py'], ['git'], ['git'], [])])
def test_get_rules(self, glob, settings, paths, conf_rules, exclude_rules,
loaded_rules):
glob([Path(path) for path in paths])
glob([PosixPath(path) for path in paths])
settings.update(rules=conf_rules,
priority={},
exclude_rules=exclude_rules)
@@ -60,9 +53,7 @@ def test_organize_commands():
"""Ensures that the function removes duplicates and sorts commands."""
commands = [CorrectedCommand('ls'), CorrectedCommand('ls -la', priority=9000),
CorrectedCommand('ls -lh', priority=100),
CorrectedCommand(u'echo café', priority=200),
CorrectedCommand('ls -lh', priority=9999)]
assert list(organize_commands(iter(commands))) \
== [CorrectedCommand('ls'), CorrectedCommand('ls -lh', priority=100),
CorrectedCommand(u'echo café', priority=200),
CorrectedCommand('ls -la', priority=9000)]

View File

@@ -1,4 +1,5 @@
import pytest
from mock import Mock
from thefuck import logs

235
tests/test_shells.py Normal file
View File

@@ -0,0 +1,235 @@
import pytest
from thefuck import shells
@pytest.fixture
def builtins_open(mocker):
return mocker.patch('six.moves.builtins.open')
@pytest.fixture
def isfile(mocker):
return mocker.patch('os.path.isfile', return_value=True)
@pytest.fixture
@pytest.mark.usefixtures('isfile')
def history_lines(mocker):
def aux(lines):
mock = mocker.patch('io.open')
mock.return_value.__enter__\
.return_value.__iter__.return_value = lines
return aux
class TestGeneric(object):
@pytest.fixture
def shell(self):
return shells.Generic()
def test_from_shell(self, shell):
assert shell.from_shell('pwd') == 'pwd'
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_put_to_history(self, builtins_open, shell):
assert shell.put_to_history('ls') is None
assert builtins_open.call_count == 0
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias('fuck')
assert 'alias FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'TF_ALIAS' in shell.app_alias('fuck')
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
# We don't know what to do in generic shell with history lines,
# so just ignore them:
assert list(shell.get_history()) == []
@pytest.mark.usefixtures('isfile')
class TestBash(object):
@pytest.fixture
def shell(self):
return shells.Bash()
@pytest.fixture(autouse=True)
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.Popen')
mock.return_value.stdout.read.return_value = (
b'alias fuck=\'eval $(thefuck $(fc -ln -1))\'\n'
b'alias l=\'ls -CF\'\n'
b'alias la=\'ls -A\'\n'
b'alias ll=\'ls -alF\'')
return mock
@pytest.mark.parametrize('before, after', [
('pwd', 'pwd'),
('fuck', 'eval $(thefuck $(fc -ln -1))'),
('awk', 'awk'),
('ll', 'ls -alF')])
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_put_to_history(self, builtins_open, shell):
shell.put_to_history('ls')
builtins_open.return_value.__enter__.return_value. \
write.assert_called_once_with('ls\n')
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {'fuck': 'eval $(thefuck $(fc -ln -1))',
'l': 'ls -CF',
'la': 'ls -A',
'll': 'ls -alF'}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias('fuck')
assert 'alias FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'TF_ALIAS' in shell.app_alias('fuck')
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
assert list(shell.get_history()) == ['ls', 'rm']
@pytest.mark.usefixtures('isfile')
class TestFish(object):
@pytest.fixture
def shell(self):
return shells.Fish()
@pytest.fixture(autouse=True)
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.Popen')
mock.return_value.stdout.read.return_value = (
b'cd\nfish_config\nfuck\nfunced\nfuncsave\ngrep\nhistory\nll\nls\n'
b'man\nmath\npopd\npushd\nruby')
return mock
@pytest.fixture
def environ(self, monkeypatch):
data = {'TF_OVERRIDDEN_ALIASES': 'cd, ls, man, open'}
monkeypatch.setattr('thefuck.shells.os.environ', data)
return data
@pytest.mark.usefixture('environ')
def test_get_overridden_aliases(self, shell, environ):
assert shell._get_overridden_aliases() == ['cd', 'ls', 'man', 'open']
@pytest.mark.parametrize('before, after', [
('cd', 'cd'),
('pwd', 'pwd'),
('fuck', 'fish -ic "fuck"'),
('find', 'find'),
('funced', 'fish -ic "funced"'),
('grep', 'grep'),
('awk', 'awk'),
('math "2 + 2"', r'fish -ic "math \"2 + 2\""'),
('man', 'man'),
('open', 'open'),
('vim', 'vim'),
('ll', 'fish -ic "ll"'),
('ls', 'ls')]) # Fish has no aliases but functions
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_put_to_history(self, builtins_open, mocker, shell):
mocker.patch('thefuck.shells.time',
return_value=1430707243.3517463)
shell.put_to_history('ls')
builtins_open.return_value.__enter__.return_value. \
write.assert_called_once_with('- cmd: ls\n when: 1430707243\n')
def test_and_(self, shell):
assert shell.and_('foo', 'bar') == 'foo; and bar'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {'fish_config': 'fish_config',
'fuck': 'fuck',
'funced': 'funced',
'funcsave': 'funcsave',
'history': 'history',
'll': 'll',
'math': 'math',
'popd': 'popd',
'pushd': 'pushd',
'ruby': 'ruby'}
def test_app_alias(self, shell):
assert 'function fuck' in shell.app_alias('fuck')
assert 'function FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'TF_ALIAS' in shell.app_alias('fuck')
@pytest.mark.usefixtures('isfile')
class TestZsh(object):
@pytest.fixture
def shell(self):
return shells.Zsh()
@pytest.fixture(autouse=True)
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.Popen')
mock.return_value.stdout.read.return_value = (
b'fuck=\'eval $(thefuck $(fc -ln -1 | tail -n 1))\'\n'
b'l=\'ls -CF\'\n'
b'la=\'ls -A\'\n'
b'll=\'ls -alF\'')
return mock
@pytest.mark.parametrize('before, after', [
('fuck', 'eval $(thefuck $(fc -ln -1 | tail -n 1))'),
('pwd', 'pwd'),
('ll', 'ls -alF')])
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
def test_to_shell(self, shell):
assert shell.to_shell('pwd') == 'pwd'
def test_put_to_history(self, builtins_open, mocker, shell):
mocker.patch('thefuck.shells.time',
return_value=1430707243.3517463)
shell.put_to_history('ls')
builtins_open.return_value.__enter__.return_value. \
write.assert_called_once_with(': 1430707243:0;ls\n')
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == 'ls && cd'
def test_get_aliases(self, shell):
assert shell.get_aliases() == {
'fuck': 'eval $(thefuck $(fc -ln -1 | tail -n 1))',
'l': 'ls -CF',
'la': 'ls -A',
'll': 'ls -alF'}
def test_app_alias(self, shell):
assert 'alias fuck' in shell.app_alias('fuck')
assert 'alias FUCK' in shell.app_alias('FUCK')
assert 'thefuck' in shell.app_alias('fuck')
assert 'TF_ALIAS' in shell.app_alias('fuck')
def test_get_history(self, history_lines, shell):
history_lines([': 1432613911:0;ls', ': 1432613916:0;rm'])
assert list(shell.get_history()) == ['ls', 'rm']

View File

@@ -1,15 +1,9 @@
# -*- coding: utf-8 -*-
import os
from subprocess import PIPE
from mock import Mock
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
import pytest
from tests.utils import CorrectedCommand, Rule, Command
from thefuck import const
from thefuck import conf
from thefuck.exceptions import EmptyCommand
@@ -25,12 +19,6 @@ class TestCorrectedCommand(object):
assert {CorrectedCommand('ls', None, 100),
CorrectedCommand('ls', None, 200)} == {CorrectedCommand('ls')}
def test_representable(self):
assert '{}'.format(CorrectedCommand('ls', None, 100)) == \
'CorrectedCommand(script=ls, side_effect=None, priority=100)'
assert u'{}'.format(CorrectedCommand(u'echo café', None, 100)) == \
u'CorrectedCommand(script=echo café, side_effect=None, priority=100)'
class TestRule(object):
def test_from_path(self, mocker):
@@ -43,20 +31,19 @@ class TestRule(object):
enabled_by_default=True,
priority=900,
requires_output=True))
rule_path = os.path.join(os.sep, 'rules', 'bash.py')
assert Rule.from_path(Path(rule_path)) \
assert Rule.from_path(Path('/rules/bash.py')) \
== Rule('bash', match, get_new_command, priority=900)
load_source.assert_called_once_with('bash', rule_path)
load_source.assert_called_once_with('bash', '/rules/bash.py')
@pytest.mark.parametrize('rules, exclude_rules, rule, is_enabled', [
(const.DEFAULT_RULES, [], Rule('git', enabled_by_default=True), True),
(const.DEFAULT_RULES, [], Rule('git', enabled_by_default=False), False),
(conf.DEFAULT_RULES, [], Rule('git', enabled_by_default=True), True),
(conf.DEFAULT_RULES, [], Rule('git', enabled_by_default=False), False),
([], [], Rule('git', enabled_by_default=False), False),
([], [], Rule('git', enabled_by_default=True), False),
(const.DEFAULT_RULES + ['git'], [], Rule('git', enabled_by_default=False), True),
(conf.DEFAULT_RULES + ['git'], [], Rule('git', enabled_by_default=False), True),
(['git'], [], Rule('git', enabled_by_default=False), True),
(const.DEFAULT_RULES, ['git'], Rule('git', enabled_by_default=True), False),
(const.DEFAULT_RULES, ['git'], Rule('git', enabled_by_default=False), False),
(conf.DEFAULT_RULES, ['git'], Rule('git', enabled_by_default=True), False),
(conf.DEFAULT_RULES, ['git'], Rule('git', enabled_by_default=False), False),
([], ['git'], Rule('git', enabled_by_default=True), False),
([], ['git'], Rule('git', enabled_by_default=False), False)])
def test_is_enabled(self, settings, rules, exclude_rules, rule, is_enabled):
@@ -108,6 +95,11 @@ class TestCommand(object):
monkeypatch.setattr('thefuck.types.Command._wait_output',
staticmethod(lambda *_: True))
@pytest.fixture(autouse=True)
def generic_shell(self, monkeypatch):
monkeypatch.setattr('thefuck.shells.from_shell', lambda x: x)
monkeypatch.setattr('thefuck.shells.to_shell', lambda x: x)
def test_from_script_calls(self, Popen, settings):
settings.env = {}
assert Command.from_raw_script(
@@ -115,7 +107,6 @@ class TestCommand(object):
'apt-get search vim', 'stdout', 'stderr')
Popen.assert_called_once_with('apt-get search vim',
shell=True,
stdin=PIPE,
stdout=PIPE,
stderr=PIPE,
env={})
@@ -131,3 +122,4 @@ class TestCommand(object):
else:
with pytest.raises(EmptyCommand):
Command.from_raw_script(script)

View File

@@ -4,37 +4,39 @@ import pytest
from itertools import islice
from thefuck import ui
from thefuck.types import CorrectedCommand
from thefuck import const
@pytest.fixture
def patch_get_key(monkeypatch):
def patch_getch(monkeypatch):
def patch(vals):
vals = iter(vals)
monkeypatch.setattr('thefuck.ui.get_key', lambda: next(vals))
def getch():
for val in vals:
if val == KeyboardInterrupt:
raise val
else:
yield val
getch_gen = getch()
monkeypatch.setattr('thefuck.ui.getch', lambda: next(getch_gen))
return patch
def test_read_actions(patch_get_key):
patch_get_key([
# Enter:
def test_read_actions(patch_getch):
patch_getch([ # Enter:
'\n',
# Enter:
'\r',
# Ignored:
'x', 'y',
# Up:
const.KEY_UP, 'k',
'\x1b', '[', 'A',
# Down:
const.KEY_DOWN, 'j',
'\x1b', '[', 'B',
# Ctrl+C:
const.KEY_CTRL_C, 'q'])
assert list(islice(ui.read_actions(), 8)) \
== [const.ACTION_SELECT, const.ACTION_SELECT,
const.ACTION_PREVIOUS, const.ACTION_PREVIOUS,
const.ACTION_NEXT, const.ACTION_NEXT,
const.ACTION_ABORT, const.ACTION_ABORT]
KeyboardInterrupt], )
assert list(islice(ui.read_actions(), 5)) \
== [ui.SELECT, ui.SELECT, ui.PREVIOUS, ui.NEXT, ui.ABORT]
def test_command_selector():
@@ -78,25 +80,25 @@ class TestSelectCommand(object):
== commands_with_side_effect[0]
assert capsys.readouterr() == ('', 'ls (+side effect)\n')
def test_with_confirmation(self, capsys, patch_get_key, commands):
patch_get_key(['\n'])
def test_with_confirmation(self, capsys, patch_getch, commands):
patch_getch(['\n'])
assert ui.select_command(iter(commands)) == commands[0]
assert capsys.readouterr() == ('', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\n')
def test_with_confirmation_abort(self, capsys, patch_get_key, commands):
patch_get_key([const.KEY_CTRL_C])
def test_with_confirmation_abort(self, capsys, patch_getch, commands):
patch_getch([KeyboardInterrupt])
assert ui.select_command(iter(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_get_key,
def test_with_confirmation_with_side_effct(self, capsys, patch_getch,
commands_with_side_effect):
patch_get_key(['\n'])
patch_getch(['\n'])
assert ui.select_command(iter(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_get_key, commands):
patch_get_key([const.KEY_DOWN, '\n'])
def test_with_confirmation_select_second(self, capsys, patch_getch, commands):
patch_getch(['\x1b', '[', 'B', '\n'])
assert ui.select_command(iter(commands)) == commands[1]
assert capsys.readouterr() == (
'', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\x1b[1K\rcd [enter/↑/↓/ctrl+c]\n')

View File

@@ -1,13 +1,9 @@
# -*- coding: utf-8 -*-
import pytest
import warnings
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, compatibility_call, \
get_valid_history_without_current
get_all_matched_commands, is_app, for_app, cache, compatibility_call
from tests.utils import Command
@@ -54,7 +50,7 @@ class TestGetClosest(object):
@pytest.fixture
def get_aliases(mocker):
mocker.patch('thefuck.shells.shell.get_aliases',
mocker.patch('thefuck.shells.get_aliases',
return_value=['vim', 'apt-get', 'fsck', 'fuck'])
@@ -166,10 +162,10 @@ class TestCache(object):
@pytest.fixture
def key(self):
if six.PY2:
return 'tests.test_utils.<function fn '
else:
if six.PY3:
return 'tests.test_utils.<function TestCache.fn.<locals>.fn '
else:
return 'tests.test_utils.<function fn '
def test_with_blank_cache(self, shelve, fn, key):
assert shelve == {}
@@ -202,7 +198,6 @@ class TestCompatibilityCall(object):
assert settings == _settings
return True
with pytest.warns(UserWarning):
assert compatibility_call(match, Command())
def test_get_new_command(self):
@@ -218,7 +213,6 @@ class TestCompatibilityCall(object):
assert settings == _settings
return True
with pytest.warns(UserWarning):
assert compatibility_call(get_new_command, Command())
def test_side_effect(self):
@@ -234,45 +228,4 @@ class TestCompatibilityCall(object):
assert settings == _settings
return True
with pytest.warns(UserWarning):
assert compatibility_call(side_effect, Command(), Command())
class TestGetValidHistoryWithoutCurrent(object):
@pytest.yield_fixture(autouse=True)
def fail_on_warning(self):
warnings.simplefilter('error')
yield
warnings.resetwarnings()
@pytest.fixture(autouse=True)
def history(self, mocker):
return mocker.patch('thefuck.shells.shell.get_history',
return_value=['le cat', 'fuck', 'ls cat',
'diff x', 'nocommand x', u'café ô'])
@pytest.fixture(autouse=True)
def alias(self, mocker):
return mocker.patch('thefuck.utils.get_alias',
return_value='fuck')
@pytest.fixture(autouse=True)
def bins(self, mocker, monkeypatch):
monkeypatch.setattr('thefuck.conf.os.environ', {'PATH': 'path'})
callables = list()
for name in ['diff', 'ls', 'café']:
bin_mock = mocker.Mock(name=name)
bin_mock.configure_mock(name=name, is_dir=lambda: False)
callables.append(bin_mock)
path_mock = mocker.Mock(iterdir=mocker.Mock(return_value=callables))
return mocker.patch('thefuck.utils.Path', return_value=path_mock)
@pytest.mark.parametrize('script, result', [
('le cat', ['ls cat', 'diff x', u'café ô']),
('diff x', ['ls cat', u'café ô']),
('fuck', ['ls cat', 'diff x', u'café ô']),
(u'cafe ô', ['ls cat', 'diff x', u'café ô']),
])
def test_get_valid_history_without_current(self, script, result):
command = Command(script=script)
assert get_valid_history_without_current(command) == result

View File

@@ -1,5 +1,5 @@
from thefuck import types
from thefuck.const import DEFAULT_PRIORITY
from thefuck.conf import DEFAULT_PRIORITY
class Command(types.Command):

View File

@@ -1,12 +1,42 @@
from imp import load_source
import os
import sys
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
from six import text_type
from . import const
ALL_ENABLED = object()
DEFAULT_RULES = [ALL_ENABLED]
DEFAULT_PRIORITY = 1000
DEFAULT_SETTINGS = {'rules': DEFAULT_RULES,
'exclude_rules': [],
'wait_command': 3,
'require_confirmation': True,
'no_colors': False,
'debug': False,
'priority': {},
'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',
'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:
#
# rules = ['cd_parent', 'git_push', 'python_command', 'sudo']
#
# The default values are as follows. Uncomment and change to fit your needs.
# See https://github.com/nvbn/thefuck#settings for more information.
#
"""
class Settings(dict):
@@ -37,25 +67,13 @@ class Settings(dict):
settings_path = self.user_dir.joinpath('settings.py')
if not settings_path.is_file():
with settings_path.open(mode='w') as settings_file:
settings_file.write(const.SETTINGS_HEADER)
for setting in const.DEFAULT_SETTINGS.items():
settings_file.write(SETTINGS_HEADER)
for setting in DEFAULT_SETTINGS.items():
settings_file.write(u'# {} = {}\n'.format(*setting))
def _get_user_dir_path(self):
# for backward compatibility, use `~/.thefuck` if it exists
legacy_user_dir = Path(os.path.expanduser('~/.thefuck'))
if legacy_user_dir.is_dir():
return legacy_user_dir
else:
default_xdg_config_dir = os.path.expanduser("~/.config")
xdg_config_dir = os.getenv("XDG_CONFIG_HOME", default_xdg_config_dir)
return Path(os.path.join(xdg_config_dir, 'thefuck'))
def _setup_user_dir(self):
"""Returns user config dir, create it when it doesn't exist."""
user_dir = self._get_user_dir_path()
user_dir = Path(os.path.expanduser('~/.thefuck'))
rules_dir = user_dir.joinpath('rules')
if not rules_dir.is_dir():
rules_dir.mkdir(parents=True)
@@ -66,14 +84,14 @@ class Settings(dict):
settings = load_source(
'settings', text_type(self.user_dir.joinpath('settings.py')))
return {key: getattr(settings, key)
for key in const.DEFAULT_SETTINGS.keys()
for key in DEFAULT_SETTINGS.keys()
if hasattr(settings, key)}
def _rules_from_env(self, val):
"""Transforms rules list from env-string to python."""
val = val.split(':')
if 'DEFAULT_RULES' in val:
val = const.DEFAULT_RULES + [rule for rule in val if rule != 'DEFAULT_RULES']
val = DEFAULT_RULES + [rule for rule in val if rule != 'DEFAULT_RULES']
return val
def _priority_from_env(self, val):
@@ -94,19 +112,16 @@ class Settings(dict):
return dict(self._priority_from_env(val))
elif attr == 'wait_command':
return int(val)
elif attr in ('require_confirmation', 'no_colors', 'debug',
'alter_history'):
elif attr in ('require_confirmation', 'no_colors', 'debug'):
return val.lower() == 'true'
elif attr == 'history_limit':
return int(val)
else:
return val
def _settings_from_env(self):
"""Loads settings from env."""
return {attr: self._val_from_env(env, attr)
for env, attr in const.ENV_TO_ATTR.items()
for env, attr in ENV_TO_ATTR.items()
if env in os.environ}
settings = Settings(const.DEFAULT_SETTINGS)
settings = Settings(DEFAULT_SETTINGS)

View File

@@ -1,56 +0,0 @@
# -*- encoding: utf-8 -*-
class _GenConst(object):
def __init__(self, name):
self._name = name
def __repr__(self):
return u'<const: {}>'.format(self._name)
KEY_UP = _GenConst('')
KEY_DOWN = _GenConst('')
KEY_CTRL_C = _GenConst('Ctrl+C')
ACTION_SELECT = _GenConst('select')
ACTION_ABORT = _GenConst('abort')
ACTION_PREVIOUS = _GenConst('previous')
ACTION_NEXT = _GenConst('next')
ALL_ENABLED = _GenConst('All rules enabled')
DEFAULT_RULES = [ALL_ENABLED]
DEFAULT_PRIORITY = 1000
DEFAULT_SETTINGS = {'rules': DEFAULT_RULES,
'exclude_rules': [],
'wait_command': 3,
'require_confirmation': True,
'no_colors': False,
'debug': False,
'priority': {},
'history_limit': None,
'alter_history': True,
'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',
'THEFUCK_DEBUG': 'debug',
'THEFUCK_PRIORITY': 'priority',
'THEFUCK_HISTORY_LIMIT': 'history_limit',
'THEFUCK_ALTER_HISTORY': 'alter_history'}
SETTINGS_HEADER = u"""# The Fuck settings file
#
# The rules are defined as in the example bellow:
#
# rules = ['cd_parent', 'git_push', 'python_command', 'sudo']
#
# The default values are as follows. Uncomment and change to fit your needs.
# See https://github.com/nvbn/thefuck#settings for more information.
#
"""

View File

@@ -1,7 +1,4 @@
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
from .conf import settings
from .types import Rule
from . import logs
@@ -58,7 +55,7 @@ def organize_commands(corrected_commands):
key=lambda corrected_command: corrected_command.priority)
logs.debug('Corrected commands: '.format(
', '.join(u'{}'.format(cmd) for cmd in [first_command] + sorted_commands)))
', '.join(str(cmd) for cmd in [first_command] + sorted_commands)))
for command in sorted_commands:
yield command

View File

@@ -28,29 +28,29 @@ def exception(title, exc_info):
def rule_failed(rule, exc_info):
exception(u'Rule {}'.format(rule.name), exc_info)
exception('Rule {}'.format(rule.name), exc_info)
def failed(msg):
sys.stderr.write(u'{red}{msg}{reset}\n'.format(
sys.stderr.write('{red}{msg}{reset}\n'.format(
msg=msg,
red=color(colorama.Fore.RED),
reset=color(colorama.Style.RESET_ALL)))
def show_corrected_command(corrected_command):
sys.stderr.write(u'{bold}{script}{reset}{side_effect}\n'.format(
sys.stderr.write('{bold}{script}{reset}{side_effect}\n'.format(
script=corrected_command.script,
side_effect=u' (+side effect)' if corrected_command.side_effect else u'',
side_effect=' (+side effect)' if corrected_command.side_effect else '',
bold=color(colorama.Style.BRIGHT),
reset=color(colorama.Style.RESET_ALL)))
def confirm_text(corrected_command):
sys.stderr.write(
(u'{clear}{bold}{script}{reset}{side_effect} '
u'[{green}enter{reset}/{blue}{reset}/{blue}{reset}'
u'/{red}ctrl+c{reset}]').format(
('{clear}{bold}{script}{reset}{side_effect} '
'[{green}enter{reset}/{blue}{reset}/{blue}{reset}'
'/{red}ctrl+c{reset}]').format(
script=corrected_command.script,
side_effect=' (+side effect)' if corrected_command.side_effect else '',
clear='\033[1K\r',

View File

@@ -1,23 +1,19 @@
# Initialize output before importing any module, that can use colorama.
from .system import init_output
init_output()
from argparse import ArgumentParser
from warnings import warn
from pprint import pformat
import sys
from . import logs, types
from .shells import shell
import colorama
from . import logs, types, shells
from .conf import settings
from .corrector import get_corrected_commands
from .exceptions import EmptyCommand
from .utils import get_installation_info, get_alias
from .utils import get_installation_info
from .ui import select_command
def fix_command():
"""Fixes previous command. Used when `thefuck` called without arguments."""
colorama.init()
settings.init()
with logs.debug_time('Total'):
logs.debug(u'Run with settings: {}'.format(pformat(settings)))
@@ -33,8 +29,6 @@ def fix_command():
if selected_command:
selected_command.run(command)
else:
sys.exit(1)
def print_alias(entry_point=True):
@@ -45,10 +39,10 @@ def print_alias(entry_point=True):
else:
position = 2
alias = get_alias()
alias = shells.thefuck_alias()
if len(sys.argv) > position:
alias = sys.argv[position]
print(shell.app_alias(alias))
print(shells.app_alias(alias))
def how_to_configure_alias():
@@ -57,8 +51,9 @@ def how_to_configure_alias():
It'll be only visible when user type fuck and when alias isn't configured.
"""
colorama.init()
settings.init()
logs.how_to_configure_alias(shell.how_to_configure())
logs.how_to_configure_alias(shells.how_to_configure())
def main():

View File

@@ -1,10 +1,8 @@
from thefuck.specific.apt import apt_available
from thefuck import shells
from thefuck.utils import memoize
from thefuck.shells import shell
try:
import CommandNotFound
enabled_by_default = apt_available
except ImportError:
enabled_by_default = False
@@ -28,5 +26,5 @@ def match(command):
def get_new_command(command):
name = get_package(command.script)
formatme = shell.and_('sudo apt-get install {}', '{}')
formatme = shells.and_('sudo apt-get install {}', '{}')
return formatme.format(name, command.script)

View File

@@ -1,9 +1,6 @@
import re
from thefuck.specific.apt import apt_available
from thefuck.utils import for_app
enabled_by_default = apt_available
@for_app('apt-get')
def match(command):

View File

@@ -1,56 +0,0 @@
import subprocess
from thefuck.specific.apt import apt_available
from thefuck.specific.sudo import sudo_support
from thefuck.utils import for_app, eager, replace_command
enabled_by_default = apt_available
@for_app('apt', 'apt-get', 'apt-cache')
@sudo_support
def match(command):
return 'E: Invalid operation' in command.stderr
@eager
def _parse_apt_operations(help_text_lines):
is_commands_list = False
for line in help_text_lines:
line = line.decode().strip()
if is_commands_list and line:
yield line.split()[0]
elif line.startswith('Basic commands:'):
is_commands_list = True
@eager
def _parse_apt_get_and_cache_operations(help_text_lines):
is_commands_list = False
for line in help_text_lines:
line = line.decode().strip()
if is_commands_list:
if not line:
return
yield line.split()[0]
elif line.startswith('Commands:'):
is_commands_list = True
def _get_operations(app):
proc = subprocess.Popen([app, '--help'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
lines = proc.stdout.readlines()
if app == 'apt':
return _parse_apt_operations(lines)
else:
return _parse_apt_get_and_cache_operations(lines)
@sudo_support
def get_new_command(command):
invalid_operation = command.stderr.split()[-1]
operations = _get_operations(command.script_parts[0])
return replace_command(command, invalid_operation, operations)

View File

@@ -1,9 +1,10 @@
import os
import re
from thefuck.utils import get_closest, replace_argument
from thefuck.specific.brew import get_brew_path_prefix, brew_available
from thefuck.utils import get_closest, replace_argument, which
from thefuck.specific.brew import get_brew_path_prefix
enabled_by_default = brew_available
enabled_by_default = bool(which('brew'))
def _get_formulas():

View File

@@ -1,14 +1,12 @@
import os
import re
from thefuck.utils import get_closest, replace_command
from thefuck.specific.brew import get_brew_path_prefix, brew_available
from thefuck.specific.brew import get_brew_path_prefix
BREW_CMD_PATH = '/Library/Homebrew/cmd'
TAP_PATH = '/Library/Taps'
TAP_CMD_PATH = '/%s/%s/cmd'
enabled_by_default = brew_available
def _get_brew_commands(brew_path_prefix):
"""To get brew default commands on local environment"""

View File

@@ -1,12 +0,0 @@
from thefuck.utils import for_app
@for_app('brew', at_least=2)
def match(command):
return ('update' in command.script
and "Error: This command updates brew itself" in command.stderr
and "Use 'brew upgrade <formula>'" in command.stderr)
def get_new_command(command):
return command.script.replace('update', 'upgrade')

View File

@@ -4,9 +4,6 @@
# > brew upgrade
# Warning: brew upgrade with no arguments will change behaviour soon!
# It currently upgrades all formula but this will soon change to require '--all'.
from thefuck.specific.brew import brew_available
enabled_by_default = brew_available
def match(command):

View File

@@ -2,14 +2,14 @@ import re
from thefuck.utils import replace_argument, for_app
@for_app('cargo', at_least=1)
@for_app('cargo')
def match(command):
return ('No such subcommand' in command.stderr
and 'Did you mean' in command.stderr)
def get_new_command(command):
broken = command.script_parts[1]
broken = command.script.split()[1]
fix = re.findall(r'Did you mean `([^`]*)`', command.stderr)[0]
return replace_argument(command.script, broken, fix)

View File

@@ -1,11 +1,11 @@
"""Attempts to spellcheck and correct failed cd commands"""
import os
import six
from difflib import get_close_matches
import re
from thefuck.specific.sudo import sudo_support
from thefuck.rules import cd_mkdir
from thefuck.utils import for_app
from thefuck import shells
__author__ = "mmussomele"
@@ -34,12 +34,9 @@ def get_new_command(command):
defaults to the rules of cd_mkdir.
Change sensitivity by changing MAX_ALLOWED_DIFF. Default value is 0.6
"""
dest = command.script_parts[1].split(os.sep)
dest = command.script.split()[1].split(os.sep)
if dest[-1] == '':
dest = dest[:-1]
if six.PY2:
cwd = os.getcwdu()
else:
cwd = os.getcwd()
for directory in dest:
if directory == ".":
@@ -47,12 +44,14 @@ def get_new_command(command):
elif directory == "..":
cwd = os.path.split(cwd)[0]
continue
best_matches = get_close_matches(directory, _get_sub_dirs(cwd), cutoff=MAX_ALLOWED_DIFF)
best_matches = get_close_matches(
directory, _get_sub_dirs(cwd), cutoff=MAX_ALLOWED_DIFF)
if best_matches:
cwd = os.path.join(cwd, best_matches[0])
else:
return cd_mkdir.get_new_command(command)
return u'cd "{0}"'.format(cwd)
repl = shells.and_('mkdir -p \\1', 'cd \\1')
return re.sub(r'^cd (.*)', repl, command.script)
return 'cd "{0}"'.format(cwd)
enabled_by_default = True

View File

@@ -1,18 +0,0 @@
import re
from thefuck.utils import for_app
from thefuck.specific.sudo import sudo_support
from thefuck.shells import shell
@sudo_support
@for_app('cd')
def match(command):
return (('no such file or directory' in command.stderr.lower()
or 'cd: can\'t cd to' in command.stderr.lower()
or 'the system cannot find the path specified.' in command.stderr.lower()))
@sudo_support
def get_new_command(command):
repl = shell.and_('mkdir -p \\1', 'cd \\1')
return re.sub(r'^cd (.*)', repl, command.script)

View File

@@ -1,15 +0,0 @@
import os
from thefuck.shells import shell
def match(command):
return (command.script.startswith('./')
and 'permission denied' in command.stderr.lower()
and os.path.exists(command.script_parts[0])
and not os.access(command.script_parts[0], os.X_OK))
def get_new_command(command):
return shell.and_(
'chmod +x {}'.format(command.script_parts[0][2:]),
command.script)

View File

@@ -1,7 +1,7 @@
from thefuck.utils import for_app
@for_app('g++', 'clang++')
@for_app(['g++', 'clang++'])
def match(command):
return ('This file requires compiler and library support for the '
'ISO C++ 2011 standard.' in command.stderr or

View File

@@ -1,12 +1,7 @@
import tarfile
import os
from thefuck import shells
from thefuck.utils import for_app
from thefuck.shells import shell
tar_extensions = ('.tar', '.tar.Z', '.tar.bz2', '.tar.gz', '.tar.lz',
'.tar.lzma', '.tar.xz', '.taz', '.tb2', '.tbz', '.tbz2',
'.tgz', '.tlz', '.txz', '.tz')
def _is_tar_extract(cmd):
@@ -19,7 +14,11 @@ def _is_tar_extract(cmd):
def _tar_file(cmd):
for c in cmd:
tar_extensions = ('.tar', '.tar.Z', '.tar.bz2', '.tar.gz', '.tar.lz',
'.tar.lzma', '.tar.xz', '.taz', '.tb2', '.tbz', '.tbz2',
'.tgz', '.tlz', '.txz', '.tz')
for c in cmd.split():
for ext in tar_extensions:
if c.endswith(ext):
return (c, c[0:len(c) - len(ext)])
@@ -29,17 +28,16 @@ def _tar_file(cmd):
def match(command):
return ('-C' not in command.script
and _is_tar_extract(command.script)
and _tar_file(command.script_parts) is not None)
and _tar_file(command.script) is not None)
def get_new_command(command):
dir = shell.quote(_tar_file(command.script_parts)[1])
return shell.and_('mkdir -p {dir}', '{cmd} -C {dir}') \
.format(dir=dir, cmd=command.script)
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):
with tarfile.TarFile(_tar_file(old_cmd.script_parts)[0]) as archive:
with tarfile.TarFile(_tar_file(old_cmd.script)[0]) as archive:
for file in archive.getnames():
try:
os.remove(file)

View File

@@ -1,15 +1,11 @@
import os
import zipfile
from thefuck.utils import for_app
from thefuck.shells import shell
def _is_bad_zip(file):
try:
with zipfile.ZipFile(file, 'r') as archive:
return len(archive.namelist()) > 1
except:
return False
def _zip_file(command):
@@ -17,29 +13,22 @@ def _zip_file(command):
# unzip [-flags] file[.zip] [file(s) ...] [-x file(s) ...]
# ^ ^ files to unzip from the archive
# archive to unzip
for c in command.script_parts[1:]:
for c in command.script.split()[1:]:
if not c.startswith('-'):
if c.endswith('.zip'):
return c
else:
return u'{}.zip'.format(c)
return '{}.zip'.format(c)
@for_app('unzip')
def match(command):
if '-d' in command.script:
return False
zip_file = _zip_file(command)
if zip_file:
return _is_bad_zip(zip_file)
else:
return False
return ('-d' not in command.script
and _is_bad_zip(_zip_file(command)))
def get_new_command(command):
return u'{} -d {}'.format(
command.script, shell.quote(_zip_file(command)[:-4]))
return '{} -d {}'.format(command.script, _zip_file(command)[:-4])
def side_effect(old_cmd, command):

View File

@@ -1,13 +1,11 @@
def match(command):
split_command = command.script_parts
split_command = command.script.split()
return (split_command
and len(split_command) >= 2
and split_command[0] == split_command[1])
return len(split_command) >= 2 and split_command[0] == split_command[1]
def get_new_command(command):
return ' '.join(command.script_parts[1:])
return command.script[command.script.find(' ')+1:]
# it should be rare enough to actually have to type twice the same word, so
# this rule can have a higher priority to come before things like "cd cd foo"

View File

@@ -2,7 +2,7 @@ import re
import os
from thefuck.utils import memoize, default_settings
from thefuck.conf import settings
from thefuck.shells import shell
from thefuck import shells
# order is important: only the first match is considered
@@ -58,7 +58,7 @@ def match(command):
return _search(command.stderr) or _search(command.stdout)
@default_settings({'fixlinecmd': u'{editor} {file} +{line}',
@default_settings({'fixlinecmd': '{editor} {file} +{line}',
'fixcolcmd': None})
def get_new_command(command):
m = _search(command.stderr) or _search(command.stdout)
@@ -75,4 +75,4 @@ def get_new_command(command):
file=m.group('file'),
line=m.group('line'))
return shell.and_(editor_call, command.script)
return shells.and_(editor_call, command.script)

View File

@@ -1,18 +1,19 @@
import re
from thefuck.shells import shell
from thefuck import shells
from thefuck.specific.git import git_support
@git_support
def match(command):
return 'did not match any file(s) known to git.' in command.stderr
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):
missing_file = re.findall(
r"error: pathspec '([^']*)' "
r'did not match any file\(s\) known to git.', command.stderr)[0]
r"did not match any file\(s\) known to git.", command.stderr)[0]
formatme = shell.and_('git add -- {}', '{}')
formatme = shells.and_('git add -- {}', '{}')
return formatme.format(missing_file, command.script)

View File

@@ -1,23 +0,0 @@
import re
from thefuck.shells import shell
from thefuck.specific.git import git_support
from thefuck.utils import eager
@git_support
def match(command):
return ('branch' in command.script
and "fatal: A branch named '" in command.stderr
and " already exists." in command.stderr)
@git_support
@eager
def get_new_command(command):
branch_name = re.findall(
r"fatal: A branch named '([^']*)' already exists.", command.stderr)[0]
new_command_templates = [['git branch -d {0}', 'git branch {0}'],
['git branch -D {0}', 'git branch {0}'],
['git checkout {0}']]
for new_command_template in new_command_templates:
yield shell.and_(*new_command_template).format(branch_name)

View File

@@ -1,14 +1,13 @@
from thefuck.shells import shell
from thefuck import shells
from thefuck.specific.git import git_support
@git_support
def match(command):
# catches "git branch list" in place of "git branch"
return (command.script_parts
and command.script_parts[1:] == 'branch list'.split())
return command.script.split()[1:] == 'branch list'.split()
@git_support
def get_new_command(command):
return shell.and_('git branch --delete list', 'git branch')
return shells.and_('git branch --delete list', 'git branch')

View File

@@ -1,9 +1,8 @@
import re
import subprocess
from thefuck import utils
from thefuck import shells, utils
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
from thefuck.shells import shell
@git_support
@@ -35,5 +34,5 @@ def get_new_command(command):
if closest_branch:
return replace_argument(command.script, missing_file, closest_branch)
else:
return shell.and_('git branch {}', '{}').format(
return shells.and_('git branch {}', '{}').format(
missing_file, command.script)

View File

@@ -5,8 +5,9 @@ from thefuck.specific.git import git_support
@git_support
def match(command):
if command.script_parts and len(command.script_parts) > 1:
return (command.script_parts[1] == 'stash'
splited_script = command.script.split()
if len(splited_script) > 1:
return (splited_script[1] == 'stash'
and 'usage:' in command.stderr)
else:
return False
@@ -25,12 +26,12 @@ stash_commands = (
@git_support
def get_new_command(command):
stash_cmd = command.script_parts[2]
stash_cmd = command.script.split()[2]
fixed = utils.get_closest(stash_cmd, stash_commands, fallback_to_first=False)
if fixed is not None:
return replace_argument(command.script, stash_cmd, fixed)
else:
cmd = command.script_parts[:]
cmd = command.script.split()
cmd.insert(2, 'save')
return ' '.join(cmd)

View File

@@ -1,12 +0,0 @@
from thefuck.specific.git import git_support
@git_support
def match(command):
return 'help' in command.script and ' is aliased to ' in command.stdout
@git_support
def get_new_command(command):
aliased = command.stdout.split('`', 2)[2].split("'", 1)[0].split(' ', 1)[0]
return 'git help {}'.format(aliased)

View File

@@ -1,4 +1,4 @@
from thefuck.shells import shell
from thefuck import shells
from thefuck.specific.git import git_support
@@ -14,4 +14,4 @@ def get_new_command(command):
branch = line.split(' ')[-1]
set_upstream = line.replace('<remote>', 'origin')\
.replace('<branch>', branch)
return shell.and_(set_upstream, command.script)
return shells.and_(set_upstream, command.script)

View File

@@ -12,7 +12,7 @@ def match(command):
@git_support
def get_new_command(command):
return replace_argument(command.script, 'push', 'push --force-with-lease')
return replace_argument(command.script, 'push', 'push --force')
enabled_by_default = False

View File

@@ -1,4 +1,4 @@
from thefuck.shells import shell
from thefuck import shells
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@@ -13,5 +13,5 @@ def match(command):
@git_support
def get_new_command(command):
return shell.and_(replace_argument(command.script, 'push', 'pull'),
return shells.and_(replace_argument(command.script, 'push', 'pull'),
command.script)

View File

@@ -1,15 +0,0 @@
from thefuck.specific.git import git_support
@git_support
def match(command):
return ({'rebase', '--continue'}.issubset(command.script_parts) and
'No changes - did you forget to use \'git add\'?' in command.stdout)
def get_new_command(command):
return 'git rebase --skip'
enabled_by_default = True
requires_output = True

View File

@@ -1,14 +0,0 @@
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@git_support
def match(command):
return ('set-url' in command.script and 'fatal: No such remote' in command.stderr)
def get_new_command(command):
return replace_argument(command.script, 'set-url', 'add')
enabled_by_default = True

Some files were not shown because too many files have changed in this diff Show More