Compare commits

..

No commits in common. "master" and "3.31" have entirely different histories.
master ... 3.31

77 changed files with 225 additions and 920 deletions

View File

@ -1,10 +0,0 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.163.1/containers/python-3/.devcontainer/base.Dockerfile
# [Choice] Python version: 3, 3.9, 3.8, 3.7, 3.6
ARG VARIANT="3"
FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}
# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.
COPY requirements.txt /tmp/pip-tmp/
RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \
&& rm -rf /tmp/pip-tmp

View File

@ -1,39 +0,0 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.163.1/containers/python-3
{
"name": "Python 3",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.profiles.linux": {
"bash (login)": {
"path": "bash",
"args": ["-l"]
}
},
"python.pythonPath": "/usr/local/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"ms-python.python"
],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "pip3 install -r requirements.txt && python3 setup.py develop"
}

View File

@ -3,14 +3,14 @@ name: Tests
on: [push, pull_request]
env:
PYTHON_LATEST: "3.11"
PYTHON_LATEST: 3.9
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12-dev"]
python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10-dev]
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
@ -41,28 +41,9 @@ jobs:
run: coverage run --source=thefuck,tests -m pytest -v --capture=sys tests
- name: Run tests (including functional)
if: matrix.os == 'ubuntu-latest' && matrix.python-version == env.PYTHON_LATEST
run: |
docker build -t thefuck/python3 -f tests/Dockerfile --build-arg PYTHON_VERSION=3 .
docker build -t thefuck/python2 -f tests/Dockerfile --build-arg PYTHON_VERSION=2 .
coverage run --source=thefuck,tests -m pytest -v --capture=sys tests --enable-functional
run: coverage run --source=thefuck,tests -m pytest -v --capture=sys tests --enable-functional
- name: Post coverage results
if: matrix.os == 'ubuntu-latest' && matrix.python-version == env.PYTHON_LATEST
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: coveralls --service=github
test-deprecated:
strategy:
matrix:
python-version: ["2.7", "3.6"]
runs-on: ubuntu-latest
container: python:${{ matrix.python-version }}
steps:
- uses: actions/checkout@v2
- name: Install The Fuck with all dependencies
run: |
pip install -Ur requirements.txt coveralls
python setup.py develop
- name: Lint
run: flake8
- name: Run tests
run: coverage run --source=thefuck,tests -m pytest -v --capture=sys tests

View File

@ -26,13 +26,6 @@ fixes, etc.
# Developing
In order to develop locally, there are two options:
- Develop using a local installation of Python 3 and setting up a virtual environment
- Develop using an automated VSCode Dev Container.
## Develop using local Python installation
[Create and activate a Python 3 virtual environment.](https://docs.python.org/3/tutorial/venv.html)
Install `The Fuck` for development:
@ -51,13 +44,13 @@ flake8
Run unit tests:
```bash
pytest
py.test
```
Run unit and functional tests (requires docker):
```bash
pytest --enable-functional
py.test --enable-functional
```
For sending package to pypi:
@ -66,27 +59,3 @@ For sending package to pypi:
sudo apt-get install pandoc
./release.py
```
## Develop using Dev Container
To make local development easier a [VSCode Devcontainer](https://code.visualstudio.com/docs/remote/remote-overview) is included with this repository. This will allows you to spin up a Docker container with all the necessary prerequisites for this project pre-installed ready to go, no local Python install/setup required.
### Prerequisites
To use the container you require:
- [Docker](https://www.docker.com/products/docker-desktop)
- [VSCode](https://code.visualstudio.com/)
- [VSCode Remote Development Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack)
- [Windows Users Only]: [Installation of WSL2 and configuration of Docker to use it](https://docs.docker.com/docker-for-windows/wsl/)
Full notes about [installation are here](https://code.visualstudio.com/docs/remote/containers#_installation)
### Running the container
Assuming you have the prerequisites:
1. Open VSCode
1. Open command palette (CMD+SHIFT+P (mac) or CTRL+SHIFT+P (windows))
1. Select `Remote-Containers: Reopen in Container`.
1. Container will be built, install all pip requirements and your VSCode will mount into it automagically.
1. Your VSCode and container now essentially become a throw away environment.

View File

@ -1,7 +1,7 @@
The MIT License (MIT)
=====================
Copyright (c) 2015-2022 Vladimir Iakovlev
Copyright (c) 2015-2021 Vladimir Iakovlev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -106,7 +106,7 @@ Reading package lists... Done
## Requirements
- python (3.5+)
- python (3.4+)
- pip
- python-dev
@ -114,7 +114,7 @@ Reading package lists... Done
## Installation
On macOS or Linux, you can install *The Fuck* via [Homebrew][homebrew]:
On OS X, you can install *The Fuck* via [Homebrew][homebrew] (or via [Linuxbrew][linuxbrew] on Linux):
```bash
brew install thefuck
@ -124,7 +124,7 @@ On Ubuntu / Mint, install *The Fuck* with the following commands:
```bash
sudo apt update
sudo apt install python3-dev python3-pip python3-setuptools
pip3 install thefuck --user
sudo pip3 install thefuck
```
On FreeBSD, install *The Fuck* with the following commands:
@ -137,11 +137,6 @@ On ChromeOS, install *The Fuck* using [chromebrew](https://github.com/skycocker/
crew install thefuck
```
On Arch based systems, install *The Fuck* with the following command:
```
sudo pacman -S thefuck
```
On other systems, install *The Fuck* by using `pip`:
```bash
@ -187,12 +182,6 @@ pip3 install thefuck --upgrade
**Note: Alias functionality was changed in v1.34 of *The Fuck***
## Uninstall
To remove *The Fuck*, reverse the installation process:
- erase or comment *thefuck* alias line from your Bash, Zsh, Fish, Powershell, tcsh, ... shell config
- use your package manager (brew, pip3, pkg, crew, pip) to uninstall the binaries
## How it works
*The Fuck* attempts to match the previous command with a rule. If a match is
@ -236,11 +225,8 @@ following rules are enabled by default:
* `git_branch_delete_checked_out` – changes `git branch -d` to `git checkout master && git branch -D` when trying to delete a checked out branch;
* `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_branch_0flag` – fixes commands such as `git branch 0v` and `git branch 0r` removing the created branch;
* `git_checkout` – fixes branch name or creates new branch;
* `git_clone_git_clone` – replaces `git clone git clone ...` with `git clone ...`
* `git_clone_missing` – adds `git clone` to URLs that appear to link to a git repository.
* `git_commit_add` – offers `git commit -a ...` or `git commit -p ...` after previous commit if it failed because nothing was staged;
* `git_commit_amend` – offers `git commit --amend` after previous commit;
* `git_commit_reset` – offers `git reset HEAD~` after previous commit;
* `git_diff_no_index` – adds `--no-index` to previous `git diff` on untracked files;
@ -250,7 +236,6 @@ following rules are enabled by default:
* `git_help_aliased` &ndash; fixes `git help <alias>` commands replacing <alias> with the aliased command;
* `git_hook_bypass` &ndash; adds `--no-verify` flag previous to `git am`, `git commit`, or `git push` command;
* `git_lfs_mistype` &ndash; fixes mistyped `git lfs <command>` commands;
* `git_main_master` &ndash; fixes incorrect branch name between `main` and `master`
* `git_merge` &ndash; adds remote to branch names;
* `git_merge_unrelated` &ndash; adds `--allow-unrelated-histories` when required
* `git_not_command` &ndash; fixes wrong git commands like `git brnch`;
@ -258,7 +243,7 @@ following rules are enabled by default:
* `git_pull_clone` &ndash; clones instead of pulling when the repo does not exist;
* `git_pull_uncommitted_changes` &ndash; stashes changes before pulling and pops them afterwards;
* `git_push` &ndash; adds `--set-upstream origin $branch` to previous failed `git push`;
* `git_push_different_branch_names` &ndash; fixes pushes when local branch name does not match remote branch name;
* `git_push_different_branch_names` &ndash; fixes pushes when local brach name does not match remote branch name;
* `git_push_pull` &ndash; runs `git pull` when `push` was rejected;
* `git_push_without_commits` &ndash; Creates an initial commit if you forget and only `git add .`, when setting up a new project;
* `git_rebase_no_changes` &ndash; runs `git rebase --skip` instead of `git rebase --continue` when there are no changes;
@ -283,7 +268,7 @@ following rules are enabled by default:
* `has_exists_script` &ndash; prepends `./` when script/binary exists;
* `heroku_multiple_apps` &ndash; add `--app <app>` to `heroku` commands like `heroku pg`;
* `heroku_not_command` &ndash; fixes wrong `heroku` commands like `heroku log`;
* `history` &ndash; tries to replace command with the most similar command from history;
* `history` &ndash; tries to replace command with most similar command from history;
* `hostscli` &ndash; tries to fix `hostscli` usage;
* `ifconfig_device_not_found` &ndash; fixes wrong device names like `wlan0` to `wlp2s0`;
* `java` &ndash; removes `.java` extension when running Java programs;
@ -298,7 +283,7 @@ following rules are enabled by default:
* `man_no_space` &ndash; fixes man commands without spaces, for example `mandiff`;
* `mercurial` &ndash; fixes wrong `hg` commands;
* `missing_space_before_subcommand` &ndash; fixes command with missing space like `npminstall`;
* `mkdir_p` &ndash; adds `-p` when you try to create a directory without a parent;
* `mkdir_p` &ndash; adds `-p` when you try to create a directory without parent;
* `mvn_no_command` &ndash; adds `clean package` to `mvn`;
* `mvn_unknown_lifecycle_phase` &ndash; fixes misspelled life cycle phases with `mvn`;
* `npm_missing_script` &ndash; fixes `npm` custom script name in `npm run-script <script>`;
@ -317,33 +302,30 @@ following rules are enabled by default:
* `python_execute` &ndash; appends missing `.py` when executing Python files;
* `python_module_error` &ndash; fixes ModuleNotFoundError by trying to `pip install` that module;
* `quotation_marks` &ndash; fixes uneven usage of `'` and `"` when containing args';
* `path_from_history` &ndash; replaces not found path with a similar absolute path from history;
* `rails_migrations_pending` &ndash; runs pending migrations;
* `path_from_history` &ndash; replaces not found path with similar absolute path from history;
* `react_native_command_unrecognized` &ndash; fixes unrecognized `react-native` commands;
* `remove_shell_prompt_literal` &ndash; remove leading shell prompt symbol `$`, common when copying commands from documentations;
* `remove_trailing_cedilla` &ndash; remove trailing cedillas `ç`, a common typo for European keyboard layouts;
* `remove_trailing_cedilla` &ndash; remove trailing cedillas `ç`, a common typo for european keyboard layouts;
* `rm_dir` &ndash; adds `-rf` when you try to remove a directory;
* `scm_correction` &ndash; corrects wrong scm like `hg log` to `git log`;
* `sed_unterminated_s` &ndash; adds missing '/' to `sed`'s `s` commands;
* `sl_ls` &ndash; changes `sl` to `ls`;
* `ssh_known_hosts` &ndash; removes host from `known_hosts` on warning;
* `sudo` &ndash; prepends `sudo` to the previous command if it failed because of permissions;
* `sudo` &ndash; prepends `sudo` to previous command if it failed because of permissions;
* `sudo_command_from_user_path` &ndash; runs commands from users `$PATH` with `sudo`;
* `switch_lang` &ndash; switches command from your local layout to en;
* `systemctl` &ndash; correctly orders parameters of confusing `systemctl`;
* `terraform_init.py` &ndash; run `terraform init` before plan or apply;
* `terraform_no_command.py` &ndash; fixes unrecognized `terraform` commands;
* `test.py` &ndash; runs `pytest` instead of `test.py`;
* `test.py` &ndash; runs `py.test` instead of `test.py`;
* `touch` &ndash; creates missing directories before "touching";
* `tsuru_login` &ndash; runs `tsuru login` if not authenticated or session expired;
* `tsuru_not_command` &ndash; fixes wrong `tsuru` commands like `tsuru shell`;
* `tmux` &ndash; fixes `tmux` commands;
* `unknown_command` &ndash; fixes hadoop hdfs-style "unknown command", for example adds missing '-' to the command on `hdfs dfs ls`;
* `unsudo` &ndash; removes `sudo` from previous command if a process refuses to run on superuser privilege.
* `unsudo` &ndash; removes `sudo` from previous command if a process refuses to run on super user privilege.
* `vagrant_up` &ndash; starts up the vagrant instance;
* `whois` &ndash; fixes `whois` command;
* `workon_doesnt_exists` &ndash; fixes `virtualenvwrapper` env name os suggests to create new.
* `wrong_hyphen_before_subcommand` &ndash; removes an improperly placed hyphen (`apt-install` -> `apt install`, `git-log` -> `git log`, etc.)
* `yarn_alias` &ndash; fixes aliased `yarn` commands like `yarn ls`;
* `yarn_command_not_found` &ndash; fixes misspelled `yarn` commands;
* `yarn_command_replaced` &ndash; fixes replaced `yarn` commands;
@ -367,9 +349,9 @@ The following rules are enabled by default on specific platforms only:
* `brew_update_formula` &ndash; turns `brew update <formula>` into `brew upgrade <formula>`;
* `dnf_no_such_command` &ndash; fixes mistyped DNF commands;
* `nixos_cmd_not_found` &ndash; installs apps on NixOS;
* `pacman` &ndash; installs app with `pacman` if it is not installed (uses `yay`, `pikaur` or `yaourt` if available);
* `pacman` &ndash; installs app with `pacman` if it is not installed (uses `yay` or `yaourt` if available);
* `pacman_invalid_option` &ndash; replaces lowercase `pacman` options with uppercase.
* `pacman_not_found` &ndash; fixes package name with `pacman`, `yay`, `pikaur` or `yaourt`.
* `pacman_not_found` &ndash; fixes package name with `pacman`, `yay` or `yaourt`.
* `yum_invalid_operation` &ndash; fixes invalid `yum` calls, like `yum isntall vim`;
The following commands are bundled with *The Fuck*, but are not enabled by
@ -443,15 +425,15 @@ Several *The Fuck* parameters can be changed in the file `$XDG_CONFIG_HOME/thefu
* `rules` &ndash; list of enabled rules, by default `thefuck.const.DEFAULT_RULES`;
* `exclude_rules` &ndash; list of disabled rules, by default `[]`;
* `require_confirmation` &ndash; requires confirmation before running new command, by default `True`;
* `wait_command` &ndash; the max amount of time in seconds for getting previous command output;
* `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; the numeric value of how many history commands will be scanned, like `2000`;
* `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`;
* `wait_slow_command` &ndash; max amount of time in seconds for getting previous command output if it in `slow_commands` list;
* `slow_commands` &ndash; list of slow commands;
* `num_close_matches` &ndash; the maximum number of close matches to suggest, by default `3`.
* `num_close_matches` &ndash; maximum number of close matches to suggest, by default `3`.
* `excluded_search_path_prefixes` &ndash; path prefixes to ignore when searching for commands, by default `[]`.
An example of `settings.py`:
@ -475,16 +457,16 @@ Or via environment variables:
* `THEFUCK_RULES` &ndash; list of enabled rules, like `DEFAULT_RULES:rm_root` or `sudo:no_command`;
* `THEFUCK_EXCLUDE_RULES` &ndash; list of disabled rules, like `git_pull:git_push`;
* `THEFUCK_REQUIRE_CONFIRMATION` &ndash; require confirmation before running new command, `true/false`;
* `THEFUCK_WAIT_COMMAND` &ndash; the max amount of time in seconds for getting previous command output;
* `THEFUCK_WAIT_COMMAND` &ndash; max amount of time in seconds for getting previous command output;
* `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_WAIT_SLOW_COMMAND` &ndash; the max amount of time in seconds for getting previous command output if it in `slow_commands` list;
* `THEFUCK_WAIT_SLOW_COMMAND` &ndash; max amount of time in seconds for getting previous command output if it in `slow_commands` list;
* `THEFUCK_SLOW_COMMANDS` &ndash; list of slow commands, like `lein:gradle`;
* `THEFUCK_NUM_CLOSE_MATCHES` &ndash; the maximum number of close matches to suggest, like `5`.
* `THEFUCK_NUM_CLOSE_MATCHES` &ndash; maximum number of close matches to suggest, like `5`.
* `THEFUCK_EXCLUDED_SEARCH_PATH_PREFIXES` &ndash; path prefixes to ignore when searching for commands, by default `[]`.
For example:
@ -562,5 +544,6 @@ Project License can be found [here](LICENSE.md).
[examples-link]: https://raw.githubusercontent.com/nvbn/thefuck/master/example.gif
[instant-mode-gif-link]: https://raw.githubusercontent.com/nvbn/thefuck/master/example_instant_mode.gif
[homebrew]: https://brew.sh/
[linuxbrew]: https://linuxbrew.sh/
##### [Back to Contents](#contents)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 631 KiB

After

Width:  |  Height:  |  Size: 704 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 457 KiB

After

Width:  |  Height:  |  Size: 535 KiB

View File

@ -1,2 +0,0 @@
@set PYTHONIOENCODING=utf-8
@powershell -noprofile -c "cmd /c \"$(thefuck %* $(doskey /history)[-2])\"; [Console]::ResetColor();"

View File

@ -1,22 +0,0 @@
if ((Get-Command "fuck").CommandType -eq "Function") {
fuck @args;
[Console]::ResetColor()
exit
}
"First time use of thefuck detected. "
if ((Get-Content $PROFILE -Raw -ErrorAction Ignore) -like "*thefuck*") {
} else {
" - Adding thefuck intialization to user `$PROFILE"
$script = "`n`$env:PYTHONIOENCODING='utf-8' `niex `"`$(thefuck --alias)`"";
Write-Output $script | Add-Content $PROFILE
}
" - Adding fuck() function to current session..."
$env:PYTHONIOENCODING='utf-8'
iex "$($(thefuck --alias).Replace("function fuck", "function global:fuck"))"
" - Invoking fuck()`n"
fuck @args;
[Console]::ResetColor()

View File

@ -31,26 +31,14 @@ elif (3, 0) < version < (3, 5):
' ({}.{} detected).'.format(*version))
sys.exit(-1)
VERSION = '3.32'
VERSION = '3.31'
install_requires = ['psutil', 'colorama', 'six']
install_requires = ['psutil', 'colorama', 'six', 'decorator', 'pyte']
extras_require = {':python_version<"3.4"': ['pathlib2'],
':python_version<"3.3"': ['backports.shutil_get_terminal_size'],
':python_version<="2.7"': ['decorator<5', 'pyte<0.8.1'],
':python_version>"2.7"': ['decorator', 'pyte'],
':python_version<="2.7"': ['decorator<5'],
":sys_platform=='win32'": ['win_unicode_console']}
if sys.platform == "win32":
scripts = ['scripts\\fuck.bat', 'scripts\\fuck.ps1']
entry_points = {'console_scripts': [
'thefuck = thefuck.entrypoints.main:main',
'thefuck_firstuse = thefuck.entrypoints.not_configured:main']}
else:
scripts = []
entry_points = {'console_scripts': [
'thefuck = thefuck.entrypoints.main:main',
'fuck = thefuck.entrypoints.not_configured:main']}
setup(name='thefuck',
version=VERSION,
description="Magnificent app which corrects your previous console command",
@ -63,8 +51,8 @@ setup(name='thefuck',
'tests', 'tests.*', 'release']),
include_package_data=True,
zip_safe=False,
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*',
install_requires=install_requires,
extras_require=extras_require,
scripts=scripts,
entry_points=entry_points)
entry_points={'console_scripts': [
'thefuck = thefuck.entrypoints.main:main',
'fuck = thefuck.entrypoints.not_configured:main']})

View File

@ -1,7 +0,0 @@
ARG PYTHON_VERSION
FROM python:${PYTHON_VERSION}
RUN apt-get update -y
RUN apt-get install -yy --no-install-recommends --no-install-suggests fish tcsh zsh
RUN pip install --upgrade pip
COPY . /src
RUN pip install /src

View File

@ -1,6 +1,6 @@
from mock import Mock
import pytest
from thefuck.entrypoints.alias import _get_alias, print_alias
from thefuck.entrypoints.alias import _get_alias
@pytest.mark.parametrize(
@ -28,12 +28,3 @@ def test_get_alias(monkeypatch, mocker, py2,
assert alias == 'instant_mode_alias'
else:
assert alias == 'app_alias'
def test_print_alias(mocker):
settings_mock = mocker.patch('thefuck.entrypoints.alias.settings')
_get_alias_mock = mocker.patch('thefuck.entrypoints.alias._get_alias')
known_args = Mock()
print_alias(known_args)
settings_mock.init.assert_called_once_with(known_args)
_get_alias_mock.assert_called_once_with(known_args)

View File

@ -5,8 +5,8 @@ from thefuck.entrypoints.fix_command import _get_raw_command
class TestGetRawCommand(object):
def test_from_force_command_argument(self):
known_args = Mock(force_command='git brunch')
assert _get_raw_command(known_args) == ['git brunch']
known_args = Mock(force_command=['git', 'brunch'])
assert _get_raw_command(known_args) == ['git', 'brunch']
def test_from_command_argument(self, os_environ):
os_environ['TF_HISTORY'] = None

View File

@ -1,20 +0,0 @@
import pytest
from pytest_docker_pexpect.docker import run as pexpect_docker_run, \
stats as pexpect_docker_stats
@pytest.fixture(autouse=True)
def build_container_mock(mocker):
return mocker.patch('pytest_docker_pexpect.docker.build_container')
def run_side_effect(*args, **kwargs):
container_id = pexpect_docker_run(*args, **kwargs)
pexpect_docker_stats(container_id)
return container_id
@pytest.fixture(autouse=True)
def run_mock(mocker):
return mocker.patch('pytest_docker_pexpect.docker.run', side_effect=run_side_effect)

View File

@ -20,12 +20,10 @@ def with_confirmation(proc, TIMEOUT):
assert proc.expect([TIMEOUT, u'test'])
def history_changed(proc, TIMEOUT, *to):
def history_changed(proc, TIMEOUT, to):
"""Ensures that history changed."""
proc.send('\033[A')
pattern = [TIMEOUT]
pattern.extend(to)
assert proc.expect(pattern)
assert proc.expect([TIMEOUT, to])
def history_not_changed(proc, TIMEOUT):
@ -46,14 +44,14 @@ def select_command_with_arrows(proc, TIMEOUT):
proc.send('\033[B')
assert proc.expect([TIMEOUT, u'git push'])
proc.send('\033[B')
assert proc.expect([TIMEOUT, u'git help', u'git hook'])
assert proc.expect([TIMEOUT, u'git help'])
proc.send('\033[A')
assert proc.expect([TIMEOUT, u'git push'])
proc.send('\033[B')
assert proc.expect([TIMEOUT, u'git help', u'git hook'])
assert proc.expect([TIMEOUT, u'git help'])
proc.send('\n')
assert proc.expect([TIMEOUT, u'usage', u'fatal: not a git repository'])
assert proc.expect([TIMEOUT, u'usage'])
def refuse_with_confirmation(proc, TIMEOUT):

View File

@ -4,12 +4,12 @@ from tests.functional.plots import with_confirmation, without_confirmation, \
select_command_with_arrows, how_to_configure
python_3 = (u'thefuck/python3',
u'',
python_3 = (u'thefuck/python3-bash',
u'FROM python:3',
u'sh')
python_2 = (u'thefuck/python2',
u'',
python_2 = (u'thefuck/python2-bash',
u'FROM python:2',
u'sh')
@ -28,6 +28,8 @@ echo "instant mode ready: $THEFUCK_INSTANT_MODE"
def proc(request, spawnu, TIMEOUT):
container, instant_mode = request.param
proc = spawnu(*container)
proc.sendline(u"pip install /src")
assert proc.expect([TIMEOUT, u'Successfully installed'])
proc.sendline(init_bashrc.format(
u'--enable-experimental-instant-mode' if instant_mode else ''))
proc.sendline(u"bash")
@ -45,7 +47,7 @@ def test_with_confirmation(proc, TIMEOUT):
@pytest.mark.functional
def test_select_command_with_arrows(proc, TIMEOUT):
select_command_with_arrows(proc, TIMEOUT)
history_changed(proc, TIMEOUT, u'git help', u'git hook')
history_changed(proc, TIMEOUT, u'git help')
@pytest.mark.functional

View File

@ -2,13 +2,29 @@ import pytest
from tests.functional.plots import with_confirmation, without_confirmation, \
refuse_with_confirmation, select_command_with_arrows
containers = ((u'thefuck/python3', u'', u'fish'),
(u'thefuck/python2', u'', u'fish'))
containers = (('thefuck/python3-fish',
u'''FROM python:3
# Use jessie-backports since it has the fish package. See here for details:
# https://github.com/tianon/docker-brew-debian/blob/88ae21052affd8a14553bb969f9d41c464032122/jessie/backports/Dockerfile
RUN awk '$1 ~ "^deb" { $3 = $3 "-backports"; print; exit }' /etc/apt/sources.list > /etc/apt/sources.list.d/backports.list
RUN apt-get update
RUN apt-get install -yy fish''',
u'fish'),
('thefuck/python2-fish',
u'''FROM python:2
# Use jessie-backports since it has the fish package. See here for details:
# https://github.com/tianon/docker-brew-debian/blob/88ae21052affd8a14553bb969f9d41c464032122/jessie/backports/Dockerfile
RUN awk '$1 ~ "^deb" { $3 = $3 "-backports"; print; exit }' /etc/apt/sources.list > /etc/apt/sources.list.d/backports.list
RUN apt-get update
RUN apt-get install -yy fish''',
u'fish'))
@pytest.fixture(params=containers)
def proc(request, spawnu, TIMEOUT):
proc = spawnu(*request.param)
proc.sendline(u"pip install /src")
assert proc.expect([TIMEOUT, u'Successfully installed'])
proc.sendline(u'thefuck --alias > ~/.config/fish/config.fish')
proc.sendline(u'fish')
return proc

View File

@ -2,13 +2,23 @@ import pytest
from tests.functional.plots import with_confirmation, without_confirmation, \
refuse_with_confirmation, select_command_with_arrows
containers = ((u'thefuck/python3', u'', u'tcsh'),
(u'thefuck/python2', u'', u'tcsh'))
containers = (('thefuck/python3-tcsh',
u'''FROM python:3
RUN apt-get update
RUN apt-get install -yy tcsh''',
u'tcsh'),
('thefuck/python2-tcsh',
u'''FROM python:2
RUN apt-get update
RUN apt-get install -yy tcsh''',
u'tcsh'))
@pytest.fixture(params=containers)
def proc(request, spawnu, TIMEOUT):
proc = spawnu(*request.param)
proc.sendline(u'pip install /src')
assert proc.expect([TIMEOUT, u'Successfully installed'])
proc.sendline(u'tcsh')
proc.sendline(u'setenv PYTHONIOENCODING utf8')
proc.sendline(u'eval `thefuck --alias`')

View File

@ -4,8 +4,17 @@ from tests.functional.plots import with_confirmation, without_confirmation, \
select_command_with_arrows, how_to_configure
python_3 = (u'thefuck/python3', u'', u'sh')
python_2 = (u'thefuck/python2', u'', u'sh')
python_3 = ('thefuck/python3-zsh',
u'''FROM python:3
RUN apt-get update
RUN apt-get install -yy zsh''',
u'sh')
python_2 = ('thefuck/python2-zsh',
u'''FROM python:2
RUN apt-get update
RUN apt-get install -yy zsh''',
u'sh')
init_zshrc = u'''echo '
@ -26,6 +35,8 @@ echo "instant mode ready: $THEFUCK_INSTANT_MODE"
def proc(request, spawnu, TIMEOUT):
container, instant_mode = request.param
proc = spawnu(*container)
proc.sendline(u'pip install /src')
assert proc.expect([TIMEOUT, u'Successfully installed'])
proc.sendline(init_zshrc.format(
u'--enable-experimental-instant-mode' if instant_mode else ''))
proc.sendline(u"zsh")
@ -43,7 +54,7 @@ def test_with_confirmation(proc, TIMEOUT):
@pytest.mark.functional
def test_select_command_with_arrows(proc, TIMEOUT):
select_command_with_arrows(proc, TIMEOUT)
history_changed(proc, TIMEOUT, u'git help', u'git hook')
history_changed(proc, TIMEOUT, u'git help')
@pytest.mark.functional

View File

@ -1,7 +1,5 @@
# -*- encoding: utf-8 -*-
import pytest
import sys
from mock import Mock, patch
from psutil import AccessDenied, TimeoutExpired
@ -24,20 +22,6 @@ class TestRerun(object):
assert rerun.get_output('', '') is None
wait_output_mock.assert_called_once()
@patch('thefuck.output_readers.rerun.Popen')
def test_get_output_invalid_continuation_byte(self, popen_mock):
output = b'ls: illegal option -- \xc3\nusage: ls [-@ABC...] [file ...]\n'
expected = u'ls: illegal option -- \ufffd\nusage: ls [-@ABC...] [file ...]\n'
popen_mock.return_value.stdout.read.return_value = output
actual = rerun.get_output('', '')
assert actual == expected
@pytest.mark.skipif(sys.platform == 'win32', reason="skip when running on Windows")
@patch('thefuck.output_readers.rerun._wait_output')
def test_get_output_unicode_misspell(self, wait_output_mock):
rerun.get_output(u'pácman', u'pácman')
wait_output_mock.assert_called_once()
def test_wait_output_is_slow(self, settings):
assert rerun._wait_output(Mock(), True)
self.proc_mock.wait.assert_called_once_with(settings.wait_slow_command)

View File

@ -1,26 +1,17 @@
import pytest
from thefuck.rules.brew_install import match, get_new_command, _get_suggestions
from thefuck.rules.brew_install import match, get_new_command
from thefuck.rules.brew_install import _get_formulas
from thefuck.types import Command
@pytest.fixture
def brew_no_available_formula_one():
return '''Warning: No available formula with the name "giss". Did you mean gist?'''
@pytest.fixture
def brew_no_available_formula_two():
return '''Warning: No available formula with the name "elasticserar". Did you mean elasticsearch or elasticsearch@6?'''
@pytest.fixture
def brew_no_available_formula_three():
return '''Warning: No available formula with the name "gitt". Did you mean git, gitg or gist?'''
def brew_no_available_formula():
return '''Error: No available formula for elsticsearch '''
@pytest.fixture
def brew_install_no_argument():
return '''Install a formula or cask. Additional options specific to a formula may be'''
return '''This command requires a formula argument'''
@pytest.fixture
@ -28,38 +19,28 @@ def brew_already_installed():
return '''Warning: git-2.3.5 already installed'''
def test_suggestions():
assert _get_suggestions("one") == ['one']
assert _get_suggestions("one or two") == ['one', 'two']
assert _get_suggestions("one, two or three") == ['one', 'two', 'three']
def _is_not_okay_to_test():
return 'elasticsearch' not in _get_formulas()
def test_match(brew_no_available_formula_one, brew_no_available_formula_two,
brew_no_available_formula_three, brew_already_installed,
@pytest.mark.skipif(_is_not_okay_to_test(),
reason='No need to run if there\'s no formula')
def test_match(brew_no_available_formula, brew_already_installed,
brew_install_no_argument):
assert match(Command('brew install giss',
brew_no_available_formula_one))
assert match(Command('brew install elasticserar',
brew_no_available_formula_two))
assert match(Command('brew install gitt',
brew_no_available_formula_three))
assert match(Command('brew install elsticsearch',
brew_no_available_formula))
assert not match(Command('brew install git',
brew_already_installed))
assert not match(Command('brew install', brew_install_no_argument))
def test_get_new_command(brew_no_available_formula_one, brew_no_available_formula_two,
brew_no_available_formula_three):
assert get_new_command(Command('brew install giss',
brew_no_available_formula_one))\
== ['brew install gist']
assert get_new_command(Command('brew install elasticsear',
brew_no_available_formula_two))\
== ['brew install elasticsearch', 'brew install elasticsearch@6']
assert get_new_command(Command('brew install gitt',
brew_no_available_formula_three))\
== ['brew install git', 'brew install gitg', 'brew install gist']
@pytest.mark.skipif(_is_not_okay_to_test(),
reason='No need to run if there\'s no formula')
def test_get_new_command(brew_no_available_formula):
assert get_new_command(Command('brew install elsticsearch',
brew_no_available_formula))\
== 'brew install elasticsearch'
assert get_new_command(Command('brew install aa',
brew_no_available_formula_one))\
brew_no_available_formula))\
!= 'brew install aha'

View File

@ -4,7 +4,7 @@ from thefuck.rules.brew_update_formula import get_new_command, match
output = ("Error: This command updates brew itself, and does not take formula"
" names.\nUse `brew upgrade thefuck`.")
" names.\nUse 'brew upgrade thefuck'.")
def test_match():

View File

@ -39,28 +39,18 @@ def composer_not_command_one_of_this():
)
@pytest.fixture
def composer_require_instead_of_install():
return 'Invalid argument package. Use "composer require package" instead to add packages to your composer.json.'
def test_match(composer_not_command, composer_not_command_one_of_this, composer_require_instead_of_install):
def test_match(composer_not_command, composer_not_command_one_of_this):
assert match(Command('composer udpate',
composer_not_command))
assert match(Command('composer pdate',
composer_not_command_one_of_this))
assert match(Command('composer install package',
composer_require_instead_of_install))
assert not match(Command('ls update', composer_not_command))
def test_get_new_command(composer_not_command, composer_not_command_one_of_this, composer_require_instead_of_install):
def test_get_new_command(composer_not_command, composer_not_command_one_of_this):
assert (get_new_command(Command('composer udpate',
composer_not_command))
== 'composer update')
assert (get_new_command(Command('composer pdate',
composer_not_command_one_of_this))
== 'composer selfupdate')
assert (get_new_command(Command('composer install package',
composer_require_instead_of_install))
== 'composer require package')

View File

@ -157,7 +157,7 @@ ReferenceError: conole is not defined
./tests/rules/test_whois.py:22:80: E501 line too long (83 > 79 characters)
"""),
FixFileTest('pytest', '/home/thefuck/tests/rules/test_fix_file.py', 218, None, """
FixFileTest('py.test', '/home/thefuck/tests/rules/test_fix_file.py', 218, None, """
monkeypatch = <_pytest.monkeypatch.monkeypatch object at 0x7fdb76a25b38>
test = ('fish a.sh', '/tmp/fix-error/a.sh', 2, None, '', "\\nfish: Unknown command 'foo'\\n/tmp/fix-error/a.sh (line 2): foo\\n ^\\n")

View File

@ -1,70 +0,0 @@
import pytest
from thefuck.rules.git_branch_0flag import get_new_command, match
from thefuck.types import Command
@pytest.fixture
def output_branch_exists():
return "fatal: A branch named 'bar' already exists."
@pytest.mark.parametrize(
"script",
[
"git branch 0a",
"git branch 0d",
"git branch 0f",
"git branch 0r",
"git branch 0v",
"git branch 0d foo",
"git branch 0D foo",
],
)
def test_match(script, output_branch_exists):
assert match(Command(script, output_branch_exists))
@pytest.mark.parametrize(
"script",
[
"git branch -a",
"git branch -r",
"git branch -v",
"git branch -d foo",
"git branch -D foo",
],
)
def test_not_match(script, output_branch_exists):
assert not match(Command(script, ""))
@pytest.mark.parametrize(
"script, new_command",
[
("git branch 0a", "git branch -D 0a && git branch -a"),
("git branch 0v", "git branch -D 0v && git branch -v"),
("git branch 0d foo", "git branch -D 0d && git branch -d foo"),
("git branch 0D foo", "git branch -D 0D && git branch -D foo"),
("git branch 0l 'maint-*'", "git branch -D 0l && git branch -l 'maint-*'"),
("git branch 0u upstream", "git branch -D 0u && git branch -u upstream"),
],
)
def test_get_new_command_branch_exists(script, output_branch_exists, new_command):
assert get_new_command(Command(script, output_branch_exists)) == new_command
@pytest.fixture
def output_not_valid_object():
return "fatal: Not a valid object name: 'bar'."
@pytest.mark.parametrize(
"script, new_command",
[
("git branch 0l 'maint-*'", "git branch -l 'maint-*'"),
("git branch 0u upstream", "git branch -u upstream"),
],
)
def test_get_new_command_not_valid_object(script, output_not_valid_object, new_command):
assert get_new_command(Command(script, output_not_valid_object)) == new_command

View File

@ -1,50 +0,0 @@
import pytest
from thefuck.rules.git_clone_missing import match, get_new_command
from thefuck.types import Command
valid_urls = [
'https://github.com/nvbn/thefuck.git',
'https://github.com/nvbn/thefuck',
'http://github.com/nvbn/thefuck.git',
'git@github.com:nvbn/thefuck.git',
'git@github.com:nvbn/thefuck',
'ssh://git@github.com:nvbn/thefuck.git',
]
invalid_urls = [
'', # No command
'notacommand', # Command not found
'ssh git@github.com:nvbn/thefrick.git', # ssh command, not a git clone
'git clone foo', # Valid clone
'git clone https://github.com/nvbn/thefuck.git', # Full command
'github.com/nvbn/thefuck.git', # Missing protocol
'github.com:nvbn/thefuck.git', # SSH missing username
'git clone git clone ssh://git@github.com:nvbn/thefrick.git', # 2x clone
'https:/github.com/nvbn/thefuck.git' # Bad protocol
]
outputs = [
'No such file or directory',
'not found',
'is not recognised as',
]
@pytest.mark.parametrize('cmd', valid_urls)
@pytest.mark.parametrize('output', outputs)
def test_match(cmd, output):
c = Command(cmd, output)
assert match(c)
@pytest.mark.parametrize('cmd', invalid_urls)
@pytest.mark.parametrize('output', outputs + ["some other output"])
def test_not_match(cmd, output):
c = Command(cmd, output)
assert not match(c)
@pytest.mark.parametrize('script', valid_urls)
@pytest.mark.parametrize('output', outputs)
def test_get_new_command(script, output):
command = Command(script, output)
new_command = 'git clone ' + script
assert get_new_command(command) == new_command

View File

@ -1,38 +0,0 @@
import pytest
from thefuck.rules.git_commit_add import match, get_new_command
from thefuck.types import Command
@pytest.mark.parametrize(
"script, output",
[
('git commit -m "test"', "no changes added to commit"),
("git commit", "no changes added to commit"),
],
)
def test_match(output, script):
assert match(Command(script, output))
@pytest.mark.parametrize(
"script, output",
[
('git commit -m "test"', " 1 file changed, 15 insertions(+), 14 deletions(-)"),
("git branch foo", ""),
("git checkout feature/test_commit", ""),
("git push", ""),
],
)
def test_not_match(output, script):
assert not match(Command(script, output))
@pytest.mark.parametrize(
"script, new_command",
[
("git commit", ["git commit -a", "git commit -p"]),
('git commit -m "foo"', ['git commit -a -m "foo"', 'git commit -p -m "foo"']),
],
)
def test_get_new_command(script, new_command):
assert get_new_command(Command(script, "")) == new_command

View File

@ -1,47 +0,0 @@
import pytest
from thefuck.rules.git_main_master import match, get_new_command
from thefuck.types import Command
@pytest.fixture
def output(branch_name):
if not branch_name:
return ""
output_str = u"error: pathspec '{}' did not match any file(s) known to git"
return output_str.format(branch_name)
@pytest.mark.parametrize(
"script, branch_name",
[
("git checkout main", "main"),
("git checkout master", "master"),
("git show main", "main"),
],
)
def test_match(script, branch_name, output):
assert match(Command(script, output))
@pytest.mark.parametrize(
"script, branch_name",
[
("git checkout master", ""),
("git checkout main", ""),
("git checkout wibble", "wibble"),
],
)
def test_not_match(script, branch_name, output):
assert not match(Command(script, output))
@pytest.mark.parametrize(
"script, branch_name, new_command",
[
("git checkout main", "main", "git checkout master"),
("git checkout master", "master", "git checkout main"),
("git checkout wibble", "wibble", "git checkout wibble"),
],
)
def test_get_new_command(script, branch_name, new_command, output):
assert get_new_command(Command(script, output)) == new_command

View File

@ -1,20 +1,27 @@
import pytest
from thefuck.types import Command
from thefuck.rules.git_push_without_commits import get_new_command, match
from thefuck.rules.git_push_without_commits import (
fix,
get_new_command,
match,
)
command = 'git push -u origin master'
expected_error = '''
error: src refspec master does not match any.
error: failed to push some refs to 'git@github.com:User/repo.git'
'''
def test_match():
script = "git push -u origin master"
output = "error: src refspec master does not match any\nerror: failed to..."
assert match(Command(script, output))
@pytest.mark.parametrize('command', [Command(command, expected_error)])
def test_match(command):
assert match(command)
def test_not_match():
script = "git push -u origin master"
assert not match(Command(script, "Everything up-to-date"))
def test_get_new_command():
script = "git push -u origin master"
output = "error: src refspec master does not match any\nerror: failed to..."
new_command = 'git commit -m "Initial commit" && git push -u origin master'
assert get_new_command(Command(script, output)) == new_command
@pytest.mark.parametrize('command, result', [(
Command(command, expected_error),
fix.format(command=command),
)])
def test_get_new_command(command, result):
assert get_new_command(command) == result

View File

@ -8,11 +8,11 @@ from thefuck.types import Command
def all_executables(mocker):
return mocker.patch(
'thefuck.rules.missing_space_before_subcommand.get_all_executables',
return_value=['git', 'ls', 'npm', 'w', 'watch'])
return_value=['git', 'ls', 'npm'])
@pytest.mark.parametrize('script', [
'gitbranch', 'ls-la', 'npminstall', 'watchls'])
'gitbranch', 'ls-la', 'npminstall'])
def test_match(script):
assert match(Command(script, ''))
@ -25,7 +25,6 @@ def test_not_match(script):
@pytest.mark.parametrize('script, result', [
('gitbranch', 'git branch'),
('ls-la', 'ls -la'),
('npminstall webpack', 'npm install webpack'),
('watchls', 'watch ls')])
('npminstall webpack', 'npm install webpack')])
def test_get_new_command(script, result):
assert get_new_command(Command(script, '')) == result

View File

@ -21,8 +21,7 @@ def history_without_current(mocker):
('vom file.py', 'vom: not found'),
('fucck', 'fucck: not found'),
('puthon', "'puthon' is not recognized as an internal or external command"),
('got commit', 'got: command not found'),
('gti commit -m "new commit"', 'gti: command not found')])
('got commit', 'got: command not found')])
def test_match(mocker, script, output):
mocker.patch('thefuck.rules.no_command.which', return_value=None)
@ -44,7 +43,6 @@ def test_not_match(mocker, script, output, which):
@pytest.mark.parametrize('script, result', [
('vom file.py', ['vim file.py']),
('fucck', ['fsck']),
('got commit', ['git commit', 'go commit']),
('gti commit -m "new commit"', ['git commit -m "new commit"'])])
('got commit', ['git commit', 'go commit'])])
def test_get_new_command(script, result):
assert get_new_command(Command(script, '')) == result

View File

@ -12,7 +12,6 @@ extra/llvm35 3.5.2-13/usr/bin/llc'''
reason='Skip if pacman is not available')
@pytest.mark.parametrize('command', [
Command('yay -S llc', 'error: target not found: llc'),
Command('pikaur -S llc', 'error: target not found: llc'),
Command('yaourt -S llc', 'error: target not found: llc'),
Command('pacman llc', 'error: target not found: llc'),
Command('sudo pacman llc', 'error: target not found: llc')])
@ -22,7 +21,6 @@ def test_match(command):
@pytest.mark.parametrize('command', [
Command('yay -S llc', 'error: target not found: llc'),
Command('pikaur -S llc', 'error: target not found: llc'),
Command('yaourt -S llc', 'error: target not found: llc'),
Command('pacman llc', 'error: target not found: llc'),
Command('sudo pacman llc', 'error: target not found: llc')])
@ -36,7 +34,6 @@ def test_match_mocked(subp_mock, command):
reason='Skip if pacman is not available')
@pytest.mark.parametrize('command, fixed', [
(Command('yay -S llc', 'error: target not found: llc'), ['yay -S extra/llvm', 'yay -S extra/llvm35']),
(Command('pikaur -S llc', 'error: target not found: llc'), ['pikaur -S extra/llvm', 'pikaur -S extra/llvm35']),
(Command('yaourt -S llc', 'error: target not found: llc'), ['yaourt -S extra/llvm', 'yaourt -S extra/llvm35']),
(Command('pacman -S llc', 'error: target not found: llc'), ['pacman -S extra/llvm', 'pacman -S extra/llvm35']),
(Command('sudo pacman -S llc', 'error: target not found: llc'), ['sudo pacman -S extra/llvm', 'sudo pacman -S extra/llvm35'])])
@ -46,7 +43,6 @@ def test_get_new_command(command, fixed):
@pytest.mark.parametrize('command, fixed', [
(Command('yay -S llc', 'error: target not found: llc'), ['yay -S extra/llvm', 'yay -S extra/llvm35']),
(Command('pikaur -S llc', 'error: target not found: llc'), ['pikaur -S extra/llvm', 'pikaur -S extra/llvm35']),
(Command('yaourt -S llc', 'error: target not found: llc'), ['yaourt -S extra/llvm', 'yaourt -S extra/llvm35']),
(Command('pacman -S llc', 'error: target not found: llc'), ['pacman -S extra/llvm', 'pacman -S extra/llvm35']),
(Command('sudo pacman -S llc', 'error: target not found: llc'), ['sudo pacman -S extra/llvm', 'sudo pacman -S extra/llvm35'])])

View File

@ -1,46 +0,0 @@
import pytest
from thefuck.rules.rails_migrations_pending import match, get_new_command
from thefuck.types import Command
output_env_development = '''
Migrations are pending. To resolve this issue, run:
rails db:migrate RAILS_ENV=development
'''
output_env_test = '''
Migrations are pending. To resolve this issue, run:
bin/rails db:migrate RAILS_ENV=test
'''
@pytest.mark.parametrize(
"command",
[
Command("", output_env_development),
Command("", output_env_test),
],
)
def test_match(command):
assert match(command)
@pytest.mark.parametrize(
"command",
[
Command("Environment data not found in the schema. To resolve this issue, run: \n\n", ""),
],
)
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize(
"command, new_command",
[
(Command("bin/rspec", output_env_development), "rails db:migrate RAILS_ENV=development && bin/rspec"),
(Command("bin/rspec", output_env_test), "bin/rails db:migrate RAILS_ENV=test && bin/rspec"),
],
)
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@ -8,15 +8,7 @@ def output():
return "$: command not found"
@pytest.mark.parametrize(
"script",
[
"$ cd newdir",
" $ cd newdir",
"$ $ cd newdir",
" $ $ cd newdir",
],
)
@pytest.mark.parametrize("script", ["$ cd newdir", " $ cd newdir"])
def test_match(script, output):
assert match(Command(script, output))
@ -39,9 +31,7 @@ def test_not_match(command):
"script, new_command",
[
("$ cd newdir", "cd newdir"),
("$ $ cd newdir", "cd newdir"),
("$ python3 -m virtualenv env", "python3 -m virtualenv env"),
(" $ $ $ python3 -m virtualenv env", "python3 -m virtualenv env"),
],
)
def test_get_new_command(script, new_command, output):

View File

@ -1,6 +1,6 @@
import os
import pytest
from thefuck.rules.ssh_known_hosts import match, get_new_command, \
from thefuck.rules.ssh_known_hosts import match, get_new_command,\
side_effect
from thefuck.types import Command

View File

@ -10,9 +10,6 @@ from thefuck.types import Command
'requested operation requires superuser privilege',
'need to be root',
'need root',
'shutdown: NOT super-user',
'Error: This command has to be run with superuser privileges (under the root user on most systems).',
'updatedb: can not open a temporary file for `/var/lib/mlocate/mlocate.db',
'must be root',
'You don\'t have access to the history DB.',
"error: [Errno 13] Permission denied: '/usr/local/lib/python2.7/dist-packages/ipaddr.py'"])

View File

@ -1,27 +0,0 @@
import pytest
from thefuck.rules.terraform_no_command import match, get_new_command
from thefuck.types import Command
@pytest.mark.parametrize('script, output', [
('terraform appyl', 'Terraform has no command named "appyl". Did you mean "apply"?'),
('terraform destory', 'Terraform has no command named "destory". Did you mean "destroy"?')])
def test_match(script, output):
assert match(Command(script, output))
@pytest.mark.parametrize('script, output', [
('terraform --version', 'Terraform v0.12.2'),
('terraform plan', 'No changes. Infrastructure is up-to-date.'),
('terraform apply', 'Apply complete! Resources: 0 added, 0 changed, 0 destroyed.'),
])
def test_not_match(script, output):
assert not match(Command(script, output))
@pytest.mark.parametrize('script, output, new_command', [
('terraform appyl', 'Terraform has no command named "appyl". Did you mean "apply"?', 'terraform apply',),
('terraform destory --some-other-option', 'Terraform has no command named "destory". Did you mean "destroy"?', 'terraform destroy --some-other-option',),
])
def test_get_new_command(script, output, new_command):
assert get_new_command(Command(script, output)) == new_command

View File

@ -1,30 +0,0 @@
import pytest
from thefuck.rules.wrong_hyphen_before_subcommand import match, get_new_command
from thefuck.types import Command
@pytest.fixture(autouse=True)
def get_all_executables(mocker):
mocker.patch(
"thefuck.rules.wrong_hyphen_before_subcommand.get_all_executables",
return_value=["git", "apt", "apt-get", "ls", "pwd"],
)
@pytest.mark.parametrize("script", ["git-log", "apt-install python"])
def test_match(script):
assert match(Command(script, ""))
@pytest.mark.parametrize("script", ["ls -la", "git2-make", "apt-get install python"])
def test_not_match(script):
assert not match(Command(script, ""))
@pytest.mark.parametrize(
"script, new_command",
[("git-log", "git log"), ("apt-install python", "apt install python")],
)
def test_get_new_command(script, new_command):
assert get_new_command(Command(script, "")) == new_command

View File

@ -87,11 +87,8 @@ class TestFish(object):
def test_app_alias_alter_history(self, settings, shell):
settings.alter_history = True
assert (
'builtin history delete --exact --case-sensitive -- $fucked_up_command\n'
in shell.app_alias('FUCK')
)
assert 'builtin history merge\n' in shell.app_alias('FUCK')
assert 'builtin history delete' in shell.app_alias('FUCK')
assert 'builtin history merge' in shell.app_alias('FUCK')
settings.alter_history = False
assert 'builtin history delete' not in shell.app_alias('FUCK')
assert 'builtin history merge' not in shell.app_alias('FUCK')

View File

@ -6,11 +6,7 @@ from thefuck.types import Command
@pytest.mark.parametrize('called, command, output', [
('git co', 'git checkout', "19:22:36.299340 git.c:282 trace: alias expansion: co => 'checkout'"),
('git com file', 'git commit --verbose file',
"19:23:25.470911 git.c:282 trace: alias expansion: com => 'commit' '--verbose'"),
('git com -m "Initial commit"', 'git commit -m "Initial commit"',
"19:22:36.299340 git.c:282 trace: alias expansion: com => 'commit'"),
('git br -d some_branch', 'git branch -d some_branch',
"19:22:36.299340 git.c:282 trace: alias expansion: br => 'branch'")])
"19:23:25.470911 git.c:282 trace: alias expansion: com => 'commit' '--verbose'")])
def test_git_support(called, command, output):
@git_support
def fn(command):
@ -27,10 +23,9 @@ def test_git_support(called, command, output):
('ls', False),
('cat git', False),
('cat hub', False)])
@pytest.mark.parametrize('output', ['', None])
def test_git_support_match(command, is_git, output):
def test_git_support_match(command, is_git):
@git_support
def fn(command):
return True
assert fn(Command(command, output)) == is_git
assert fn(Command(command, '')) == is_git

View File

@ -8,5 +8,5 @@ def test_readme(source_root):
for rule in bundled:
if rule.stem != '__init__':
assert rule.stem in readme, \
assert rule.stem in readme,\
'Missing rule "{}" in README.md'.format(rule.stem)

View File

@ -143,9 +143,7 @@ class TestCommand(object):
([''], None),
(['', ''], None),
(['ls', '-la'], 'ls -la'),
(['ls'], 'ls'),
(['echo \\ '], 'echo \\ '),
(['echo \\\n'], 'echo \\\n')])
(['ls'], 'ls')])
def test_from_script(self, script, result):
if result:
assert Command.from_raw_script(script).script == result

View File

@ -146,8 +146,6 @@ def test_get_all_matched_commands(stderr, result):
@pytest.mark.usefixtures('no_memoize')
@pytest.mark.parametrize('script, names, result', [
('/usr/bin/git diff', ['git', 'hub'], True),
('/bin/hdfs dfs -rm foo', ['hdfs'], True),
('git diff', ['git', 'hub'], True),
('hub diff', ['git', 'hub'], True),
('hg diff', ['git', 'hub'], False)])
@ -157,8 +155,6 @@ def test_is_app(script, names, result):
@pytest.mark.usefixtures('no_memoize')
@pytest.mark.parametrize('script, names, result', [
('/usr/bin/git diff', ['git', 'hub'], True),
('/bin/hdfs dfs -rm foo', ['hdfs'], True),
('git diff', ['git', 'hub'], True),
('hub diff', ['git', 'hub'], True),
('hg diff', ['git', 'hub'], False)])
@ -235,7 +231,7 @@ class TestCache(object):
class TestGetValidHistoryWithoutCurrent(object):
@pytest.fixture(autouse=True)
@pytest.yield_fixture(autouse=True)
def fail_on_warning(self):
warnings.simplefilter('error')
yield
@ -245,7 +241,7 @@ class TestGetValidHistoryWithoutCurrent(object):
def history(self, mocker):
mock = mocker.patch('thefuck.shells.shell.get_history')
# Passing as an argument causes `UnicodeDecodeError`
# with newer pytest and python 2.7
# with newer py.test and python 2.7
mock.return_value = ['le cat', 'fuck', 'ls cat',
'diff x', 'nocommand x', u'café ô']
return mock

View File

@ -1,3 +1,4 @@
from imp import load_source
import os
import sys
from warnings import warn
@ -5,17 +6,6 @@ from six import text_type
from . import const
from .system import Path
try:
import importlib.util
def load_source(name, pathname, _file=None):
module_spec = importlib.util.spec_from_file_location(name, pathname)
module = importlib.util.module_from_spec(module_spec)
module_spec.loader.exec_module(module)
return module
except ImportError:
from imp import load_source
class Settings(dict):
def __getattr__(self, item):

View File

@ -1,5 +1,4 @@
import six
from ..conf import settings
from ..logs import warn
from ..shells import shell
from ..utils import which
@ -24,5 +23,4 @@ def _get_alias(known_args):
def print_alias(known_args):
settings.init(known_args)
print(_get_alias(known_args))

View File

@ -12,7 +12,7 @@ from ..utils import get_alias, get_all_executables
def _get_raw_command(known_args):
if known_args.force_command:
return [known_args.force_command]
return known_args.force_command
elif not os.environ.get('TF_HISTORY'):
return known_args.command
else:

View File

@ -7,7 +7,7 @@ import os # noqa: E402
import sys # noqa: E402
from .. import logs # noqa: E402
from ..argument_parser import Parser # noqa: E402
from ..utils import get_installation_version # noqa: E402
from ..utils import get_installation_info # noqa: E402
from ..shells import shell # noqa: E402
from .alias import print_alias # noqa: E402
from .fix_command import fix_command # noqa: E402
@ -20,7 +20,7 @@ def main():
if known_args.help:
parser.print_help()
elif known_args.version:
logs.version(get_installation_version(),
logs.version(get_installation_info().version,
sys.version.split()[0], shell.info())
# It's important to check if an alias is being requested before checking if
# `TF_HISTORY` is in `os.environ`, otherwise it might mess with subshells.

View File

@ -40,9 +40,6 @@ def _group_by_calls(log):
def _get_script_group_lines(grouped, script):
if six.PY2:
script = script.encode('utf-8')
parts = shlex.split(script)
for script_line, lines in reversed(grouped):

View File

@ -1,6 +1,5 @@
import os
import shlex
import six
from subprocess import Popen, PIPE, STDOUT
from psutil import AccessDenied, Process, TimeoutExpired
from .. import logs
@ -54,9 +53,6 @@ def get_output(script, expanded):
env = dict(os.environ)
env.update(settings.env)
if six.PY2:
expanded = expanded.encode('utf-8')
split_expand = shlex.split(expanded)
is_slow = split_expand[0] in settings.slow_commands if split_expand else False
with logs.debug_time(u'Call: {}; with env: {}; is slow: {}'.format(
@ -64,7 +60,7 @@ def get_output(script, expanded):
result = Popen(expanded, shell=True, stdin=PIPE,
stdout=PIPE, stderr=STDOUT, env=env)
if _wait_output(result, is_slow):
output = result.stdout.read().decode('utf-8', errors='replace')
output = result.stdout.read().decode('utf-8')
logs.debug(u'Received output: {}'.format(output))
return output
else:

View File

@ -1,24 +1,42 @@
import os
import re
from thefuck.utils import for_app
from thefuck.specific.brew import brew_available
from thefuck.utils import get_closest, replace_argument
from thefuck.specific.brew import get_brew_path_prefix, brew_available
enabled_by_default = brew_available
def _get_suggestions(str):
suggestions = str.replace(" or ", ", ").split(", ")
return suggestions
def _get_formulas():
# Formulas are based on each local system's status
try:
brew_path_prefix = get_brew_path_prefix()
brew_formula_path = brew_path_prefix + '/Library/Formula'
for file_name in os.listdir(brew_formula_path):
if file_name.endswith('.rb'):
yield file_name[:-3]
except Exception:
pass
def _get_similar_formula(formula_name):
return get_closest(formula_name, _get_formulas(), cutoff=0.85)
@for_app('brew', at_least=2)
def match(command):
is_proper_command = ('install' in command.script and
'No available formula' in command.output and
'Did you mean' in command.output)
return is_proper_command
is_proper_command = ('brew install' in command.script and
'No available formula' in command.output)
if is_proper_command:
formula = re.findall(r'Error: No available formula for ([a-z]+)',
command.output)[0]
return bool(_get_similar_formula(formula))
return False
def get_new_command(command):
matcher = re.search('Warning: No available formula with the name "(?:[^"]+)". Did you mean (.+)\\?', command.output)
suggestions = _get_suggestions(matcher.group(1))
return ["brew install " + formula for formula in suggestions]
not_exist_formula = re.findall(r'Error: No available formula for ([a-z]+)',
command.output)[0]
exist_formula = _get_similar_formula(not_exist_formula)
return replace_argument(command.script, not_exist_formula, exist_formula)

View File

@ -5,7 +5,7 @@ from thefuck.utils import for_app
def match(command):
return ('update' in command.script
and "Error: This command updates brew itself" in command.output
and "Use `brew upgrade" in command.output)
and "Use 'brew upgrade" in command.output)
def get_new_command(command):

View File

@ -5,18 +5,12 @@ from thefuck.utils import replace_argument, for_app
@for_app('composer')
def match(command):
return (('did you mean this?' in command.output.lower()
or 'did you mean one of these?' in command.output.lower())) or (
"install" in command.script_parts and "composer require" in command.output.lower()
)
or 'did you mean one of these?' in command.output.lower()))
def get_new_command(command):
if "install" in command.script_parts and "composer require" in command.output.lower():
broken_cmd, new_cmd = "install", "require"
else:
broken_cmd = re.findall(r"Command \"([^']*)\" is not defined", command.output)[0]
new_cmd = re.findall(r'Did you mean this\?[^\n]*\n\s*([^\n]*)', command.output)
if not new_cmd:
new_cmd = re.findall(r'Did you mean one of these\?[^\n]*\n\s*([^\n]*)', command.output)
new_cmd = new_cmd[0].strip()
return replace_argument(command.script, broken_cmd, new_cmd)
broken_cmd = re.findall(r"Command \"([^']*)\" is not defined", command.output)[0]
new_cmd = re.findall(r'Did you mean this\?[^\n]*\n\s*([^\n]*)', command.output)
if not new_cmd:
new_cmd = re.findall(r'Did you mean one of these\?[^\n]*\n\s*([^\n]*)', command.output)
return replace_argument(command.script, broken_cmd, new_cmd[0].strip())

View File

@ -1,5 +1,4 @@
from thefuck.utils import for_app
from thefuck.shells import shell
@for_app('docker')
@ -10,4 +9,4 @@ def match(command):
def get_new_command(command):
return shell.and_('docker login', command.script)
return 'docker login && {}'.format(command.script)

View File

@ -1,24 +0,0 @@
from thefuck.shells import shell
from thefuck.specific.git import git_support
from thefuck.utils import memoize
@memoize
def first_0flag(script_parts):
return next((p for p in script_parts if len(p) == 2 and p.startswith("0")), None)
@git_support
def match(command):
return command.script_parts[1] == "branch" and first_0flag(command.script_parts)
@git_support
def get_new_command(command):
branch_name = first_0flag(command.script_parts)
fixed_flag = branch_name.replace("0", "-")
fixed_script = command.script.replace(branch_name, fixed_flag)
if "A branch named '" in command.output and "' already exists." in command.output:
delete_branch = u"git branch -D {}".format(branch_name)
return shell.and_(delete_branch, fixed_script)
return fixed_script

View File

@ -1,42 +0,0 @@
'''
Rule: git_clone_missing
Correct missing `git clone` command when pasting a git URL
```sh
>>> https://github.com/nvbn/thefuck.git
git clone https://github.com/nvbn/thefuck.git
```
Author: Miguel Guthridge
'''
from six.moves.urllib import parse
from thefuck.utils import which
def match(command):
# We want it to be a URL by itself
if len(command.script_parts) != 1:
return False
# Ensure we got the error we expected
if which(command.script_parts[0]) or not (
'No such file or directory' in command.output
or 'not found' in command.output
or 'is not recognised as' in command.output
):
return False
url = parse.urlparse(command.script, scheme='ssh')
# HTTP URLs need a network address
if not url.netloc and url.scheme != 'ssh':
return False
# SSH needs a username and a splitter between the path
if url.scheme == 'ssh' and not (
'@' in command.script
and ':' in command.script
):
return False
return url.scheme in ['http', 'https', 'ssh']
def get_new_command(command):
return 'git clone ' + command.script

View File

@ -1,17 +0,0 @@
from thefuck.utils import eager, replace_argument
from thefuck.specific.git import git_support
@git_support
def match(command):
return (
"commit" in command.script_parts
and "no changes added to commit" in command.output
)
@eager
@git_support
def get_new_command(command):
for opt in ("-a", "-p"):
yield replace_argument(command.script, "commit", "commit {}".format(opt))

View File

@ -23,5 +23,5 @@ def get_new_command(command):
)
priority = 1100
priority = 900
requires_output = False

View File

@ -1,16 +0,0 @@
from thefuck.specific.git import git_support
@git_support
def match(command):
return "'master'" in command.output or "'main'" in command.output
@git_support
def get_new_command(command):
if "'master'" in command.output:
return command.script.replace("master", "main")
return command.script.replace("main", "master")
priority = 1200

View File

@ -1,12 +1,14 @@
import re
from thefuck.shells import shell
from thefuck.specific.git import git_support
fix = u'git commit -m "Initial commit." && {command}'
refspec_does_not_match = re.compile(r'src refspec \w+ does not match any\.')
@git_support
def match(command):
return bool(re.search(r"src refspec \w+ does not match any", command.output))
return bool(refspec_does_not_match.search(command.output))
def get_new_command(command):
return shell.and_('git commit -m "Initial commit"', command.script)
return fix.format(command=command.script)

View File

@ -14,7 +14,7 @@ def get_golang_commands():
if which('go'):
get_golang_commands = cache(which('go'))(get_golang_commands)
get_docker_commands = cache(which('go'))(get_golang_commands)
@for_app('go')

View File

@ -5,7 +5,7 @@ from thefuck.utils import for_app, eager, replace_command
regex = re.compile(r"Task '(.*)' (is ambiguous|not found)")
@for_app('gradle', 'gradlew')
@for_app('gradle', './gradlew')
def match(command):
return regex.findall(command.output)

View File

@ -4,7 +4,7 @@ from thefuck.utils import get_all_executables, memoize
@memoize
def _get_executable(script_part):
for executable in get_all_executables():
if len(executable) > 1 and script_part.startswith(executable):
if script_part.startswith(executable):
return executable

View File

@ -35,7 +35,8 @@ def get_new_command(command):
get_all_executables())
if cmd not in new_cmds]
return [command.script.replace(old_command, cmd, 1) for cmd in new_cmds]
return [' '.join([new_command] + command.script_parts[1:])
for new_command in new_cmds]
priority = 3000

View File

@ -12,7 +12,7 @@ from thefuck.specific.archlinux import get_pkgfile, archlinux_env
def match(command):
return (command.script_parts
and (command.script_parts[0] in ('pacman', 'yay', 'pikaur', 'yaourt')
and (command.script_parts[0] in ('pacman', 'yay', 'yaourt')
or command.script_parts[0:2] == ['sudo', 'pacman'])
and 'error: target not found:' in command.output)

View File

@ -1,14 +0,0 @@
import re
from thefuck.shells import shell
SUGGESTION_REGEX = r"To resolve this issue, run:\s+(.*?)\n"
def match(command):
return "Migrations are pending. To resolve this issue, run:" in command.output
def get_new_command(command):
migration_script = re.search(SUGGESTION_REGEX, command.output).group(1)
return shell.and_(migration_script, command.script)

View File

@ -1,5 +1,4 @@
"""Fixes error for commands containing one or more occurrences of the shell
prompt symbol '$'.
"""Fixes error for commands containing the shell prompt symbol '$'.
This usually happens when commands are copied from documentations
including them in their code blocks.
@ -20,4 +19,4 @@ def match(command):
def get_new_command(command):
return command.script.lstrip("$ ")
return command.script.replace("$", "", 1).strip()

View File

@ -4,8 +4,6 @@ patterns = ['permission denied',
'you cannot perform this operation unless you are root',
'non-root users cannot',
'operation not permitted',
'not super-user',
'superuser privilege',
'root privilege',
'this command has to be run under the root user.',
'this operation requires root.',
@ -24,8 +22,7 @@ patterns = ['permission denied',
'you don\'t have write permissions',
'use `sudo`',
'sudorequirederror',
'error: insufficient privileges',
'updatedb: can not open a temporary file']
'error: insufficient privileges']
def match(command):

View File

@ -1,16 +0,0 @@
import re
from thefuck.utils import for_app
MISTAKE = r'(?<=Terraform has no command named ")([^"]+)(?="\.)'
FIX = r'(?<=Did you mean ")([^"]+)(?="\?)'
@for_app('terraform')
def match(command):
return re.search(MISTAKE, command.output) and re.search(FIX, command.output)
def get_new_command(command):
mistake = re.search(MISTAKE, command.output).group(0)
fix = re.search(FIX, command.output).group(0)
return command.script.replace(mistake, fix)

View File

@ -3,7 +3,7 @@ def match(command):
def get_new_command(command):
return 'pytest'
return 'py.test'
# make it come before the python_command rule

View File

@ -1,20 +0,0 @@
from thefuck.utils import get_all_executables
from thefuck.specific.sudo import sudo_support
@sudo_support
def match(command):
first_part = command.script_parts[0]
if "-" not in first_part or first_part in get_all_executables():
return False
cmd, _ = first_part.split("-", 1)
return cmd in get_all_executables()
@sudo_support
def get_new_command(command):
return command.script.replace("-", " ", 1)
priority = 4500
requires_output = False

View File

@ -52,7 +52,7 @@ class Fish(Generic):
if settings.alter_history:
alter_history = (' builtin history delete --exact'
' --case-sensitive -- $fucked_up_command\n'
' builtin history merge\n')
' builtin history merge ^ /dev/null\n')
else:
alter_history = ''
# It is VERY important to have the variables declared WITHIN the alias

View File

@ -34,8 +34,6 @@ def get_pkgfile(command):
def archlinux_env():
if utils.which('yay'):
pacman = 'yay'
elif utils.which('pikaur'):
pacman = 'pikaur'
elif utils.which('yaourt'):
pacman = 'yaourt'
elif utils.which('pacman'):

View File

@ -14,7 +14,7 @@ def git_support(fn, command):
return False
# perform git aliases expansion
if command.output and 'trace: alias expansion:' in command.output:
if 'trace: alias expansion:' in command.output:
search = re.search("trace: alias expansion: ([^ ]*) => ([^\n]*)",
command.output)
alias = search.group(1)
@ -25,7 +25,7 @@ def git_support(fn, command):
# eg. 'git commit'
expansion = ' '.join(shell.quote(part)
for part in shell.split_command(search.group(2)))
new_script = re.sub(r"\b{}\b".format(alias), expansion, command.script)
new_script = command.script.replace(alias, expansion)
command = command.update(script=new_script)

View File

@ -1,8 +1,9 @@
from imp import load_source
import os
import sys
from . import logs
from .shells import shell
from .conf import settings, load_source
from .conf import settings
from .const import DEFAULT_PRIORITY, ALL_ENABLED
from .exceptions import EmptyCommand
from .utils import get_alias, format_raw_script

View File

@ -180,13 +180,13 @@ def is_app(command, *app_names, **kwargs):
raise TypeError("got an unexpected keyword argument '{}'".format(kwargs.keys()))
if len(command.script_parts) > at_least:
return os.path.basename(command.script_parts[0]) in app_names
return command.script_parts[0] in app_names
return False
def for_app(*app_names, **kwargs):
"""Specifies that matching script is for one of app names."""
"""Specifies that matching script is for on of app names."""
def _for_app(fn, command):
if is_app(command, *app_names, **kwargs):
return fn(command)
@ -294,15 +294,10 @@ def cache(*depends_on):
cache.disabled = False
def get_installation_version():
try:
from importlib.metadata import version
def get_installation_info():
import pkg_resources
return version('thefuck')
except ImportError:
import pkg_resources
return pkg_resources.require('thefuck')[0].version
return pkg_resources.require('thefuck')[0]
def get_alias():
@ -344,4 +339,4 @@ def format_raw_script(raw_script):
else:
script = ' '.join(raw_script)
return script.lstrip()
return script.strip()

View File

@ -1,9 +1,9 @@
[tox]
envlist = py{27,35,36,37,38,39,310,311}
envlist = py27,py35,py36,py37,py38
[testenv]
deps = -rrequirements.txt
commands = pytest -v --capture=sys
commands = py.test -v --capture=sys
[flake8]
ignore = E501,W503,W504