Compare commits

..

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

154 changed files with 530 additions and 3071 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

@ -1,11 +0,0 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
[*.py]
max_line_length = 119

View File

@ -1,68 +0,0 @@
name: Tests
on: [push, pull_request]
env:
PYTHON_LATEST: "3.11"
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"]
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Cache dependencies
id: cache-deps
uses: actions/cache@v2
with:
path: |
${{ env.pythonLocation }}/bin/*
${{ env.pythonLocation }}/lib/*
${{ env.pythonLocation }}/scripts/*
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('setup.py', 'requirements.txt') }}
- name: Install The Fuck with all dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
run: |
pip install -Ur requirements.txt coveralls
python setup.py develop
- name: Lint
if: matrix.os == 'ubuntu-latest' && matrix.python-version == env.PYTHON_LATEST
run: flake8
- name: Run tests
if: matrix.os != 'ubuntu-latest' || matrix.python-version != env.PYTHON_LATEST
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
- 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

66
.travis.yml Normal file
View File

@ -0,0 +1,66 @@
language: python
sudo: false
matrix:
include:
- os: linux
dist: xenial
python: "nightly"
- os: linux
dist: xenial
python: "3.8-dev"
- os: linux
dist: xenial
python: "3.7-dev"
- os: linux
dist: xenial
python: "3.7"
- os: linux
dist: trusty
python: "3.6-dev"
- os: linux
dist: trusty
python: "3.6"
- os: linux
dist: trusty
python: "3.5"
- os: linux
dist: trusty
python: "3.4"
- os: linux
dist: trusty
python: "2.7"
- os: osx
language: generic
allow_failures:
- python: nightly
- python: 3.8-dev
- python: 3.7-dev
- python: 3.6-dev
services:
- docker
addons:
apt:
packages:
- python-commandnotfound
- python3-commandnotfound
before_install:
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then rm -rf /usr/local/include/c++; fi
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew update; fi
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew upgrade python; fi
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then pip3 install virtualenv; fi
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then virtualenv venv -p python3; fi
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then source venv/bin/activate; fi
- pip install -U pip
- pip install -U coveralls
install:
- pip install -Ur requirements.txt
- python setup.py develop
- rm -rf build
script:
- flake8
- export COVERAGE_PYTHON_VERSION=python-${TRAVIS_PYTHON_VERSION:0:1}
- export RUN_TESTS="coverage run --source=thefuck,tests -m py.test -v --capture=sys tests"
- if [[ $TRAVIS_PYTHON_VERSION == 3.7 && $TRAVIS_OS_NAME != "osx" ]]; then $RUN_TESTS --enable-functional; fi
- if [[ $TRAVIS_PYTHON_VERSION != 3.7 || $TRAVIS_OS_NAME == "osx" ]]; then $RUN_TESTS; fi
after_success:
- if [[ $TRAVIS_PYTHON_VERSION == 3.7 && $TRAVIS_OS_NAME != "osx" ]]; then coveralls; fi

View File

@ -26,15 +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:
```bash
@ -51,13 +42,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 +57,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-2018 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

134
README.md
View File

@ -1,4 +1,4 @@
# The Fuck [![Version][version-badge]][version-link] [![Build Status][workflow-badge]][workflow-link] [![Coverage][coverage-badge]][coverage-link] [![MIT License][license-badge]](LICENSE.md)
# 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* is a magnificent app, inspired by a [@liamosaur](https://twitter.com/liamosaur/)
[tweet](https://twitter.com/liamosaur/status/506975850596536320),
@ -91,30 +91,15 @@ Reading package lists... Done
...
```
## Contents
1. [Requirements](#requirements)
2. [Installations](#installation)
3. [Updating](#updating)
4. [How it works](#how-it-works)
5. [Creating your own rules](#creating-your-own-rules)
6. [Settings](#settings)
7. [Third party packages with rules](#third-party-packages-with-rules)
8. [Experimental instant mode](#experimental-instant-mode)
9. [Developing](#developing)
10. [License](#license-mit)
## Requirements
- python (3.5+)
- python (3.4+)
- pip
- python-dev
##### [Back to Contents](#contents)
## Installation
On macOS or Linux, you can install *The Fuck* via [Homebrew][homebrew]:
On OS X, you can install *The Fuck* via [Homebrew][homebrew]:
```bash
brew install thefuck
@ -124,12 +109,13 @@ 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:
```bash
pkg install thefuck
sudo portsnap fetch update
cd /usr/ports/misc/thefuck && sudo make install clean
```
On ChromeOS, install *The Fuck* using [chromebrew](https://github.com/skycocker/chromebrew) with the following command:
@ -137,11 +123,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
@ -165,7 +146,7 @@ eval $(thefuck --alias FUCK)
Changes are only available in a new shell session. To make changes immediately
available, run `source ~/.bashrc` (or your shell config file like `.zshrc`).
To run fixed commands without confirmation, use the `--yeah` option (or just `-y` for short, or `--hard` if you're especially frustrated):
To run fixed commands without confirmation, use the `--yeah` option (or just `-y` for short):
```bash
fuck --yeah
@ -177,8 +158,6 @@ To fix commands recursively until succeeding, use the `-r` option:
fuck -r
```
##### [Back to Contents](#contents)
## Updating
```bash
@ -187,12 +166,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
@ -207,23 +180,17 @@ following rules are enabled by default:
* `cargo_no_command` – fixes wrongs commands like `cargo buid`;
* `cat_dir` – replaces `cat` with `ls` when you try to `cat` a directory;
* `cd_correction` – spellchecks and correct failed cd commands;
* `cd_cs` – changes `cs` to `cd`;
* `cd_mkdir` – creates directories before cd'ing into them;
* `cd_parent` – changes `cd..` to `cd ..`;
* `chmod_x` – add execution bit;
* `choco_install` – append common suffixes for chocolatey packages;
* `composer_not_command` – fixes composer command name;
* `conda_mistype` – fixes conda commands;
* `cp_create_destination` – creates a new directory when you attempt to `cp` or `mv` to a non existent one
* `cp_omitting_directory` – adds `-a` when you `cp` directory;
* `cpp11` – adds missing `-std=c++11` to `g++` or `clang++`;
* `dirty_untar` – fixes `tar x` command that untarred in the current directory;
* `dirty_unzip` – fixes `unzip` command that unzipped in the current directory;
* `django_south_ghost` – adds `--delete-ghost-migrations` to failed because ghosts django south migration;
* `django_south_merge` – adds `--merge` to inconsistent django south migration;
* `docker_login` – executes a `docker login` and repeats the previous command;
* `docker_not_command` – fixes wrong docker commands like `docker tags`;
* `docker_image_being_used_by_container` ‐ removes the container that is using the image before removing the image;
* `dry` – fixes repetitions like `git git push`;
* `fab_command_not_found` – fix misspelled fabric commands;
* `fix_alt_space` – replaces Alt+Space with Space character;
@ -233,24 +200,15 @@ following rules are enabled by default:
* `git_add_force` &ndash; adds `--force` to `git add <pathspec>...` when paths are .gitignore'd;
* `git_bisect_usage` &ndash; fixes `git bisect strt`, `git bisect goood`, `git bisect rset`, etc. when bisecting;
* `git_branch_delete` &ndash; changes `git branch -d` to `git branch -D`;
* `git_branch_delete_checked_out` &ndash; changes `git branch -d` to `git checkout master && git branch -D` when trying to delete a checked out branch;
* `git_branch_exists` &ndash; offers `git branch -d foo`, `git branch -D foo` or `git checkout foo` when creating a branch that already exists;
* `git_branch_list` &ndash; catches `git branch list` in place of `git branch` and removes created branch;
* `git_branch_0flag` &ndash; fixes commands such as `git branch 0v` and `git branch 0r` removing the created branch;
* `git_checkout` &ndash; fixes branch name or creates new branch;
* `git_clone_git_clone` &ndash; replaces `git clone git clone ...` with `git clone ...`
* `git_clone_missing` &ndash; adds `git clone` to URLs that appear to link to a git repository.
* `git_commit_add` &ndash; offers `git commit -a ...` or `git commit -p ...` after previous commit if it failed because nothing was staged;
* `git_commit_amend` &ndash; offers `git commit --amend` after previous commit;
* `git_commit_reset` &ndash; offers `git reset HEAD~` after previous commit;
* `git_diff_no_index` &ndash; adds `--no-index` to previous `git diff` on untracked files;
* `git_diff_staged` &ndash; adds `--staged` to previous `git diff` with unexpected output;
* `git_fix_stash` &ndash; fixes `git stash` commands (misspelled subcommand and missing `save`);
* `git_flag_after_filename` &ndash; fixes `fatal: bad flag '...' after filename`
* `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 +216,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;
@ -267,13 +225,12 @@ following rules are enabled by default:
* `git_rm_recursive` &ndash; adds `-r` when you try to `rm` a directory;
* `git_rm_staged` &ndash; adds `-f` or `--cached` when you try to `rm` a file with staged changes
* `git_rebase_merge_dir` &ndash; offers `git rebase (--continue | --abort | --skip)` or removing the `.git/rebase-merge` dir when a rebase is in progress;
* `git_remote_seturl_add` &ndash; runs `git remote add` when `git remote set_url` on nonexistent remote;
* `git_remote_seturl_add` &ndash; runs `git remote add` when `git remote set_url` on nonexistant remote;
* `git_stash` &ndash; stashes your local modifications before rebasing or switching branch;
* `git_stash_pop` &ndash; adds your local modifications before popping stash, then resets;
* `git_tag_force` &ndash; adds `--force` to `git tag <tagname>` when the tag already exists;
* `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;
* `go_unknown_command` &ndash; fixes wrong `go` commands, for example `go bulid`;
* `gradle_no_task` &ndash; fixes not found or ambiguous `gradle` task;
* `gradle_wrapper` &ndash; replaces `gradle` with `./gradlew`;
* `grep_arguments_order` &ndash; fixes `grep` arguments order for situations like `grep -lir . test`;
@ -283,7 +240,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,59 +255,49 @@ 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`;
* `mvn_unknown_lifecycle_phase` &ndash; fixes misspelled lifecycle phases with `mvn`;
* `npm_missing_script` &ndash; fixes `npm` custom script name in `npm run-script <script>`;
* `npm_run_script` &ndash; adds missing `run-script` for custom `npm` scripts;
* `npm_wrong_command` &ndash; fixes wrong npm commands like `npm urgrade`;
* `no_command` &ndash; fixes wrong console commands, for example `vom/vim`;
* `no_such_file` &ndash; creates missing directories with `mv` and `cp` commands;
* `omnienv_no_such_command` &ndash; fixes wrong commands for `goenv`, `nodenv`, `pyenv` and `rbenv` (eg.: `pyenv isntall` or `goenv list`);
* `open` &ndash; either prepends `http://` to address passed to `open` or create a new file or directory and passes it to `open`;
* `pip_install` &ndash; fixes permission issues with `pip install` commands by adding `--user` or prepending `sudo` if necessary;
* `pip_unknown_command` &ndash; fixes wrong `pip` commands, for example `pip instatl/pip install`;
* `php_s` &ndash; replaces `-s` by `-S` when trying to run a local php server;
* `port_already_in_use` &ndash; kills process that bound port;
* `prove_recursively` &ndash; adds `-r` when called with directory;
* `python_command` &ndash; prepends `python` when you try to run non-executable/without `./` python script;
* `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 trailling 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;
* `yarn_help` &ndash; makes it easier to open `yarn` documentation;
##### [Back to Contents](#contents)
The following rules are enabled by default on specific platforms only:
* `apt_get` &ndash; installs app from apt if it not installed (requires `python-commandnotfound` / `python3-commandnotfound`);
@ -366,11 +313,8 @@ The following rules are enabled by default on specific platforms only:
* `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>`;
* `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_invalid_option` &ndash; replaces lowercase `pacman` options with uppercase.
* `pacman_not_found` &ndash; fixes package name with `pacman`, `yay`, `pikaur` or `yaourt`.
* `yum_invalid_operation` &ndash; fixes invalid `yum` calls, like `yum isntall vim`;
* `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`.
The following commands are bundled with *The Fuck*, but are not enabled by
default:
@ -378,8 +322,6 @@ default:
* `git_push_force` &ndash; adds `--force-with-lease` to a `git push` (may conflict with `git_push_pull`);
* `rm_root` &ndash; adds `--no-preserve-root` to `rm -rf /` command.
##### [Back to Contents](#contents)
## Creating your own rules
To add your own rule, create a file named `your-rule-name.py`
@ -403,8 +345,8 @@ Your rule should not change `Command`.
**Rules api changed in 3.0:** To access a rule's settings, import it with
`from thefuck.conf import settings`
`settings` is a special object assembled from `~/.config/thefuck/settings.py`,
`settings` is a special object assembled from `~/.config/thefuck/settings.py`,
and values from env ([see more below](#settings)).
A simple example rule for running a script with `sudo`:
@ -433,26 +375,23 @@ requires_output = True
[utility functions for rules](https://github.com/nvbn/thefuck/tree/master/thefuck/utils.py),
[app/os-specific helpers](https://github.com/nvbn/thefuck/tree/master/thefuck/specific/).
##### [Back to Contents](#contents)
## Settings
Several *The Fuck* parameters can be changed in the file `$XDG_CONFIG_HOME/thefuck/settings.py`
(`$XDG_CONFIG_HOME` defaults to `~/.config`):
* `rules` &ndash; list of enabled rules, by default `thefuck.const.DEFAULT_RULES`;
* `rules` &ndash; list of enabled rules, by default `thefuck.conf.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`.
* `excluded_search_path_prefixes` &ndash; path prefixes to ignore when searching for commands, by default `[]`.
* `num_close_matches` &ndash; maximum number of close matches to suggest, by default `3`.
An example of `settings.py`:
@ -475,17 +414,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_EXCLUDED_SEARCH_PATH_PREFIXES` &ndash; path prefixes to ignore when searching for commands, by default `[]`.
* `THEFUCK_NUM_CLOSE_MATCHES` &ndash; maximum number of close matches to suggest, like `5`.
For example:
@ -500,8 +438,6 @@ export THEFUCK_HISTORY_LIMIT='2000'
export THEFUCK_NUM_CLOSE_MATCHES='5'
```
##### [Back to Contents](#contents)
## Third-party packages with rules
If you'd like to make a specific set of non-public rules, but would still like
@ -521,8 +457,6 @@ thefuck_contrib_foo
*The Fuck* will find rules located in the `rules` module.
##### [Back to Contents](#contents)
## Experimental instant mode
The default behavior of *The Fuck* requires time to re-run previous commands.
@ -542,8 +476,6 @@ For example:
eval $(thefuck --alias --enable-experimental-instant-mode)
```
##### [Back to Contents](#contents)
## Developing
See [CONTRIBUTING.md](CONTRIBUTING.md)
@ -554,13 +486,13 @@ 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/
[workflow-badge]: https://github.com/nvbn/thefuck/workflows/Tests/badge.svg
[workflow-link]: https://github.com/nvbn/thefuck/actions?query=workflow%3ATests
[travis-badge]: https://travis-ci.org/nvbn/thefuck.svg?branch=master
[travis-link]: https://travis-ci.org/nvbn/thefuck
[appveyor-badge]: https://ci.appveyor.com/api/projects/status/1sskj4imj02um0gu/branch/master?svg=true
[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
[instant-mode-gif-link]: https://raw.githubusercontent.com/nvbn/thefuck/master/example_instant_mode.gif
[homebrew]: https://brew.sh/
##### [Back to Contents](#contents)
[homebrew]: http://brew.sh/

24
appveyor.yml Normal file
View File

@ -0,0 +1,24 @@
build: false
environment:
matrix:
- PYTHON: "C:/Python27"
- PYTHON: "C:/Python34"
- PYTHON: "C:/Python35"
- PYTHON: "C:/Python36"
- PYTHON: "C:/Python37"
init:
- "ECHO %PYTHON%"
- ps: "ls C:/Python*"
install:
- "curl -fsS -o C:/get-pip.py https://bootstrap.pypa.io/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%/python.exe -m flake8"
- "%PYTHON%/Scripts/py.test.exe -sv"

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

@ -37,7 +37,7 @@ http://github.com/ninjaaron/fast-entry_points
'''
from setuptools.command import easy_install
import re
TEMPLATE = r'''\
TEMPLATE = '''\
# -*- coding: utf-8 -*-
# EASY-INSTALL-ENTRY-SCRIPT: '{3}','{4}','{5}'
__requires__ = '{3}'
@ -83,7 +83,7 @@ def main():
import shutil
import sys
dests = sys.argv[1:] or ['.']
filename = re.sub(r'\.pyc$', '.py', __file__)
filename = re.sub('\.pyc$', '.py', __file__)
for dst in dests:
shutil.copy(filename, dst)

View File

@ -32,6 +32,5 @@ call('git push --tags', shell=True)
env = os.environ
env['CONVERT_README'] = 'true'
call('rm -rf dist/*', shell=True, env=env)
call('python setup.py sdist bdist_wheel', shell=True, env=env)
call('twine upload dist/*', shell=True, env=env)

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

@ -26,31 +26,18 @@ if version < (2, 7):
print('thefuck requires Python version 2.7 or later' +
' ({}.{} detected).'.format(*version))
sys.exit(-1)
elif (3, 0) < version < (3, 5):
print('thefuck requires Python version 3.5 or later' +
elif (3, 0) < version < (3, 4):
print('thefuck requires Python version 3.4 or later' +
' ({}.{} detected).'.format(*version))
sys.exit(-1)
VERSION = '3.32'
VERSION = '3.28'
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'],
":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 +50,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

@ -7,10 +7,6 @@ from thefuck.system import Path
shells.shell = shells.Generic()
def pytest_configure(config):
config.addinivalue_line("markers", "functional: mark test as functional")
def pytest_addoption(parser):
"""Adds `--enable-functional` argument."""
group = parser.getgroup("thefuck")
@ -46,7 +42,7 @@ def no_cache(monkeypatch):
@pytest.fixture(autouse=True)
def functional(request):
if request.node.get_closest_marker('functional') \
if request.node.get_marker('functional') \
and not request.config.getoption('enable_functional'):
pytest.skip('functional tests are disabled')

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

@ -76,45 +76,6 @@ apt_get_operations = ['update', 'upgrade', 'install', 'remove', 'autoremove',
'dselect-upgrade', 'clean', 'autoclean', 'check',
'changelog', 'download']
new_apt_get_help = b'''apt 1.6.12 (amd64)
Usage: apt-get [options] command
apt-get [options] install|remove pkg1 [pkg2 ...]
apt-get [options] source pkg1 [pkg2 ...]
apt-get is a command line interface for retrieval of packages
and information about them from authenticated sources and
for installation, upgrade and removal of packages together
with their dependencies.
Most used commands:
update - Retrieve new lists of packages
upgrade - Perform an upgrade
install - Install new packages (pkg is libc6 not libc6.deb)
remove - Remove packages
purge - Remove packages and config files
autoremove - Remove automatically all unused packages
dist-upgrade - Distribution upgrade, see apt-get(8)
dselect-upgrade - Follow dselect selections
build-dep - Configure build-dependencies for source packages
clean - Erase downloaded archive files
autoclean - Erase old downloaded archive files
check - Verify that there are no broken dependencies
source - Download source archives
download - Download the binary package into the current directory
changelog - Download and display the changelog for the given package
See apt-get(8) for more information about the available commands.
Configuration options and syntax is detailed in apt.conf(5).
Information about how to configure sources can be found in sources.list(5).
Package and version choices can be expressed via apt_preferences(5).
Security details are available in apt-secure(8).
This APT has Super Cow Powers.
'''
new_apt_get_operations = ['update', 'upgrade', 'install', 'remove', 'purge',
'autoremove', 'dist-upgrade', 'dselect-upgrade',
'build-dep', 'clean', 'autoclean', 'check',
'source', 'download', 'changelog']
@pytest.mark.parametrize('script, output', [
('apt', invalid_operation('saerch')),
@ -143,8 +104,7 @@ def set_help(mocker):
@pytest.mark.parametrize('app, help_text, operations', [
('apt', apt_help, apt_operations),
('apt-get', apt_get_help, apt_get_operations),
('apt-get', new_apt_get_help, new_apt_get_operations)
('apt-get', apt_get_help, apt_get_operations)
])
def test_get_operations(set_help, app, help_text, operations):
set_help(help_text)
@ -156,8 +116,6 @@ def test_get_operations(set_help, app, help_text, operations):
apt_get_help, 'apt-get install vim'),
('apt saerch vim', invalid_operation('saerch'),
apt_help, 'apt search vim'),
('apt uninstall vim', invalid_operation('uninstall'),
apt_help, 'apt remove vim'),
])
def test_get_new_command(set_help, output, script, help_text, result):
set_help(help_text)

View File

@ -1,10 +1,8 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.rules.apt_list_upgradable import get_new_command, match
from thefuck.types import Command
full_english_output = '''
match_output = '''
Hit:1 http://us.archive.ubuntu.com/ubuntu zesty InRelease
Hit:2 http://us.archive.ubuntu.com/ubuntu zesty-updates InRelease
Get:3 http://us.archive.ubuntu.com/ubuntu zesty-backports InRelease [89.2 kB]
@ -19,11 +17,6 @@ Reading state information... Done
8 packages can be upgraded. Run 'apt list --upgradable' to see them.
'''
match_output = [
full_english_output,
'Führen Sie »apt list --upgradable« aus, um sie anzuzeigen.' # German
]
no_match_output = '''
Hit:1 http://us.archive.ubuntu.com/ubuntu zesty InRelease
Get:2 http://us.archive.ubuntu.com/ubuntu zesty-updates InRelease [89.2 kB]
@ -55,9 +48,8 @@ All packages are up to date.
'''
@pytest.mark.parametrize('output', match_output)
def test_match(output):
assert match(Command('sudo apt update', output))
def test_match():
assert match(Command('sudo apt update', match_output))
@pytest.mark.parametrize('command', [
@ -75,10 +67,9 @@ def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('output', match_output)
def test_get_new_command(output):
new_command = get_new_command(Command('sudo apt update', output))
def test_get_new_command():
new_command = get_new_command(Command('sudo apt update', match_output))
assert new_command == 'sudo apt list --upgradable'
new_command = get_new_command(Command('apt update', output))
new_command = get_new_command(Command('apt update', match_output))
assert new_command == 'apt list --upgradable'

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

@ -1,11 +0,0 @@
from thefuck.rules.cd_cs import match, get_new_command
from thefuck.types import Command
def test_match():
assert match(Command('cs', 'cs: command not found'))
assert match(Command('cs /etc/', 'cs: command not found'))
def test_get_new_command():
assert get_new_command(Command('cs /etc/', 'cs: command not found')) == 'cd /etc/'

View File

@ -1,86 +0,0 @@
import pytest
from thefuck.rules.choco_install import match, get_new_command
from thefuck.types import Command
package_not_found_error = (
'Chocolatey v0.10.15\n'
'Installing the following packages:\n'
'logstitcher\n'
'By installing you accept licenses for the packages.\n'
'logstitcher not installed. The package was not found with the source(s) listed.\n'
' Source(s): \'https://chocolatey.org/api/v2/\'\n'
' NOTE: When you specify explicit sources, it overrides default sources.\n'
'If the package version is a prerelease and you didn\'t specify `--pre`,\n'
' the package may not be found.\n'
'Please see https://chocolatey.org/docs/troubleshooting for more\n'
' assistance.\n'
'\n'
'Chocolatey installed 0/1 packages. 1 packages failed.\n'
' See the log for details (C:\\ProgramData\\chocolatey\\logs\\chocolatey.log).\n'
'\n'
'Failures\n'
' - logstitcher - logstitcher not installed. The package was not found with the source(s) listed.\n'
' Source(s): \'https://chocolatey.org/api/v2/\'\n'
' NOTE: When you specify explicit sources, it overrides default sources.\n'
'If the package version is a prerelease and you didn\'t specify `--pre`,\n'
' the package may not be found.\n'
'Please see https://chocolatey.org/docs/troubleshooting for more\n'
' assistance.\n'
)
@pytest.mark.parametrize('command', [
Command('choco install logstitcher', package_not_found_error),
Command('cinst logstitcher', package_not_found_error),
Command('choco install logstitcher -y', package_not_found_error),
Command('cinst logstitcher -y', package_not_found_error),
Command('choco install logstitcher -y -n=test', package_not_found_error),
Command('cinst logstitcher -y -n=test', package_not_found_error),
Command('choco install logstitcher -y -n=test /env', package_not_found_error),
Command('cinst logstitcher -y -n=test /env', package_not_found_error),
Command('choco install chocolatey -y', package_not_found_error),
Command('cinst chocolatey -y', package_not_found_error)])
def test_match(command):
assert match(command)
@pytest.mark.parametrize('command', [
Command('choco /?', ''),
Command('choco upgrade logstitcher', ''),
Command('cup logstitcher', ''),
Command('choco upgrade logstitcher -y', ''),
Command('cup logstitcher -y', ''),
Command('choco upgrade logstitcher -y -n=test', ''),
Command('cup logstitcher -y -n=test', ''),
Command('choco upgrade logstitcher -y -n=test /env', ''),
Command('cup logstitcher -y -n=test /env', ''),
Command('choco upgrade chocolatey -y', ''),
Command('cup chocolatey -y', ''),
Command('choco uninstall logstitcher', ''),
Command('cuninst logstitcher', ''),
Command('choco uninstall logstitcher -y', ''),
Command('cuninst logstitcher -y', ''),
Command('choco uninstall logstitcher -y -n=test', ''),
Command('cuninst logstitcher -y -n=test', ''),
Command('choco uninstall logstitcher -y -n=test /env', ''),
Command('cuninst logstitcher -y -n=test /env', ''),
Command('choco uninstall chocolatey -y', ''),
Command('cuninst chocolatey -y', '')])
def not_test_match(command):
assert not match(command)
@pytest.mark.parametrize('before, after', [
('choco install logstitcher', 'choco install logstitcher.install'),
('cinst logstitcher', 'cinst logstitcher.install'),
('choco install logstitcher -y', 'choco install logstitcher.install -y'),
('cinst logstitcher -y', 'cinst logstitcher.install -y'),
('choco install logstitcher -y -n=test', 'choco install logstitcher.install -y -n=test'),
('cinst logstitcher -y -n=test', 'cinst logstitcher.install -y -n=test'),
('choco install logstitcher -y -n=test /env', 'choco install logstitcher.install -y -n=test /env'),
('cinst logstitcher -y -n=test /env', 'cinst logstitcher.install -y -n=test /env'),
('choco install chocolatey -y', 'choco install chocolatey.install -y'),
('cinst chocolatey -y', 'cinst chocolatey.install -y'), ])
def test_get_new_command(before, after):
assert (get_new_command(Command(before, '')) == after)

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

@ -1,24 +0,0 @@
import pytest
from thefuck.rules.conda_mistype import match, get_new_command
from thefuck.types import Command
@pytest.fixture
def mistype_response():
return """
CommandNotFoundError: No command 'conda lst'.
Did you mean 'conda list'?
"""
def test_match(mistype_response):
assert match(Command('conda lst', mistype_response))
err_response = 'bash: codna: command not found'
assert not match(Command('codna list', err_response))
def test_get_new_command(mistype_response):
assert (get_new_command(Command('conda lst', mistype_response)) == ['conda list'])

View File

@ -1,30 +0,0 @@
import pytest
from thefuck.rules.cp_create_destination import match, get_new_command
from thefuck.types import Command
@pytest.mark.parametrize(
"script, output",
[("cp", "cp: directory foo does not exist\n"), ("mv", "No such file or directory")],
)
def test_match(script, output):
assert match(Command(script, output))
@pytest.mark.parametrize(
"script, output", [("cp", ""), ("mv", ""), ("ls", "No such file or directory")]
)
def test_not_match(script, output):
assert not match(Command(script, output))
@pytest.mark.parametrize(
"script, output, new_command",
[
("cp foo bar/", "cp: directory foo does not exist\n", "mkdir -p bar/ && cp foo bar/"),
("mv foo bar/", "No such file or directory", "mkdir -p bar/ && mv foo bar/"),
("cp foo bar/baz/", "cp: directory foo does not exist\n", "mkdir -p bar/baz/ && cp foo bar/baz/"),
],
)
def test_get_new_command(script, output, new_command):
assert get_new_command(Command(script, output)) == new_command

View File

@ -1,27 +0,0 @@
from thefuck.rules.docker_image_being_used_by_container import match, get_new_command
from thefuck.types import Command
def test_match():
err_response = """Error response from daemon: conflict: unable to delete cd809b04b6ff (cannot be forced) - image is being used by running container e5e2591040d1"""
assert match(Command('docker image rm -f cd809b04b6ff', err_response))
def test_not_match():
err_response = 'bash: docker: command not found'
assert not match(Command('docker image rm -f cd809b04b6ff', err_response))
def test_not_docker_command():
err_response = """Error response from daemon: conflict: unable to delete cd809b04b6ff (cannot be forced) - image is being used by running container e5e2591040d1"""
assert not match(Command('git image rm -f cd809b04b6ff', err_response))
def test_get_new_command():
err_response = """
Error response from daemon: conflict: unable to delete cd809b04b6ff (cannot be forced) - image
is being used by running container e5e2591040d1
"""
result = get_new_command(Command('docker image rm -f cd809b04b6ff', err_response))
expected = 'docker container rm -f e5e2591040d1 && docker image rm -f cd809b04b6ff'
assert result == expected

View File

@ -1,37 +0,0 @@
from thefuck.rules.docker_login import match, get_new_command
from thefuck.types import Command
def test_match():
err_response1 = """
Sending build context to Docker daemon 118.8kB
Step 1/6 : FROM foo/bar:fdb7c6d
pull access denied for foo/bar, repository does not exist or may require 'docker login'
"""
assert match(Command('docker build -t artifactory:9090/foo/bar:fdb7c6d .', err_response1))
err_response2 = """
The push refers to repository [artifactory:9090/foo/bar]
push access denied for foo/bar, repository does not exist or may require 'docker login'
"""
assert match(Command('docker push artifactory:9090/foo/bar:fdb7c6d', err_response2))
err_response3 = """
docker push artifactory:9090/foo/bar:fdb7c6d
The push refers to repository [artifactory:9090/foo/bar]
9c29c7ad209d: Preparing
71f3ad53dfe0: Preparing
f58ee068224c: Preparing
aeddc924d0f7: Preparing
c2040e5d6363: Preparing
4d42df4f350f: Preparing
35723dab26f9: Preparing
71f3ad53dfe0: Pushed
cb95fa0faeb1: Layer already exists
"""
assert not match(Command('docker push artifactory:9090/foo/bar:fdb7c6d', err_response3))
def test_get_new_command():
assert get_new_command(Command('docker build -t artifactory:9090/foo/bar:fdb7c6d .', '')) == 'docker login && docker build -t artifactory:9090/foo/bar:fdb7c6d .'
assert get_new_command(Command('docker push artifactory:9090/foo/bar:fdb7c6d', '')) == 'docker login && docker push artifactory:9090/foo/bar:fdb7c6d'

View File

@ -4,46 +4,6 @@ from thefuck.types import Command
from thefuck.rules.docker_not_command import get_new_command, match
_DOCKER_SWARM_OUTPUT = '''
Usage: docker swarm COMMAND
Manage Swarm
Commands:
ca Display and rotate the root CA
init Initialize a swarm
join Join a swarm as a node and/or manager
join-token Manage join tokens
leave Leave the swarm
unlock Unlock swarm
unlock-key Manage the unlock key
update Update the swarm
Run 'docker swarm COMMAND --help' for more information on a command.
'''
_DOCKER_IMAGE_OUTPUT = '''
Usage: docker image COMMAND
Manage images
Commands:
build Build an image from a Dockerfile
history Show the history of an image
import Import the contents from a tarball to create a filesystem image
inspect Display detailed information on one or more images
load Load an image from a tar archive or STDIN
ls List images
prune Remove unused images
pull Pull an image or a repository from a registry
push Push an image or a repository to a registry
rm Remove one or more images
save Save one or more images to a tar archive (streamed to STDOUT by default)
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
Run 'docker image COMMAND --help' for more information on a command.
'''
@pytest.fixture
def docker_help(mocker):
help = b'''Usage: docker [OPTIONS] COMMAND [arg...]
@ -144,94 +104,6 @@ Run 'docker COMMAND --help' for more information on a command.
return mock
@pytest.fixture
def docker_help_new(mocker):
helptext_new = b'''
Usage: docker [OPTIONS] COMMAND
A self-sufficient runtime for containers
Options:
--config string Location of client config files (default "/Users/ik1ne/.docker")
-c, --context string Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var
and default context set with "docker context use")
-D, --debug Enable debug mode
-H, --host list Daemon socket(s) to connect to
-l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info")
--tls Use TLS; implied by --tlsverify
--tlscacert string Trust certs signed only by this CA (default "/Users/ik1ne/.docker/ca.pem")
--tlscert string Path to TLS certificate file (default "/Users/ik1ne/.docker/cert.pem")
--tlskey string Path to TLS key file (default "/Users/ik1ne/.docker/key.pem")
--tlsverify Use TLS and verify the remote
-v, --version Print version information and quit
Management Commands:
builder Manage builds
config Manage Docker configs
container Manage containers
context Manage contexts
image Manage images
network Manage networks
node Manage Swarm nodes
plugin Manage plugins
secret Manage Docker secrets
service Manage services
stack Manage Docker stacks
swarm Manage Swarm
system Manage Docker
trust Manage trust on Docker images
volume Manage volumes
Commands:
attach Attach local standard input, output, and error streams to a running container
build Build an image from a Dockerfile
commit Create a new image from a container's changes
cp Copy files/folders between a container and the local filesystem
create Create a new container
diff Inspect changes to files or directories on a container's filesystem
events Get real time events from the server
exec Run a command in a running container
export Export a container's filesystem as a tar archive
history Show the history of an image
images List images
import Import the contents from a tarball to create a filesystem image
info Display system-wide information
inspect Return low-level information on Docker objects
kill Kill one or more running containers
load Load an image from a tar archive or STDIN
login Log in to a Docker registry
logout Log out from a Docker registry
logs Fetch the logs of a container
pause Pause all processes within one or more containers
port List port mappings or a specific mapping for the container
ps List containers
pull Pull an image or a repository from a registry
push Push an image or a repository to a registry
rename Rename a container
restart Restart one or more containers
rm Remove one or more containers
rmi Remove one or more images
run Run a command in a new container
save Save one or more images to a tar archive (streamed to STDOUT by default)
search Search the Docker Hub for images
start Start one or more stopped containers
stats Display a live stream of container(s) resource usage statistics
stop Stop one or more running containers
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
top Display the running processes of a container
unpause Unpause all processes within one or more containers
update Update configuration of one or more containers
version Show the Docker version information
wait Block until one or more containers stop, then print their exit codes
Run 'docker COMMAND --help' for more information on a command.
'''
mock = mocker.patch('subprocess.Popen')
mock.return_value.stdout = BytesIO(b'')
mock.return_value.stderr = BytesIO(helptext_new)
return mock
def output(cmd):
return "docker: '{}' is not a docker command.\n" \
"See 'docker --help'.".format(cmd)
@ -241,24 +113,6 @@ def test_match():
assert match(Command('docker pes', output('pes')))
# tests docker (management command)
@pytest.mark.usefixtures('no_memoize')
@pytest.mark.parametrize('script, output', [
('docker swarn', output('swarn')),
('docker imge', output('imge'))])
def test_match_management_cmd(script, output):
assert match(Command(script, output))
# tests docker (management cmd) (management subcmd)
@pytest.mark.usefixtures('no_memoize')
@pytest.mark.parametrize('script, output', [
('docker swarm int', _DOCKER_SWARM_OUTPUT),
('docker image la', _DOCKER_IMAGE_OUTPUT)])
def test_match_management_subcmd(script, output):
assert match(Command(script, output))
@pytest.mark.parametrize('script, output', [
('docker ps', ''),
('cat pes', output('pes'))])
@ -266,28 +120,10 @@ def test_not_match(script, output):
assert not match(Command(script, output))
@pytest.mark.usefixtures('no_memoize', 'docker_help')
@pytest.mark.usefixtures('docker_help')
@pytest.mark.parametrize('wrong, fixed', [
('pes', ['ps', 'push', 'pause']),
('tags', ['tag', 'stats', 'images'])])
def test_get_new_command(wrong, fixed):
command = Command('docker {}'.format(wrong), output(wrong))
assert get_new_command(command) == ['docker {}'.format(x) for x in fixed]
@pytest.mark.usefixtures('no_memoize', 'docker_help_new')
@pytest.mark.parametrize('wrong, fixed', [
('swarn', ['swarm', 'start', 'search']),
('inage', ['image', 'images', 'rename'])])
def test_get_new_management_command(wrong, fixed):
command = Command('docker {}'.format(wrong), output(wrong))
assert get_new_command(command) == ['docker {}'.format(x) for x in fixed]
@pytest.mark.usefixtures('no_memoize', 'docker_help_new')
@pytest.mark.parametrize('wrong, fixed, output', [
('swarm int', ['swarm init', 'swarm join', 'swarm join-token'], _DOCKER_SWARM_OUTPUT),
('image la', ['image load', 'image ls', 'image tag'], _DOCKER_IMAGE_OUTPUT)])
def test_get_new_management_command_subcommand(wrong, fixed, output):
command = Command('docker {}'.format(wrong), output)
assert get_new_command(command) == ['docker {}'.format(x) for x in fixed]

View File

@ -2,54 +2,62 @@
import pytest
import os
from collections import namedtuple
from thefuck.rules.fix_file import match, get_new_command
from thefuck.types import Command
FixFileTest = namedtuple('FixFileTest', ['script', 'file', 'line', 'col', 'output'])
# (script, file, line, col (or None), output)
tests = (
FixFileTest('gcc a.c', 'a.c', 3, 1, """
('gcc a.c', 'a.c', 3, 1,
"""
a.c: In function 'main':
a.c:3:1: error: expected expression before '}' token
}
^
"""),
FixFileTest('clang a.c', 'a.c', 3, 1, """
('clang a.c', 'a.c', 3, 1,
"""
a.c:3:1: error: expected expression
}
^
"""),
FixFileTest('perl a.pl', 'a.pl', 3, None, """
('perl a.pl', 'a.pl', 3, None,
"""
syntax error at a.pl line 3, at EOF
Execution of a.pl aborted due to compilation errors.
"""),
FixFileTest('perl a.pl', 'a.pl', 2, None, """
('perl a.pl', 'a.pl', 2, None,
"""
Search pattern not terminated at a.pl line 2.
"""),
FixFileTest('sh a.sh', 'a.sh', 2, None, """
('sh a.sh', 'a.sh', 2, None,
"""
a.sh: line 2: foo: command not found
"""),
FixFileTest('zsh a.sh', 'a.sh', 2, None, """
('zsh a.sh', 'a.sh', 2, None,
"""
a.sh:2: command not found: foo
"""),
FixFileTest('bash a.sh', 'a.sh', 2, None, """
('bash a.sh', 'a.sh', 2, None,
"""
a.sh: line 2: foo: command not found
"""),
FixFileTest('rustc a.rs', 'a.rs', 2, 5, """
('rustc a.rs', 'a.rs', 2, 5,
"""
a.rs:2:5: 2:6 error: unexpected token: `+`
a.rs:2 +
^
"""),
FixFileTest('cargo build', 'src/lib.rs', 3, 5, """
('cargo build', 'src/lib.rs', 3, 5,
"""
Compiling test v0.1.0 (file:///tmp/fix-error/test)
src/lib.rs:3:5: 3:6 error: unexpected token: `+`
src/lib.rs:3 +
@ -59,14 +67,16 @@ Could not compile `test`.
To learn more, run the command again with --verbose.
"""),
FixFileTest('python a.py', 'a.py', 2, None, """
('python a.py', 'a.py', 2, None,
"""
File "a.py", line 2
+
^
SyntaxError: invalid syntax
"""),
FixFileTest('python a.py', 'a.py', 8, None, """
('python a.py', 'a.py', 8, None,
"""
Traceback (most recent call last):
File "a.py", line 8, in <module>
match("foo")
@ -79,7 +89,8 @@ Traceback (most recent call last):
TypeError: first argument must be string or compiled pattern
"""),
FixFileTest(u'python café.py', u'café.py', 8, None, u"""
(u'python café.py', u'café.py', 8, None,
u"""
Traceback (most recent call last):
File "café.py", line 8, in <module>
match("foo")
@ -92,48 +103,57 @@ Traceback (most recent call last):
TypeError: first argument must be string or compiled pattern
"""),
FixFileTest('ruby a.rb', 'a.rb', 3, None, """
('ruby a.rb', 'a.rb', 3, None,
"""
a.rb:3: syntax error, unexpected keyword_end
"""),
FixFileTest('lua a.lua', 'a.lua', 2, None, """
('lua a.lua', 'a.lua', 2, None,
"""
lua: a.lua:2: unexpected symbol near '+'
"""),
FixFileTest('fish a.sh', '/tmp/fix-error/a.sh', 2, None, """
('fish a.sh', '/tmp/fix-error/a.sh', 2, None,
"""
fish: Unknown command 'foo'
/tmp/fix-error/a.sh (line 2): foo
^
"""),
FixFileTest('./a', './a', 2, None, """
('./a', './a', 2, None,
"""
awk: ./a:2: BEGIN { print "Hello, world!" + }
awk: ./a:2: ^ syntax error
"""),
FixFileTest('llc a.ll', 'a.ll', 1, 2, """
('llc a.ll', 'a.ll', 1, 2,
"""
llc: a.ll:1:2: error: expected top-level entity
+
^
"""),
FixFileTest('go build a.go', 'a.go', 1, 2, """
('go build a.go', 'a.go', 1, 2,
"""
can't load package:
a.go:1:2: expected 'package', found '+'
"""),
FixFileTest('make', 'Makefile', 2, None, """
('make', 'Makefile', 2, None,
"""
bidule
make: bidule: Command not found
Makefile:2: recipe for target 'target' failed
make: *** [target] Error 127
"""),
FixFileTest('git st', '/home/martin/.config/git/config', 1, None, """
('git st', '/home/martin/.config/git/config', 1, None,
"""
fatal: bad config file line 1 in /home/martin/.config/git/config
"""),
FixFileTest('node fuck.js asdf qwer', '/Users/pablo/Workspace/barebones/fuck.js', '2', 5, """
('node fuck.js asdf qwer', '/Users/pablo/Workspace/barebones/fuck.js', '2', 5,
"""
/Users/pablo/Workspace/barebones/fuck.js:2
conole.log(arg); // this should read console.log(arg);
^
@ -150,14 +170,16 @@ ReferenceError: conole is not defined
at node.js:814:3
"""),
FixFileTest('pep8', './tests/rules/test_systemctl.py', 17, 80, """
('pep8', './tests/rules/test_systemctl.py', 17, 80,
"""
./tests/rules/test_systemctl.py:17:80: E501 line too long (93 > 79 characters)
./tests/rules/test_systemctl.py:18:80: E501 line too long (103 > 79 characters)
./tests/rules/test_whois.py:20:80: E501 line too long (89 > 79 characters)
./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, """
('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")
@ -169,7 +191,7 @@ E NameError: name 'mocker' is not defined
/home/thefuck/tests/rules/test_fix_file.py:218: NameError
"""),
)
) # noqa
@pytest.mark.parametrize('test', tests)
@ -177,7 +199,7 @@ E NameError: name 'mocker' is not defined
def test_match(mocker, monkeypatch, test):
mocker.patch('os.path.isfile', return_value=True)
monkeypatch.setenv('EDITOR', 'dummy_editor')
assert match(Command('', test.output))
assert match(Command('', test[4]))
@pytest.mark.parametrize('test', tests)
@ -187,7 +209,7 @@ def test_no_editor(mocker, monkeypatch, test):
if 'EDITOR' in os.environ:
monkeypatch.delenv('EDITOR')
assert not match(Command('', test.output))
assert not match(Command('', test[4]))
@pytest.mark.parametrize('test', tests)
@ -196,7 +218,7 @@ def test_not_file(mocker, monkeypatch, test):
mocker.patch('os.path.isfile', return_value=False)
monkeypatch.setenv('EDITOR', 'dummy_editor')
assert not match(Command('', test.output))
assert not match(Command('', test[4]))
@pytest.mark.parametrize('test', tests)
@ -212,12 +234,12 @@ def test_get_new_command_with_settings(mocker, monkeypatch, test, settings):
mocker.patch('os.path.isfile', return_value=True)
monkeypatch.setenv('EDITOR', 'dummy_editor')
cmd = Command(test.script, test.output)
cmd = Command(test[0], test[4])
settings.fixcolcmd = '{editor} {file} +{line}:{col}'
if test.col:
if test[3]:
assert (get_new_command(cmd) ==
u'dummy_editor {} +{}:{} && {}'.format(test.file, test.line, test.col, test.script))
u'dummy_editor {} +{}:{} && {}'.format(test[1], test[2], test[3], test[0]))
else:
assert (get_new_command(cmd) ==
u'dummy_editor {} +{} && {}'.format(test.file, test.line, test.script))
u'dummy_editor {} +{} && {}'.format(test[1], test[2], test[0]))

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,29 +0,0 @@
import pytest
from thefuck.rules.git_branch_delete_checked_out import match, get_new_command
from thefuck.types import Command
@pytest.fixture
def output():
return "error: Cannot delete branch 'foo' checked out at '/bar/foo'"
@pytest.mark.parametrize("script", ["git branch -d foo", "git branch -D foo"])
def test_match(script, output):
assert match(Command(script, output))
@pytest.mark.parametrize("script", ["git branch -d foo", "git branch -D foo"])
def test_not_match(script):
assert not match(Command(script, "Deleted branch foo (was a1b2c3d)."))
@pytest.mark.parametrize(
"script, new_command",
[
("git branch -d foo", "git checkout master && git branch -D foo"),
("git branch -D foo", "git checkout master && git branch -D foo"),
],
)
def test_get_new_command(script, new_command, output):
assert get_new_command(Command(script, output)) == new_command

View File

@ -39,11 +39,6 @@ def test_not_match(command):
(b'', []),
(b'* master', ['master']),
(b' remotes/origin/master', ['master']),
(b' remotes/origin/test/1', ['test/1']),
(b' remotes/origin/test/1/2/3', ['test/1/2/3']),
(b' test/1', ['test/1']),
(b' test/1/2/3', ['test/1/2/3']),
(b' remotes/origin/HEAD -> origin/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',
@ -56,18 +51,18 @@ def test_get_branches(branches, branch_list, git_branch):
@pytest.mark.parametrize('branches, command, new_command', [
(b'',
Command('git checkout unknown', did_not_match('unknown')),
['git checkout -b unknown']),
'git checkout -b unknown'),
(b'',
Command('git commit unknown', did_not_match('unknown')),
['git branch unknown && git commit unknown']),
'git branch unknown && git commit unknown'),
(b' test-random-branch-123',
Command('git checkout tst-rdm-brnch-123',
did_not_match('tst-rdm-brnch-123')),
['git checkout test-random-branch-123', 'git checkout -b tst-rdm-brnch-123']),
'git checkout test-random-branch-123'),
(b' test-random-branch-123',
Command('git commit tst-rdm-brnch-123',
did_not_match('tst-rdm-brnch-123')),
['git commit test-random-branch-123'])])
'git commit test-random-branch-123')])
def test_get_new_command(branches, command, new_command, git_branch):
git_branch(branches)
assert get_new_command(command) == new_command

View File

@ -1,24 +0,0 @@
from thefuck.rules.git_clone_git_clone import match, get_new_command
from thefuck.types import Command
output_clean = """
fatal: Too many arguments.
usage: git clone [<options>] [--] <repo> [<dir>]
"""
def test_match():
assert match(Command('git clone git clone foo', output_clean))
def test_not_match():
assert not match(Command('', ''))
assert not match(Command('git branch', ''))
assert not match(Command('git clone foo', ''))
assert not match(Command('git clone foo bar baz', output_clean))
def test_get_new_command():
assert get_new_command(Command('git clone git clone foo', output_clean)) == 'git clone foo'

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,25 +0,0 @@
import pytest
from thefuck.rules.git_commit_reset import match, get_new_command
from thefuck.types import Command
@pytest.mark.parametrize('script, output', [
('git commit -m "test"', 'test output'),
('git commit', '')])
def test_match(output, script):
assert match(Command(script, output))
@pytest.mark.parametrize('script', [
'git branch foo',
'git checkout feature/test_commit',
'git push'])
def test_not_match(script):
assert not match(Command(script, ''))
@pytest.mark.parametrize('script', [
('git commit -m "test commit"'),
('git commit')])
def test_get_new_command(script):
assert get_new_command(Command(script, '')) == 'git reset HEAD~'

View File

@ -1,43 +0,0 @@
import pytest
from thefuck.rules.git_hook_bypass import match, get_new_command
from thefuck.types import Command
@pytest.mark.parametrize(
"command",
[
Command("git am", ""),
Command("git commit", ""),
Command("git commit -m 'foo bar'", ""),
Command("git push", ""),
Command("git push -u foo bar", ""),
],
)
def test_match(command):
assert match(command)
@pytest.mark.parametrize(
"command",
[
Command("git add foo", ""),
Command("git status", ""),
Command("git diff foo bar", ""),
],
)
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize(
"command, new_command",
[
(Command("git am", ""), "git am --no-verify"),
(Command("git commit", ""), "git commit --no-verify"),
(Command("git commit -m 'foo bar'", ""), "git commit --no-verify -m 'foo bar'"),
(Command("git push", ""), "git push --no-verify"),
(Command("git push -p", ""), "git push --no-verify -p"),
],
)
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@ -1,29 +0,0 @@
import pytest
from thefuck.rules.git_lfs_mistype import match, get_new_command
from thefuck.types import Command
@pytest.fixture
def mistype_response():
return """
Error: unknown command "evn" for "git-lfs"
Did you mean this?
env
ext
Run 'git-lfs --help' for usage.
"""
def test_match(mistype_response):
assert match(Command('git lfs evn', mistype_response))
err_response = 'bash: git: command not found'
assert not match(Command('git lfs env', err_response))
assert not match(Command('docker lfs env', mistype_response))
def test_get_new_command(mistype_response):
assert (get_new_command(Command('git lfs evn', mistype_response))
== ['git lfs env', 'git lfs ext'])

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

@ -1,82 +0,0 @@
import pytest
from io import BytesIO
from thefuck.rules.go_unknown_command import match, get_new_command
from thefuck.types import Command
@pytest.fixture
def build_misspelled_output():
return '''go bulid: unknown command
Run 'go help' for usage.'''
@pytest.fixture
def go_stderr(mocker):
stderr = b'''Go is a tool for managing Go source code.
Usage:
\tgo <command> [arguments]
The commands are:
\tbug start a bug report
\tbuild compile packages and dependencies
\tclean remove object files and cached files
\tdoc show documentation for package or symbol
\tenv print Go environment information
\tfix update packages to use new APIs
\tfmt gofmt (reformat) package sources
\tgenerate generate Go files by processing source
\tget add dependencies to current module and install them
\tinstall compile and install packages and dependencies
\tlist list packages or modules
\tmod module maintenance
\trun compile and run Go program
\ttest test packages
\ttool run specified go tool
\tversion print Go version
\tvet report likely mistakes in packages
Use "go help <command>" for more information about a command.
Additional help topics:
\tbuildconstraint build constraints
\tbuildmode build modes
\tc calling between Go and C
\tcache build and test caching
\tenvironment environment variables
\tfiletype file types
\tgo.mod the go.mod file
\tgopath GOPATH environment variable
\tgopath-get legacy GOPATH go get
\tgoproxy module proxy protocol
\timportpath import path syntax
\tmodules modules, module versions, and more
\tmodule-get module-aware go get
\tmodule-auth module authentication using go.sum
\tmodule-private module configuration for non-public modules
\tpackages package lists and patterns
\ttestflag testing flags
\ttestfunc testing functions
Use "go help <topic>" for more information about that topic.
'''
mock = mocker.patch('subprocess.Popen')
mock.return_value.stderr = BytesIO(stderr)
return mock
def test_match(build_misspelled_output):
assert match(Command('go bulid', build_misspelled_output))
def test_not_match():
assert not match(Command('go run', 'go run: no go files listed'))
@pytest.mark.usefixtures('no_memoize', 'go_stderr')
def test_get_new_command(build_misspelled_output):
assert get_new_command(Command('go bulid', build_misspelled_output)) == 'go build'

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

@ -1,25 +0,0 @@
import pytest
from thefuck.rules.nixos_cmd_not_found import match, get_new_command
from thefuck.types import Command
@pytest.mark.parametrize('command', [
Command('vim', 'nix-env -iA nixos.vim')])
def test_match(mocker, command):
mocker.patch('thefuck.rules.nixos_cmd_not_found', return_value=None)
assert match(command)
@pytest.mark.parametrize('command', [
Command('vim', ''),
Command('', '')])
def test_not_match(mocker, command):
mocker.patch('thefuck.rules.nixos_cmd_not_found', return_value=None)
assert not match(command)
@pytest.mark.parametrize('command, new_command', [
(Command('vim', 'nix-env -iA nixos.vim'), 'nix-env -iA nixos.vim && vim'),
(Command('pacman', 'nix-env -iA nixos.pacman'), 'nix-env -iA nixos.pacman && pacman')])
def test_get_new_command(mocker, command, new_command):
assert get_new_command(command) == new_command

View File

@ -6,7 +6,7 @@ from thefuck.types 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', 'python'])
return_value=['vim', 'fsck', 'git', 'go'])
@pytest.fixture(autouse=True)
@ -20,9 +20,7 @@ def history_without_current(mocker):
@pytest.mark.parametrize('script, output', [
('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 +42,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

@ -1,57 +0,0 @@
import pytest
from thefuck.rules.omnienv_no_such_command import get_new_command, match
from thefuck.types import Command
@pytest.fixture
def output(pyenv_cmd):
return "pyenv: no such command `{}'".format(pyenv_cmd)
@pytest.fixture(autouse=True)
def Popen(mocker):
mock = mocker.patch('thefuck.rules.omnienv_no_such_command.Popen')
mock.return_value.stdout.readlines.return_value = (
b'--version\nactivate\ncommands\ncompletions\ndeactivate\nexec_\n'
b'global\nhelp\nhooks\ninit\ninstall\nlocal\nprefix_\n'
b'realpath.dylib\nrehash\nroot\nshell\nshims\nuninstall\nversion_\n'
b'version-file\nversion-file-read\nversion-file-write\nversion-name_\n'
b'version-origin\nversions\nvirtualenv\nvirtualenv-delete_\n'
b'virtualenv-init\nvirtualenv-prefix\nvirtualenvs_\n'
b'virtualenvwrapper\nvirtualenvwrapper_lazy\nwhence\nwhich_\n'
).split()
return mock
@pytest.mark.parametrize('script, pyenv_cmd', [
('pyenv globe', 'globe'),
('pyenv intall 3.8.0', 'intall'),
('pyenv list', 'list'),
])
def test_match(script, pyenv_cmd, output):
assert match(Command(script, output=output))
def test_match_goenv_output_quote():
"""test goenv's specific output with quotes (')"""
assert match(Command('goenv list', output="goenv: no such command 'list'"))
@pytest.mark.parametrize('script, output', [
('pyenv global', 'system'),
('pyenv versions', ' 3.7.0\n 3.7.1\n* 3.7.2\n'),
('pyenv install --list', ' 3.7.0\n 3.7.1\n 3.7.2\n'),
])
def test_not_match(script, output):
assert not match(Command(script, output=output))
@pytest.mark.parametrize('script, pyenv_cmd, result', [
('pyenv globe', 'globe', 'pyenv global'),
('pyenv intall 3.8.0', 'intall', 'pyenv install 3.8.0'),
('pyenv list', 'list', 'pyenv install --list'),
('pyenv remove 3.8.0', 'remove', 'pyenv uninstall 3.8.0'),
])
def test_get_new_command(script, pyenv_cmd, output, result):
assert result in get_new_command(Command(script, output))

View File

@ -1,30 +0,0 @@
import pytest
from thefuck.rules.pacman_invalid_option import get_new_command, match
from thefuck.types import Command
good_output = """community/shared_meataxe 1.0-3
A set of programs for working with matrix representations over finite fields
"""
bad_output = "error: invalid option '-"
@pytest.mark.parametrize("option", "SURQFDVT")
def test_not_match_good_output(option):
assert not match(Command("pacman -{}s meat".format(option), good_output))
@pytest.mark.parametrize("option", "azxcbnm")
def test_not_match_bad_output(option):
assert not match(Command("pacman -{}v meat".format(option), bad_output))
@pytest.mark.parametrize("option", "surqfdvt")
def test_match(option):
assert match(Command("pacman -{}v meat".format(option), bad_output))
@pytest.mark.parametrize("option", "surqfdvt")
def test_get_new_command(option):
new_command = get_new_command(Command("pacman -{}v meat".format(option), ""))
assert new_command == "pacman -{}v meat".format(option.upper())

View File

@ -11,8 +11,6 @@ extra/llvm35 3.5.2-13/usr/bin/llc'''
@pytest.mark.skipif(not getattr(pacman_not_found, 'enabled_by_default', True),
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')])
@ -21,8 +19,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')])
@ -35,8 +31,6 @@ def test_match_mocked(subp_mock, command):
@pytest.mark.skipif(not getattr(pacman_not_found, 'enabled_by_default', True),
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'])])
@ -45,8 +39,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,27 +0,0 @@
# -*- coding: UTF-8 -*-
from thefuck.rules.pip_install import match, get_new_command
from thefuck.types import Command
def test_match():
response1 = """
Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: '/Library/Python/2.7/site-packages/entrypoints.pyc'
Consider using the `--user` option or check the permissions.
"""
assert match(Command('pip install -r requirements.txt', response1))
response2 = """
Collecting bacon
Downloading https://files.pythonhosted.org/packages/b2/81/19fb79139ee71c8bc4e5a444546f318e2b87253b8939ec8a7e10d63b7341/bacon-0.3.1.zip (11.0MB)
100% || 11.0MB 3.0MB/s
Installing collected packages: bacon
Running setup.py install for bacon ... done
Successfully installed bacon-0.3.1
"""
assert not match(Command('pip install bacon', response2))
def test_get_new_command():
assert get_new_command(Command('pip install -r requirements.txt', '')) == 'pip install --user -r requirements.txt'
assert get_new_command(Command('pip install bacon', '')) == 'pip install --user bacon'
assert get_new_command(Command('pip install --user -r requirements.txt', '')) == 'sudo pip install -r requirements.txt'

View File

@ -3,35 +3,22 @@ from thefuck.rules.pip_unknown_command import match, get_new_command
from thefuck.types import Command
@pytest.fixture
def pip_unknown_cmd():
return '''ERROR: unknown command "instatl" - maybe you meant "install"'''
@pytest.fixture
def pip_unknown_cmd_without_recommend():
return '''ERROR: unknown command "i"'''
@pytest.fixture
def broken():
return 'instatl'
@pytest.fixture
def suggested():
return 'install'
@pytest.fixture
def pip_unknown_cmd(broken, suggested):
return 'ERROR: unknown command "{}" - maybe you meant "{}"'.format(broken, suggested)
def test_match(pip_unknown_cmd, pip_unknown_cmd_without_recommend):
assert match(Command('pip instatl', pip_unknown_cmd))
assert not match(Command('pip i',
pip_unknown_cmd_without_recommend))
@pytest.mark.parametrize('script, broken, suggested, new_cmd', [
('pip un+install thefuck', 'un+install', 'uninstall', 'pip uninstall thefuck'),
('pip instatl', 'instatl', 'install', 'pip install')])
def test_get_new_command(script, new_cmd, pip_unknown_cmd):
assert get_new_command(Command(script,
pip_unknown_cmd)) == new_cmd
def test_get_new_command(pip_unknown_cmd):
assert get_new_command(Command('pip instatl',
pip_unknown_cmd)) == 'pip install'

View File

@ -1,63 +0,0 @@
import pytest
from thefuck.rules.python_module_error import get_new_command, match
from thefuck.types import Command
@pytest.fixture
def module_error_output(filename, module_name):
return """Traceback (most recent call last):
File "{0}", line 1, in <module>
import {1}
ModuleNotFoundError: No module named '{1}'""".format(
filename, module_name
)
@pytest.mark.parametrize(
"test",
[
Command("python hello_world.py", "Hello World"),
Command(
"./hello_world.py",
"""Traceback (most recent call last):
File "hello_world.py", line 1, in <module>
pritn("Hello World")
NameError: name 'pritn' is not defined""",
),
],
)
def test_not_match(test):
assert not match(test)
positive_tests = [
(
"python some_script.py",
"some_script.py",
"more_itertools",
"pip install more_itertools && python some_script.py",
),
(
"./some_other_script.py",
"some_other_script.py",
"a_module",
"pip install a_module && ./some_other_script.py",
),
]
@pytest.mark.parametrize(
"script, filename, module_name, corrected_script", positive_tests
)
def test_match(script, filename, module_name, corrected_script, module_error_output):
assert match(Command(script, module_error_output))
@pytest.mark.parametrize(
"script, filename, module_name, corrected_script", positive_tests
)
def test_get_new_command(
script, filename, module_name, corrected_script, module_error_output
):
assert get_new_command(Command(script, module_error_output)) == corrected_script

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

@ -1,48 +0,0 @@
import pytest
from thefuck.rules.remove_shell_prompt_literal import match, get_new_command
from thefuck.types import Command
@pytest.fixture
def output():
return "$: command not found"
@pytest.mark.parametrize(
"script",
[
"$ cd newdir",
" $ cd newdir",
"$ $ cd newdir",
" $ $ cd newdir",
],
)
def test_match(script, output):
assert match(Command(script, output))
@pytest.mark.parametrize(
"command",
[
Command("$", "$: command not found"),
Command(" $", "$: command not found"),
Command("$?", "127: command not found"),
Command(" $?", "127: command not found"),
Command("", ""),
],
)
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize(
"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):
assert get_new_command(Command(script, output)) == new_command

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,7 +1,6 @@
# -*- encoding: utf-8 -*-
import pytest
from thefuck.rules import switch_lang
from thefuck.types import Command
@ -10,8 +9,7 @@ from thefuck.types import Command
Command(u'фзе-пуе', 'command not found: фзе-пуе'),
Command(u'λσ', 'command not found: λσ'),
Command(u'שפא-עקא', 'command not found: שפא-עקא'),
Command(u'ךד', 'command not found: ךד'),
Command(u'녀애 ㅣㄴ', 'command not found: 녀애 ㅣㄴ')])
Command(u'ךד', 'command not found: ךד')])
def test_match(command):
assert switch_lang.match(command)
@ -21,8 +19,7 @@ def test_match(command):
Command(u'ls', 'command not found: ls'),
Command(u'агсл', 'command not found: агсл'),
Command(u'фзе-пуе', 'some info'),
Command(u'שפא-עקא', 'some info'),
Command(u'녀애 ㅣㄴ', 'some info')])
Command(u'שפא-עקא', 'some info')])
def test_not_match(command):
assert not switch_lang.match(command)
@ -31,9 +28,6 @@ def test_not_match(command):
(Command(u'фзе-пуе штыефдд мшь', ''), 'apt-get install vim'),
(Command(u'λσ -λα', ''), 'ls -la'),
(Command(u'שפא-עקא ןמדאשךך הןצ', ''), 'apt-get install vim'),
(Command(u'ךד -ךש', ''), 'ls -la'),
(Command(u'멧-ㅎㄷㅅ ㅑㅜㄴㅅ미ㅣ 퍄ㅡ', ''), 'apt-get install vim'),
(Command(u'ㅣㄴ -ㅣㅁ', ''), 'ls -la'),
(Command(u'ㅔㅁㅅ촤', ''), 'patchk'), ])
(Command(u'ךד -ךש', ''), 'ls -la')])
def test_get_new_command(command, new_command):
assert switch_lang.get_new_command(command) == new_command

View File

@ -1,33 +0,0 @@
import pytest
from thefuck.rules.terraform_init import match, get_new_command
from thefuck.types import Command
@pytest.mark.parametrize('script, output', [
('terraform plan', 'Error: Initialization required. '
'Please see the error message above.'),
('terraform plan', 'This module is not yet installed. Run "terraform init" '
'to install all modules required by this configuration.'),
('terraform apply', 'Error: Initialization required. '
'Please see the error message above.'),
('terraform apply', 'This module is not yet installed. Run "terraform init" '
'to install all modules required by this configuration.')])
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=output))
@pytest.mark.parametrize('command, new_command', [
(Command('terraform plan', ''), 'terraform init && terraform plan'),
(Command('terraform apply', ''), 'terraform init && terraform apply'),
])
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.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

@ -1,173 +0,0 @@
from io import BytesIO
import pytest
from thefuck.rules.yum_invalid_operation import match, get_new_command, _get_operations
from thefuck.types import Command
yum_help_text = '''Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
Usage: yum [options] COMMAND
List of Commands:
check Check for problems in the rpmdb
check-update Check for available package updates
clean Remove cached data
deplist List a package's dependencies
distribution-synchronization Synchronize installed packages to the latest available versions
downgrade downgrade a package
erase Remove a package or packages from your system
fs Acts on the filesystem data of the host, mainly for removing docs/lanuages for minimal hosts.
fssnapshot Creates filesystem snapshots, or lists/deletes current snapshots.
groups Display, or use, the groups information
help Display a helpful usage message
history Display, or use, the transaction history
info Display details about a package or group of packages
install Install a package or packages on your system
langavailable Check available languages
langinfo List languages information
langinstall Install appropriate language packs for a language
langlist List installed languages
langremove Remove installed language packs for a language
list List a package or groups of packages
load-transaction load a saved transaction from filename
makecache Generate the metadata cache
provides Find what package provides the given value
reinstall reinstall a package
repo-pkgs Treat a repo. as a group of packages, so we can install/remove all of them
repolist Display the configured software repositories
search Search package details for the given string
shell Run an interactive yum shell
swap Simple way to swap packages, instead of using shell
update Update a package or packages on your system
update-minimal Works like upgrade, but goes to the 'newest' package match which fixes a problem that affects your system
updateinfo Acts on repository update information
upgrade Update packages taking obsoletes into account
version Display a version for the machine and/or available repos.
Options:
-h, --help show this help message and exit
-t, --tolerant be tolerant of errors
-C, --cacheonly run entirely from system cache, don't update cache
-c [config file], --config=[config file]
config file location
-R [minutes], --randomwait=[minutes]
maximum command wait time
-d [debug level], --debuglevel=[debug level]
debugging output level
--showduplicates show duplicates, in repos, in list/search commands
-e [error level], --errorlevel=[error level]
error output level
--rpmverbosity=[debug level name]
debugging output level for rpm
-q, --quiet quiet operation
-v, --verbose verbose operation
-y, --assumeyes answer yes for all questions
--assumeno answer no for all questions
--version show Yum version and exit
--installroot=[path] set install root
--enablerepo=[repo] enable one or more repositories (wildcards allowed)
--disablerepo=[repo] disable one or more repositories (wildcards allowed)
-x [package], --exclude=[package]
exclude package(s) by name or glob
--disableexcludes=[repo]
disable exclude from main, for a repo or for
everything
--disableincludes=[repo]
disable includepkgs for a repo or for everything
--obsoletes enable obsoletes processing during updates
--noplugins disable Yum plugins
--nogpgcheck disable gpg signature checking
--disableplugin=[plugin]
disable plugins by name
--enableplugin=[plugin]
enable plugins by name
--skip-broken skip packages with depsolving problems
--color=COLOR control whether color is used
--releasever=RELEASEVER
set value of $releasever in yum config and repo files
--downloadonly don't update, just download
--downloaddir=DLDIR specifies an alternate directory to store packages
--setopt=SETOPTS set arbitrary config and repo options
--bugfix Include bugfix relevant packages, in updates
--security Include security relevant packages, in updates
--advisory=ADVS, --advisories=ADVS
Include packages needed to fix the given advisory, in
updates
--bzs=BZS Include packages needed to fix the given BZ, in
updates
--cves=CVES Include packages needed to fix the given CVE, in
updates
--sec-severity=SEVS, --secseverity=SEVS
Include security relevant packages matching the
severity, in updates
Plugin Options:
--samearch-priorities
Priority-exclude packages based on name + arch
'''
yum_unsuccessful_search_text = '''Warning: No matches found for: {}
No matches found
'''
yum_successful_vim_search_text = '''================================================== N/S matched: vim ===================================================
protobuf-vim.x86_64 : Vim syntax highlighting for Google Protocol Buffers descriptions
vim-X11.x86_64 : The VIM version of the vi editor for the X Window System - GVim
vim-common.x86_64 : The common files needed by any version of the VIM editor
vim-enhanced.x86_64 : A version of the VIM editor which includes recent enhancements
vim-filesystem.x86_64 : VIM filesystem layout
vim-filesystem.noarch : VIM filesystem layout
vim-minimal.x86_64 : A minimal version of the VIM editor
Name and summary matches only, use "search all" for everything.
'''
yum_invalid_op_text = '''Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
No such command: {}. Please use /usr/bin/yum --help
'''
yum_operations = [
'check', 'check-update', 'clean', 'deplist', 'distribution-synchronization', 'downgrade', 'erase', 'fs',
'fssnapshot', 'groups', 'help', 'history', 'info', 'install', 'langavailable', 'langinfo', 'langinstall',
'langlist', 'langremove', 'list', 'load-transaction', 'makecache', 'provides', 'reinstall', 'repo-pkgs', 'repolist',
'search', 'shell', 'swap', 'update', 'update-minimal', 'updateinfo', 'upgrade', 'version', ]
@pytest.mark.parametrize('command', [
'saerch', 'uninstall',
])
def test_match(command):
assert match(Command('yum {}'.format(command), yum_invalid_op_text.format(command)))
@pytest.mark.parametrize('command, output', [
('vim', ''),
('yum', yum_help_text,),
('yum help', yum_help_text,),
('yum search asdf', yum_unsuccessful_search_text.format('asdf'),),
('yum search vim', yum_successful_vim_search_text)
])
def test_not_match(command, output):
assert not match(Command(command, output))
@pytest.fixture
def yum_help(mocker):
mock = mocker.patch('subprocess.Popen')
mock.return_value.stdout = BytesIO(bytes(yum_help_text.encode('utf-8')))
return mock
@pytest.mark.usefixtures('no_memoize', 'yum_help')
def test_get_operations():
assert _get_operations() == yum_operations
@pytest.mark.usefixtures('no_memoize', 'yum_help')
@pytest.mark.parametrize('script, output, result', [
('yum uninstall', yum_invalid_op_text.format('uninstall'), 'yum remove',),
('yum saerch asdf', yum_invalid_op_text.format('saerch'), 'yum search asdf',),
('yum hlep', yum_invalid_op_text.format('hlep'), 'yum help',),
])
def test_get_new_command(script, output, result):
assert get_new_command(Command(script, output))[0] == result

View File

@ -11,11 +11,6 @@ class TestBash(object):
def shell(self):
return Bash()
@pytest.fixture(autouse=True)
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.bash.Popen')
return mock
@pytest.fixture(autouse=True)
def shell_aliases(self):
os.environ['TF_SHELL_ALIASES'] = (
@ -79,12 +74,7 @@ class TestBash(object):
config_exists.return_value = False
assert not shell.how_to_configure().can_configure_automatically
def test_info(self, shell, Popen):
Popen.return_value.stdout.read.side_effect = [b'3.5.9']
def test_info(self, shell, mocker):
patch = mocker.patch('thefuck.shells.bash.Popen')
patch.return_value.stdout.read.side_effect = [b'3.5.9']
assert shell.info() == 'Bash 3.5.9'
def test_get_version_error(self, shell, Popen):
Popen.return_value.stdout.read.side_effect = OSError
with pytest.raises(OSError):
shell._get_version()
assert Popen.call_args[0][0] == ['bash', '-c', 'echo $BASH_VERSION']

View File

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.const import ARGUMENT_PLACEHOLDER
from thefuck.shells import Fish
@ -29,9 +28,8 @@ class TestFish(object):
('THEFUCK_OVERRIDDEN_ALIASES', '\ncut,\n\ngit,\tsed\r')])
def test_get_overridden_aliases(self, shell, os_environ, key, value):
os_environ[key] = value
overridden = shell._get_overridden_aliases()
assert set(overridden) == {'cd', 'cut', 'git', 'grep',
'ls', 'man', 'open', 'sed'}
assert shell._get_overridden_aliases() == {'cd', 'cut', 'git', 'grep',
'ls', 'man', 'open', 'sed'}
@pytest.mark.parametrize('before, after', [
('cd', 'cd'),
@ -83,15 +81,11 @@ class TestFish(object):
assert 'TF_SHELL=fish' 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')
assert ARGUMENT_PLACEHOLDER in shell.app_alias('fuck')
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')
@ -119,17 +113,6 @@ class TestFish(object):
config_exists.return_value = False
assert not shell.how_to_configure().can_configure_automatically
def test_get_version(self, shell, Popen):
Popen.return_value.stdout.read.side_effect = [b'fish, version 3.5.9\n']
assert shell._get_version() == '3.5.9'
assert Popen.call_args[0][0] == ['fish', '--version']
@pytest.mark.parametrize('side_effect, exception', [
([b'\n'], IndexError),
(OSError('file not found'), OSError),
])
def test_get_version_error(self, side_effect, exception, shell, Popen):
Popen.return_value.stdout.read.side_effect = side_effect
with pytest.raises(exception):
shell._get_version()
assert Popen.call_args[0][0] == ['fish', '--version']
def test_info(self, shell, Popen):
Popen.return_value.stdout.read.side_effect = [b'3.5.9']
assert shell.info() == 'Fish Shell 3.5.9'

View File

@ -43,14 +43,3 @@ class TestGeneric(object):
def test_how_to_configure(self, shell):
assert shell.how_to_configure() is None
@pytest.mark.parametrize('side_effect, expected_info, warn', [
([u'3.5.9'], u'Generic Shell 3.5.9', False),
([OSError], u'Generic Shell', True),
])
def test_info(self, side_effect, expected_info, warn, shell, mocker):
warn_mock = mocker.patch('thefuck.shells.generic.warn')
shell._get_version = mocker.Mock(side_effect=side_effect)
assert shell.info() == expected_info
assert warn_mock.called is warn
assert shell._get_version.called

View File

@ -10,11 +10,6 @@ class TestPowershell(object):
def shell(self):
return Powershell()
@pytest.fixture(autouse=True)
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.powershell.Popen')
return mock
def test_and_(self, shell):
assert shell.and_('ls', 'cd') == '(ls) -and (cd)'
@ -25,20 +20,3 @@ class TestPowershell(object):
def test_how_to_configure(self, shell):
assert not shell.how_to_configure().can_configure_automatically
@pytest.mark.parametrize('side_effect, expected_version, call_args', [
([b'''Major Minor Build Revision
----- ----- ----- --------
5 1 17763 316 \n'''], 'PowerShell 5.1.17763.316', ['powershell.exe']),
([IOError, b'PowerShell 6.1.2\n'], 'PowerShell 6.1.2', ['powershell.exe', 'pwsh'])])
def test_info(self, side_effect, expected_version, call_args, shell, Popen):
Popen.return_value.stdout.read.side_effect = side_effect
assert shell.info() == expected_version
assert Popen.call_count == len(call_args)
assert all([Popen.call_args_list[i][0][0][0] == call_arg for i, call_arg in enumerate(call_args)])
def test_get_version_error(self, shell, Popen):
Popen.return_value.stdout.read.side_effect = RuntimeError
with pytest.raises(RuntimeError):
shell._get_version()
assert Popen.call_args[0][0] == ['powershell.exe', '$PSVersionTable.PSVersion']

View File

@ -61,17 +61,3 @@ class TestTcsh(object):
config_exists):
config_exists.return_value = False
assert not shell.how_to_configure().can_configure_automatically
def test_info(self, shell, Popen):
Popen.return_value.stdout.read.side_effect = [
b'tcsh 6.20.00 (Astron) 2016-11-24 (unknown-unknown-bsd44) \n']
assert shell.info() == 'Tcsh 6.20.00'
assert Popen.call_args[0][0] == ['tcsh', '--version']
@pytest.mark.parametrize('side_effect, exception', [
([b'\n'], IndexError), (OSError, OSError)])
def test_get_version_error(self, side_effect, exception, shell, Popen):
Popen.return_value.stdout.read.side_effect = side_effect
with pytest.raises(exception):
shell._get_version()
assert Popen.call_args[0][0] == ['tcsh', '--version']

View File

@ -11,11 +11,6 @@ class TestZsh(object):
def shell(self):
return Zsh()
@pytest.fixture(autouse=True)
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.zsh.Popen')
return mock
@pytest.fixture(autouse=True)
def shell_aliases(self):
os.environ['TF_SHELL_ALIASES'] = (
@ -74,12 +69,7 @@ class TestZsh(object):
config_exists.return_value = False
assert not shell.how_to_configure().can_configure_automatically
def test_info(self, shell, Popen):
Popen.return_value.stdout.read.side_effect = [b'3.5.9']
def test_info(self, shell, mocker):
patch = mocker.patch('thefuck.shells.zsh.Popen')
patch.return_value.stdout.read.side_effect = [b'3.5.9']
assert shell.info() == 'ZSH 3.5.9'
def test_get_version_error(self, shell, Popen):
Popen.return_value.stdout.read.side_effect = OSError
with pytest.raises(OSError):
shell._get_version()
assert Popen.call_args[0][0] == ['zsh', '-c', 'echo $ZSH_VERSION']

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

@ -43,7 +43,7 @@ class TestSettingsFromFile(object):
assert settings.rules == const.DEFAULT_RULES + ['test']
@pytest.mark.usefixtures('load_source')
@pytest.mark.usefixture('load_source')
class TestSettingsFromEnv(object):
def test_from_env(self, os_environ, settings):
os_environ.update({'THEFUCK_RULES': 'bash:lisp',
@ -54,8 +54,7 @@ class TestSettingsFromEnv(object):
'THEFUCK_PRIORITY': 'bash=10:lisp=wrong:vim=15',
'THEFUCK_WAIT_SLOW_COMMAND': '999',
'THEFUCK_SLOW_COMMANDS': 'lein:react-native:./gradlew',
'THEFUCK_NUM_CLOSE_MATCHES': '359',
'THEFUCK_EXCLUDED_SEARCH_PATH_PREFIXES': '/media/:/mnt/'})
'THEFUCK_NUM_CLOSE_MATCHES': '359'})
settings.init()
assert settings.rules == ['bash', 'lisp']
assert settings.exclude_rules == ['git', 'vim']
@ -66,7 +65,6 @@ class TestSettingsFromEnv(object):
assert settings.wait_slow_command == 999
assert settings.slow_commands == ['lein', 'react-native', './gradlew']
assert settings.num_close_matches == 359
assert settings.excluded_search_path_prefixes == ['/media/', '/mnt/']
def test_from_env_with_DEFAULT(self, os_environ, settings):
os_environ.update({'THEFUCK_RULES': 'DEFAULT_RULES:bash:lisp'})

View File

@ -8,15 +8,14 @@ from thefuck.types import Command
from thefuck.corrector import get_corrected_commands, organize_commands
@pytest.fixture
def glob(mocker):
results = {}
mocker.patch('thefuck.system.Path.glob',
new_callable=lambda: lambda *_: results.pop('value', []))
return lambda value: results.update({'value': value})
class TestGetRules(object):
@pytest.fixture
def glob(self, mocker):
results = {}
mocker.patch('thefuck.system.Path.glob',
new_callable=lambda: lambda *_: results.pop('value', []))
return lambda value: results.update({'value': value})
@pytest.fixture(autouse=True)
def load_source(self, monkeypatch):
monkeypatch.setattr('thefuck.types.load_source',
@ -40,14 +39,6 @@ class TestGetRules(object):
self._compare_names(rules, loaded_rules)
def test_get_rules_rule_exception(mocker, glob):
load_source = mocker.patch('thefuck.types.load_source',
side_effect=ImportError("No module named foo..."))
glob([Path('git.py')])
assert not corrector.get_rules()
load_source.assert_called_once_with('git', 'git.py')
def test_get_corrected_commands(mocker):
command = Command('test', 'test')
rules = [Rule(match=lambda _: False),

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

@ -41,16 +41,10 @@ class TestCorrectedCommand(object):
settings.update(override_settings)
CorrectedCommand(script, None, 1000).run(Command(script, ''))
out, _ = capsys.readouterr()
assert out == printed
assert out[:-1] == printed
class TestRule(object):
def test_from_path_rule_exception(self, mocker):
load_source = mocker.patch('thefuck.types.load_source',
side_effect=ImportError("No module named foo..."))
assert Rule.from_path(Path('git.py')) is None
load_source.assert_called_once_with('git', 'git.py')
def test_from_path(self, mocker):
match = object()
get_new_command = object()
@ -66,22 +60,20 @@ class TestRule(object):
== Rule('bash', match, get_new_command, priority=900))
load_source.assert_called_once_with('bash', rule_path)
def test_from_path_excluded_rule(self, mocker, settings):
load_source = mocker.patch('thefuck.types.load_source')
settings.update(exclude_rules=['git'])
rule_path = os.path.join(os.sep, 'rules', 'git.py')
assert Rule.from_path(Path(rule_path)) is None
assert not load_source.called
@pytest.mark.parametrize('rules, rule, is_enabled', [
(const.DEFAULT_RULES, Rule('git', enabled_by_default=True), True),
(const.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),
(['git'], Rule('git', enabled_by_default=False), True)])
def test_is_enabled(self, settings, rules, rule, is_enabled):
settings.update(rules=rules)
@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),
([], [], 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),
(['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),
([], ['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):
settings.update(rules=rules,
exclude_rules=exclude_rules)
assert rule.is_enabled == is_enabled
def test_isnt_match(self):
@ -139,13 +131,10 @@ class TestCommand(object):
env=os_environ)
@pytest.mark.parametrize('script, result', [
([], None),
([''], 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

@ -2,7 +2,7 @@
import pytest
import warnings
from mock import Mock, call, patch
from mock import Mock, patch
from thefuck.utils import default_settings, \
memoize, get_closest, get_all_executables, replace_argument, \
get_all_matched_commands, is_app, for_app, cache, \
@ -76,38 +76,6 @@ def test_get_all_executables():
assert 'fuck' not in all_callables
@pytest.fixture
def os_environ_pathsep(monkeypatch, path, pathsep):
env = {'PATH': path}
monkeypatch.setattr('os.environ', env)
monkeypatch.setattr('os.pathsep', pathsep)
return env
@pytest.mark.usefixtures('no_memoize', 'os_environ_pathsep')
@pytest.mark.parametrize('path, pathsep', [
('/foo:/bar:/baz:/foo/bar', ':'),
(r'C:\\foo;C:\\bar;C:\\baz;C:\\foo\\bar', ';')])
def test_get_all_executables_pathsep(path, pathsep):
with patch('thefuck.utils.Path') as Path_mock:
get_all_executables()
Path_mock.assert_has_calls([call(p) for p in path.split(pathsep)], True)
@pytest.mark.usefixtures('no_memoize', 'os_environ_pathsep')
@pytest.mark.parametrize('path, pathsep, excluded', [
('/foo:/bar:/baz:/foo/bar:/mnt/foo', ':', '/mnt/foo'),
(r'C:\\foo;C:\\bar;C:\\baz;C:\\foo\\bar;Z:\\foo', ';', r'Z:\\foo')])
def test_get_all_executables_exclude_paths(path, pathsep, excluded, settings):
settings.init()
settings.excluded_search_path_prefixes = [excluded]
with patch('thefuck.utils.Path') as Path_mock:
get_all_executables()
path_list = path.split(pathsep)
assert call(path_list[-1]) not in Path_mock.mock_calls
assert all(call(p) in Path_mock.mock_calls for p in path_list[:-1])
@pytest.mark.parametrize('args, result', [
(('apt-get instol vim', 'instol', 'install'), 'apt-get install vim'),
(('git brnch', 'brnch', 'branch'), 'git branch')])
@ -146,8 +114,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 +123,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 +199,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
@ -243,12 +207,9 @@ class TestGetValidHistoryWithoutCurrent(object):
@pytest.fixture(autouse=True)
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
mock.return_value = ['le cat', 'fuck', 'ls cat',
'diff x', 'nocommand x', u'café ô']
return mock
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):

View File

@ -55,7 +55,7 @@ class Parser(object):
"""It's too dangerous to use `-y` and `-r` together."""
group = self._parser.add_mutually_exclusive_group()
group.add_argument(
'-y', '--yes', '--yeah', '--hard',
'-y', '--yes', '--yeah',
action='store_true',
help='execute fixed command without confirmation')
group.add_argument(

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):
@ -111,7 +101,7 @@ class Settings(dict):
elif attr in ('require_confirmation', 'no_colors', 'debug',
'alter_history', 'instant_mode'):
return val.lower() == 'true'
elif attr in ('slow_commands', 'excluded_search_path_prefixes'):
elif attr == 'slow_commands':
return val.split(':')
else:
return val

View File

@ -43,8 +43,7 @@ DEFAULT_SETTINGS = {'rules': DEFAULT_RULES,
'repeat': False,
'instant_mode': False,
'num_close_matches': 3,
'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'},
'excluded_search_path_prefixes': []}
'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'}}
ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
'THEFUCK_EXCLUDE_RULES': 'exclude_rules',
@ -59,8 +58,7 @@ ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
'THEFUCK_SLOW_COMMANDS': 'slow_commands',
'THEFUCK_REPEAT': 'repeat',
'THEFUCK_INSTANT_MODE': 'instant_mode',
'THEFUCK_NUM_CLOSE_MATCHES': 'num_close_matches',
'THEFUCK_EXCLUDED_SEARCH_PATH_PREFIXES': 'excluded_search_path_prefixes'}
'THEFUCK_NUM_CLOSE_MATCHES': 'num_close_matches'}
SETTINGS_HEADER = u"""# The Fuck settings file
#

View File

@ -15,7 +15,7 @@ def get_loaded_rules(rules_paths):
for path in rules_paths:
if path.name != '__init__.py':
rule = Rule.from_path(path)
if rule and rule.is_enabled:
if rule.is_enabled:
yield rule
@ -71,7 +71,7 @@ def organize_commands(corrected_commands):
without_duplicates,
key=lambda corrected_command: corrected_command.priority)
logs.debug(u'Corrected commands: {}'.format(
logs.debug('Corrected commands: '.format(
', '.join(u'{}'.format(cmd) for cmd in [first_command] + sorted_commands)))
for command in sorted_commands:

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:
@ -23,7 +23,6 @@ def _get_raw_command(known_args):
diff = SequenceMatcher(a=alias, b=command).ratio()
if diff < const.DIFF_WITH_ALIAS or command in executables:
return [command]
return []
def fix_command(known_args):

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,15 +20,12 @@ 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.
# Check https://github.com/nvbn/thefuck/issues/921 for reference
elif known_args.alias:
print_alias(known_args)
elif known_args.command or 'TF_HISTORY' in os.environ:
fix_command(known_args)
elif known_args.alias:
print_alias(known_args)
elif known_args.shell_logger:
try:
from .shell_logger import shell_logger # noqa: E402

View File

@ -106,7 +106,7 @@ def how_to_configure_alias(configuration_details):
if configuration_details.can_configure_automatically:
print(
u"Or run {bold}fuck{reset} a second time to configure"
u"Or run {bold}fuck{reset} second time for configuring"
u" it automatically.".format(
bold=color(colorama.Style.BRIGHT),
reset=color(colorama.Style.RESET_ALL)))

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,17 +53,13 @@ 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(
is_slow = shlex.split(expanded) in settings.slow_commands
with logs.debug_time(u'Call: {}; with env: {}; is slow: '.format(
script, env, is_slow)):
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

@ -6,8 +6,8 @@ from thefuck.utils import for_app, eager, replace_command
enabled_by_default = apt_available
@sudo_support
@for_app('apt', 'apt-get', 'apt-cache')
@sudo_support
def match(command):
return 'E: Invalid operation' in command.output
@ -34,8 +34,7 @@ def _parse_apt_get_and_cache_operations(help_text_lines):
return
yield line.split()[0]
elif line.startswith('Commands:') \
or line.startswith('Most used commands:'):
elif line.startswith('Commands:'):
is_commands_list = True
@ -54,10 +53,5 @@ def _get_operations(app):
@sudo_support
def get_new_command(command):
invalid_operation = command.output.split()[-1]
if invalid_operation == 'uninstall':
return [command.script.replace('uninstall', 'remove')]
else:
operations = _get_operations(command.script_parts[0])
return replace_command(command, invalid_operation, operations)
operations = _get_operations(command.script_parts[0])
return replace_command(command, invalid_operation, operations)

View File

@ -8,7 +8,7 @@ enabled_by_default = apt_available
@sudo_support
@for_app('apt')
def match(command):
return 'apt list --upgradable' in command.output
return "Run 'apt list --upgradable' to see them." in command.output
@sudo_support

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

@ -3,8 +3,8 @@ import re
from thefuck.utils import get_closest, replace_command
from thefuck.specific.brew import get_brew_path_prefix, brew_available
BREW_CMD_PATH = '/Homebrew/Library/Homebrew/cmd'
TAP_PATH = '/Homebrew/Library/Taps'
BREW_CMD_PATH = '/Library/Homebrew/cmd'
TAP_PATH = '/Library/Taps'
TAP_CMD_PATH = '/%s/%s/cmd'
enabled_by_default = brew_available
@ -62,7 +62,7 @@ def _brew_commands():
# Failback commands for testing (Based on Homebrew 0.9.5)
return ['info', 'home', 'options', 'install', 'uninstall',
'search', 'list', 'update', 'upgrade', 'pin', 'unpin',
'doctor', 'create', 'edit', 'cask']
'doctor', 'create', 'edit']
def match(command):

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

@ -1,21 +0,0 @@
# -*- encoding: utf-8 -*-
# Redirects cs to cd when there is a typo
# Due to the proximity of the keys - d and s - this seems like a common typo
# ~ > cs /etc/
# cs: command not found
# ~ > fuck
# cd /etc/ [enter/↑/↓/ctrl+c]
# /etc >
def match(command):
if command.script_parts[0] == 'cs':
return True
def get_new_command(command):
return 'cd' + ''.join(command.script[2:])
priority = 900

View File

@ -1,25 +0,0 @@
from thefuck.utils import for_app, which
@for_app("choco", "cinst")
def match(command):
return ((command.script.startswith('choco install') or 'cinst' in command.script_parts)
and 'Installing the following packages' in command.output)
def get_new_command(command):
# Find the argument that is the package name
for script_part in command.script_parts:
if (
script_part not in ["choco", "cinst", "install"]
# Need exact match (bc chocolatey is a package)
and not script_part.startswith('-')
# Leading hyphens are parameters; some packages contain them though
and '=' not in script_part and '/' not in script_part
# These are certainly parameters
):
return command.script.replace(script_part, script_part + ".install")
return []
enabled_by_default = bool(which("choco")) or bool(which("cinst"))

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

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