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

Compare commits

...

104 Commits
3.11 ... 3.12

Author SHA1 Message Date
Vladimir Iakovlev
03a828d586 Bump to 3.12 2017-01-09 18:17:50 +01:00
Vladimir Iakovlev
4a0d71c1c4 #N/A: Add ifconfig_device_not_found rule 2017-01-09 18:13:37 +01:00
Vladimir Iakovlev
a6f63c0568 #580: Use bashlex in generic shell 2017-01-09 17:50:23 +01:00
Vladimir Iakovlev
d1b9492085 Merge pull request #586 from duboviy/master
Add Python 3.6 support
2017-01-09 17:38:37 +01:00
Eugene Duboviy
993a661c60 Update .travis.yml 2017-01-08 17:13:22 +02:00
Eugene Duboviy
bc9121cb13 Update appveyor.yml 2017-01-08 17:08:38 +02:00
Eugene Duboviy
7db140c456 Update tox.ini 2017-01-08 17:06:45 +02:00
Vladimir Iakovlev
e313ff73a9 Merge pull request #582 from josephfrazier/git_stash_pop
Fix `git stash pop` with local changes
2016-12-22 14:02:05 +01:00
Joseph Frazier
8c62706db4 Fix git stash pop with local changes
When there are local changes to a file, and a git stash is popped that
contains other changes to that same file, git fails as follows:

    $ git stash pop
    error: Your local changes to the following files would be overwritten by merge:
            src/index.js
    Please commit your changes or stash them before you merge.
    Aborting
    $

This change adds a rule that corrects this problem as suggested [here]:

    $ git stash pop
    error: Your local changes to the following files would be overwritten by merge:
            src/index.js
    Please commit your changes or stash them before you merge.
    Aborting
    $ fuck
    git add . && git stash pop && git reset . [enter/↑/↓/ctrl+c]
    Auto-merging src/index.js
    On branch flow
    Changes to be committed:
      (use "git reset HEAD <file>..." to unstage)

            modified:   src/index.js

    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)

            modified:   src/index.js

    Dropped refs/stash@{0} (f94776d484c4278997ac6837a7b138b9b9cdead1)
    Unstaged changes after reset:
    M        src/index.js
    $

[here]: https://stackoverflow.com/questions/15126463/how-do-i-merge-local-modifications-with-a-git-stash-without-an-extra-commit/15126489#15126489
2016-12-11 12:44:04 -05:00
Vladimir Iakovlev
6baa7f650e Merge pull request #580 from josephfrazier/bash-command-substitution
bash: fix parsing of command substitution
2016-11-30 15:49:47 +01:00
Joseph Frazier
4ae32cf4ee bash: use generic shell's UTF-8 methods 2016-11-23 22:42:16 -05:00
Joseph Frazier
385746850e generic shell: extract UTF-8 encoding/decoding into methods 2016-11-23 07:53:22 -05:00
Joseph Frazier
4f87141f0c bash: fallback to generic parser if bashlex fails 2016-11-23 07:43:25 -05:00
Joseph Frazier
dbedcc7aa6 Test parsing bash arithmetic expressions 2016-11-23 07:36:58 -05:00
Vladimir Iakovlev
e0b5d47fa5 Merge pull request #578 from scorphus/fish-builtin-history
#577: Use builtin `history` in Fish function
2016-11-22 12:03:06 +01:00
Joseph Frazier
ca44ee0640 bash: use bashlex for split_command, not shlex 2016-11-18 14:43:07 -05:00
Joseph Frazier
892e8a8e65 Test parsing bash command substitution
This is to help address bad corrections like the following (note the
position of the -p flag):

    thefuck 'git log $(git ls-files thefuck | grep python_command) -p'
    git log $(git ls-files thefuck | grep -p python_command) [enter/↑/↓/ctrl+c]
2016-11-18 14:43:00 -05:00
Pablo Santiago Blum de Aguiar
a947259eef #577: Use builtin history in Fish function
Fix #577
2016-11-17 22:57:11 -02:00
Vladimir Iakovlev
785cb83ff3 Merge branch 'josephfrazier-git-flag-after-filename' 2016-11-08 23:53:50 +01:00
Vladimir Iakovlev
aec8fe3233 #570: Refine tests 2016-11-08 23:53:40 +01:00
Vladimir Iakovlev
c21dbd2be3 Merge branch 'git-flag-after-filename' of https://github.com/josephfrazier/thefuck into josephfrazier-git-flag-after-filename 2016-11-08 23:48:40 +01:00
Vladimir Iakovlev
6173913291 Merge pull request #572 from josephfrazier/ls_all
Suggest `ls -A` when `ls` has no output
2016-11-01 12:51:04 +01:00
Vladimir Iakovlev
6f0d1e287d #571: Don't put empty string in history in zsh 2016-10-31 18:52:48 +01:00
Joseph Frazier
756044e087 Suggest ls -A when ls has no output 2016-10-31 13:49:38 -04:00
Vladimir Iakovlev
ddd8788353 #571: always honor alter_history setting in zsh 2016-10-31 12:57:31 +01:00
Vladimir Iakovlev
76c0e7bc70 Merge pull request #571 from josephfrazier/bash-honor-alter-history
bash: always honor alter_history setting
2016-10-31 12:56:31 +01:00
Vladimir Iakovlev
4865bdd81f Merge pull request #569 from scorphus/rebase-skip
#N/A: Add `git_rebase_merge_dir` rule
2016-10-31 12:55:05 +01:00
Joseph Frazier
fa169c686c test_git_flag_after_filename.py: dedupe test commands 2016-10-31 00:22:24 -04:00
Joseph Frazier
9cae0bffff git_flag_after_filename: fix flake8 errors
These were found by creating a `.flake8` file containing:

    [flake8]
    ignore = E501,W503
    exclude = venv

then running:

    flake8 $(git diff master... --name-only)

See https://github.com/nvbn/thefuck/pull/563 for running `flake8` in CI
2016-10-31 00:22:24 -04:00
Joseph Frazier
b519d317f7 bash: always honor alter_history setting
This ensures that even if the command suggested and run by `thefuck`
fails, it will still be added to the history, allowing the user to tweak
it further (or run `fuck` again) if desired.

Note that the fish shell appears to already behave this way.
2016-10-30 23:17:52 -04:00
Joseph Frazier
5b420204c9 git: fix fatal: bad flag '...' after filename
For example:

    $ git log README.md -p
    fatal: bad flag '-p' used after filename
    $ fuck
    git log -p README.md [enter/↑/↓/ctrl+c]
    Aborted

    $ git log -p README.md --name-only
    fatal: bad flag '--name-only' used after filename
    $ fuck
    git log -p --name-only README.md [enter/↑/↓/ctrl+c]
    Aborted

    $ git log README.md -p CONTRIBUTING.md
    fatal: bad flag '-p' used after filename
    $ fuck
    git log -p README.md CONTRIBUTING.md [enter/↑/↓/ctrl+c]
2016-10-30 21:40:25 -04:00
Pablo Santiago Blum de Aguiar
07005b591a #N/A: Add git_rebase_merge_dir rule 2016-10-30 20:30:26 -02:00
Vladimir Iakovlev
cb99e42e02 Merge pull request #567 from scorphus/git-rm-local-modifications
#N/A: Add `git_rm_local_modifications` rule
2016-10-30 19:51:20 +01:00
Vladimir Iakovlev
51f77964c6 Merge pull request #568 from scorphus/osx-brew-install
#N/A: Do not fail if formula is already installed
2016-10-30 19:50:02 +01:00
Pablo Santiago Blum de Aguiar
30b1c44f91 #N/A: Do not fail if formula is already installed 2016-10-30 15:02:12 -02:00
Pablo Santiago Blum de Aguiar
af28f0334a #N/A: Add git_rm_local_modifications rule 2016-10-29 17:51:55 -02:00
Vladimir Iakovlev
5ee5439c1e #565: Refine git_push rule 2016-10-08 12:24:48 +02:00
Vladimir Iakovlev
cf006dac2c Merge branch 'master' into josephfrazier-git-push-u
# Conflicts:
#	thefuck/rules/git_push.py
2016-10-08 12:20:23 +02:00
Vladimir Iakovlev
5b535077bf #N/A: Stop changing Command inside rules 2016-10-08 12:18:33 +02:00
Vladimir Iakovlev
cf3acbfa2e Merge branch 'git-push-u' of https://github.com/josephfrazier/thefuck into josephfrazier-git-push-u 2016-10-07 10:40:02 +02:00
Vladimir Iakovlev
4d714994a3 Merge pull request #564 from josephfrazier/docker-python
Use official Python images for Docker tests
2016-10-07 10:38:49 +02:00
Vladimir Iakovlev
02f717a0e8 Merge pull request #562 from josephfrazier/man-help
Suggest `foo --help` when `man foo` shows no pages
2016-10-07 10:37:33 +02:00
Vladimir Iakovlev
8f4f2f03a7 Merge pull request #561 from josephfrazier/ag-literal
Suggest `ag -Q` when relevant
2016-10-07 10:35:34 +02:00
Joseph Frazier
feb36ede5c Fix suggestion for git push -u
This was broken by https://github.com/nvbn/thefuck/pull/559
2016-10-06 13:09:40 -04:00
Joseph Frazier
16a440cb9d test_zsh.py: use official python images, not ubuntu
This should help reduce build times.
2016-10-06 11:15:18 -04:00
Joseph Frazier
10b20574d1 test_tcsh.py: use official python images, not ubuntu
This should help reduce build times.
2016-10-06 11:12:38 -04:00
Joseph Frazier
91fceb401a test_fish.py: use official python images, not ubuntu
This should help reduce build times.
2016-10-06 11:09:19 -04:00
Joseph Frazier
4b79e23ba7 test_bash.py: use official python images, not ubuntu
This should help reduce build times.
2016-10-06 10:56:37 -04:00
Joseph Frazier
f915a6ed0c test_performance.py: use python:3 image, not ubuntu
This should help reduce build times.
2016-10-06 10:54:47 -04:00
Joseph Frazier
a964af7e95 ag_literal.py: use endswith() rather than in
https://github.com/nvbn/thefuck/pull/561#discussion_r81898499
2016-10-05 10:55:49 -04:00
Joseph Frazier
77fc021a6c Refactor tests/rules/test_ag_literal.py
https://github.com/nvbn/thefuck/pull/561#discussion_r81894710
2016-10-05 10:52:24 -04:00
Joseph Frazier
4822ceb87a ag_literal.py: remove unused import (Flake8 F401)
https://github.com/nvbn/thefuck/pull/561#discussion_r81892699
2016-10-05 10:34:17 -04:00
Joseph Frazier
b2947aba8d test_ag_literal.py: Add blank line (PEP 8 E302)
https://github.com/nvbn/thefuck/pull/561#discussion_r81892174
2016-10-05 10:32:14 -04:00
Joseph Frazier
d2e0a19aae Add missing semicolon to aws_cli entry in README 2016-10-03 14:22:17 -04:00
Joseph Frazier
0c84eefa55 Don't suggest man 2/3 foo if no man pages exist
Suggest `foo --help` instead. However, if there are man pages, suggest
`foo --help` after `man 2/3 foo`

This addresses the comment in the previous commit message:

> However, in cases where multiple sections have man pages for `foo`,
> running `man foo` could bring up the "wrong" section of man pages.
> `man read` is an example of this, but that should probably be handled in
> a way that still suggests `foo --help` first when there are *no* man
> pages for `foo` in any section.
2016-10-03 14:10:42 -04:00
Joseph Frazier
8bd6c5da67 For man foo, try foo --help before man 3 foo
`man` without a section searches all sections, so having `foo --help`
suggested first makes more sense than adding a specific section. See
https://github.com/nvbn/thefuck/pull/562#issuecomment-251142710

However, in cases where multiple sections have man pages for `foo`,
running `man foo` could bring up the "wrong" section of man pages.
`man read` is an example of this, but that should probably be handled in
a way that still suggests `foo --help` first when there are *no* man
pages for `foo` in any section.

Closes https://github.com/nvbn/thefuck/issues/546
2016-10-03 12:03:57 -04:00
Vladimir Iakovlev
ce6b82c92d #560: Fix code style 2016-10-03 13:07:30 +02:00
Joseph Frazier
5dbbb3b1ed Add ... --help to man suggestions
This is along the lines of what @waldyrious suggested in
https://github.com/nvbn/thefuck/issues/546, but it just adds a new
suggestion rather than replacing the other ones.
2016-10-03 03:57:53 -04:00
Joseph Frazier
db4b37910d Suggest ag -Q when relevant
This detects when `ag` suggests the `-Q` option, and adds it.
2016-10-03 00:33:40 -04:00
Joseph Frazier
2b88ea11ea Suggest git diff --no-index when relevant
This makes it easier to use `git diff` on untracked files.
2016-10-03 00:05:01 -04:00
Vladimir Iakovlev
db7dffdb44 Merge pull request #559 from josephfrazier/git-push-explicit-upstream
Fix suggestions for `git push -u origin`
2016-10-02 17:21:53 +02:00
Vladimir Iakovlev
92f3c8fb52 Merge pull request #557 from OJFord/patch-1
Add sudo rule for Aura
2016-10-02 17:21:04 +02:00
Vladimir Iakovlev
7c4f0d2e55 Merge pull request #551 from scorphus/git-bisect-usage
#N/A: Add `git_bisect_usage` rule
2016-10-02 17:20:43 +02:00
Vladimir Iakovlev
d05eb0a6dc #552: Fix code style 2016-10-02 17:19:33 +02:00
Vladimir Iakovlev
cf352fd788 Merge branch 'remove-trailing-cedilla' of https://github.com/wikiti/thefuck into wikiti-remove-trailing-cedilla 2016-10-02 17:18:24 +02:00
Vladimir Iakovlev
3c1cce6bd2 Merge branch 'brew-link' of https://github.com/josephfrazier/thefuck into josephfrazier-brew-link
# Conflicts:
#	README.md
2016-10-02 17:17:15 +02:00
Vladimir Iakovlev
5d3a727d1a Merge pull request #555 from josephfrazier/brew-uninstall-force
Suggest `brew uninstall --force` when relevant
2016-10-02 17:14:54 +02:00
Vladimir Iakovlev
ea87d55771 Merge pull request #554 from JordonPhillips/aws-rule
Add new aws cli rule
2016-10-02 17:14:14 +02:00
Joseph Frazier
aa6b18d0ce Fix suggestions for git push -u origin
Resolves https://github.com/nvbn/thefuck/issues/558
2016-09-30 16:13:50 -04:00
Joseph Frazier
934eeaf4fc Test that git push -u origin still works
This was broken by https://github.com/nvbn/thefuck/pull/538
2016-09-30 16:11:46 -04:00
Ollie Ford
3ad8d52a84 Add sudo rule for Aura
When installing from Arch User Repository without root:
    aura >>= You have to use `sudo` for that.

This commit adds the slightly more general, but unambiguous, "use `sudo`".

This commit closes #543.
2016-09-30 20:32:40 +01:00
Joseph Frazier
bb5c7c576f Suggest brew link --overwrite --dry-run when relevant
This makes it easier to see which files would be overwritten by
`brew link --overwrite`
2016-09-30 15:31:25 -04:00
Joseph Frazier
17c3935078 Test brew uninstall --force suggestion 2016-09-29 17:44:07 -04:00
Joseph Frazier
a734b94fec Suggest brew uninstall --force when relevant
Resolves https://github.com/nvbn/thefuck/issues/553
2016-09-29 17:26:20 -04:00
JordonPhillips
7bf405e9c3 Add aws cli rule
This rule corrects spelling mistakes for aws cli commands and
subcommands.
2016-09-29 14:22:08 -07:00
Daniel Herzog
c3bcdd7dee Update README 2016-09-29 21:43:02 +01:00
Daniel Herzog
ad53023860 Fix encoding for Python 2.7 2016-09-29 21:41:50 +01:00
Daniel
8938323229 Fix encoding 2016-09-29 11:06:56 +01:00
Daniel
92133f77d6 Add test file 2016-09-29 10:44:17 +01:00
Daniel
64eaf96eb8 Add rule 2016-09-29 10:34:41 +01:00
Pablo Santiago Blum de Aguiar
c9264aff10 #N/A: Add git_bisect_usage rule 2016-09-27 19:42:01 -03:00
Vladimir Iakovlev
9660ec7813 Merge branch 'juzim-git-pull-uncommitted-changes' 2016-09-20 00:28:51 +02:00
Vladimir Iakovlev
9ac47d8f78 #550: Use shell.and_ 2016-09-20 00:28:09 +02:00
Julian Zimmermann
6e2b82911f Removed linebreak 2016-09-19 13:07:48 +02:00
Julian Zimmermann
af9d34c299 Added rule that stashes changed files before pulling and pops them afterwards. 2016-09-19 12:52:23 +02:00
Vladimir Iakovlev
bcc11219e6 Merge pull request #545 from waldyrious/patch-1
readme: add -H flag to second sudo pip command
2016-09-06 22:38:17 +02:00
Waldir Pimenta
495a66088b readme: add -H flag to second sudo pip command 2016-09-06 17:01:09 +01:00
Vladimir Iakovlev
4fe64e3dfa #N/A: Match git_add only if pathspec exists 2016-08-23 13:03:49 +03:00
Vladimir Iakovlev
cae76eb55f Merge branch 'kthrift-fix/prevent-cwd-tilde-dir-creation' 2016-08-22 05:45:41 +03:00
Vladimir Iakovlev
afd2ed4e51 #540: Fix code style, add test 2016-08-22 05:45:27 +03:00
Vladimir Iakovlev
1a4d74d487 Merge branch 'fix/prevent-cwd-tilde-dir-creation' of https://github.com/kthrift/thefuck into kthrift-fix/prevent-cwd-tilde-dir-creation 2016-08-22 05:20:29 +03:00
Kyle Thrift
0bd3e85e08 fix: new config dirs created in $HOME/.config/thefuck instead of $CWD
fix: use correct path in warning message when XDG_CONFIG_HOME defined
2016-08-21 15:59:16 -04:00
Vladimir Iakovlev
faeeef7666 Merge pull request #539 from blahgeek/master
prevent infinity loop while detecting shell
2016-08-21 15:22:36 +08:00
Vladimir Iakovlev
4d65d6a1df Merge pull request #538 from lukechilds/git-push-with-args
Preserve args for git_push
2016-08-21 15:20:54 +08:00
BlahGeek
cfa51506fb prevent infinity loop while detecting shell
In OS X, Process(pid=0).parent() == Process(pid=0)
2016-08-20 12:00:03 +08:00
Luke Childs
5df350254e Check arguments are preserved in git_push 2016-08-19 22:29:43 +01:00
Luke Childs
612c393ec4 Check git_push matches without specifying a branch 2016-08-19 22:19:09 +01:00
Luke Childs
4d89b3499e Preserve args for git_push 2016-08-19 22:08:30 +01:00
Vladimir Iakovlev
070bb2ff28 #N/A: Show deprecation warning when ~/.thefuck used 2016-08-14 20:02:33 +03:00
Vladimir Iakovlev
71025dff17 #N/A: Monkeypatch old pathlib even on unix 2016-08-14 15:32:53 +03:00
Vladimir Iakovlev
621b455334 #N/A: Monkeypatch pathlib on windows 2016-08-14 15:15:03 +03:00
Vladimir Iakovlev
176924c18d #N/A: Move imports from pathlib/pathlib2 to utils 2016-08-14 15:01:00 +03:00
Vladimir Iakovlev
1f75fc1ea9 #N/A: Remove deprecated thefuck-alias entry point 2016-08-14 14:43:13 +03:00
Vladimir Iakovlev
46cb87615e #N/A: Remove old-style rules support 2016-08-14 14:37:32 +03:00
69 changed files with 975 additions and 214 deletions

View File

@@ -2,6 +2,9 @@ language: python
sudo: false
matrix:
include:
- os: linux
dist: trusty
python: "3.6"
- os: linux
dist: trusty
python: "3.5"
@@ -29,7 +32,7 @@ addons:
- python3-commandnotfound
before_install:
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew update ; fi
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then brew install $FORMULA; fi
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then if brew ls --versions $FORMULA; then brew upgrade $FORMULA || echo Python is up to date; else brew install $FORMULA; fi; fi
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then virtualenv venv -p $FORMULA; fi
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then source venv/bin/activate; fi
- pip install -U pip
@@ -41,7 +44,7 @@ install:
script:
- export COVERAGE_PYTHON_VERSION=python-${TRAVIS_PYTHON_VERSION:0:1}
- export RUN_TESTS="coverage run --source=thefuck,tests -m py.test -v --capture=sys tests"
- if [[ $TRAVIS_PYTHON_VERSION == 3.5 && $TRAVIS_OS_NAME != "osx" ]]; then $RUN_TESTS --enable-functional; fi
- if [[ $TRAVIS_PYTHON_VERSION != 3.5 || $TRAVIS_OS_NAME == "osx" ]]; then $RUN_TESTS; fi
- if [[ $TRAVIS_PYTHON_VERSION == 3.6 && $TRAVIS_OS_NAME != "osx" ]]; then $RUN_TESTS --enable-functional; fi
- if [[ $TRAVIS_PYTHON_VERSION != 3.6 || $TRAVIS_OS_NAME == "osx" ]]; then $RUN_TESTS; fi
after_success:
- coveralls

View File

@@ -103,7 +103,6 @@ brew install thefuck
```
On Ubuntu you can install `The Fuck` with:
```bash
sudo apt update
sudo apt install python3-dev python3-pip
@@ -135,7 +134,7 @@ To make them available immediately, run `source ~/.bashrc` (or your shell config
## Update
```bash
sudo pip install thefuck --upgrade
sudo -H pip install thefuck --upgrade
```
**Aliases changed in 1.34.**
@@ -145,6 +144,8 @@ sudo pip install thefuck --upgrade
The Fuck tries to match a rule for the previous command, creates a new command
using the matched rule and runs it. Rules enabled by default are as follows:
* `ag_literal` &ndash; adds `-Q` to `ag` when suggested;
* `aws_cli` &ndash; fixes misspelled commands like `aws dynamdb scan`;
* `cargo` &ndash; runs `cargo build` instead of `cargo`;
* `cargo_no_command` &ndash; fixes wrongs commands like `cargo buid`;
* `cd_correction` &ndash; spellchecks and correct failed cd commands;
@@ -164,22 +165,29 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `fix_alt_space` &ndash; replaces Alt+Space with Space character;
* `fix_file` &ndash; opens a file with an error in your `$EDITOR`;
* `git_add` &ndash; fixes *"pathspec 'foo' did not match any file(s) known to git."*;
* `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_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_checkout` &ndash; fixes branch name or creates new branch;
* `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_not_command` &ndash; fixes wrong git commands like `git brnch`;
* `git_pull` &ndash; sets upstream before executing previous `git pull`;
* `git_pull_clone` &ndash; clones instead of pulling when the repo does not exist;
* `git_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_pull` &ndash; runs `git pull` when `push` was rejected;
* `git_rebase_no_changes` &ndash; runs `git rebase --skip` instead of `git rebase --continue` when there are no changes;
* `git_rm_local_modifications` &ndash; adds `-f` or `--cached` when you try to `rm` a locally modified file;
* `git_rm_recursive` &ndash; adds `-r` when you try to `rm` a directory;
* `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 nonexistant remote;
* `git_stash` &ndash; stashes you local modifications before rebasing or switching branch;
* `git_stash_pop` &ndash; adds your local modifications before popping stash, then resets;
* `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;
* `gradle_no_task` &ndash; fixes not found or ambiguous `gradle` task;
@@ -191,11 +199,13 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `has_exists_script` &ndash; prepends `./` when script/binary exists;
* `heroku_not_command` &ndash; fixes wrong `heroku` commands like `heroku log`;
* `history` &ndash; tries to replace command with most similar command from history;
* `ifconfig_device_not_found` &ndash; fixes wrong device names like `wlan0` to `wlp2s0`;
* `java` &ndash; removes `.java` extension when running Java programs;
* `javac` &ndash; appends missing `.java` when compiling Java files;
* `lein_not_task` &ndash; fixes wrong `lein` tasks like `lein rpl`;
* `ln_no_hard_link` &ndash; catches hard link creation on directories, suggest symbolic link;
* `ln_s_order` &ndash; fixes `ln -s` arguments order;
* `ls_all` &ndash; adds `-A` to `ls` when output is empty;
* `ls_lah` &ndash; adds `-lah` to `ls`;
* `man` &ndash; changes manual section;
* `man_no_space` &ndash; fixes man commands without spaces, for example `mandiff`;
@@ -215,6 +225,7 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `python_execute` &ndash; appends missing `.py` when executing Python files;
* `quotation_marks` &ndash; fixes uneven usage of `'` and `"` when containing args';
* `react_native_command_unrecognized` &ndash; fixes unrecognized `react-native` commands;
* `remove_trailing_cedilla` &ndash; remove trailling cedillas `ç`, a common typo for european keyboard layouts;
* `rm_dir` &ndash; adds `-rf` when you trying to remove directory;
* `sed_unterminated_s` &ndash; adds missing '/' to `sed`'s `s` commands;
* `sl_ls` &ndash; changes `sl` to `ls`;
@@ -238,6 +249,8 @@ Enabled by default only on specific platforms:
* `apt_get_search` &ndash; changes trying to search using `apt-get` with searching using `apt-cache`;
* `apt_invalid_operation` &ndash; fixes invalid `apt` and `apt-get` calls, like `apt-get isntall vim`;
* `brew_install` &ndash; fixes formula name for `brew install`;
* `brew_link` &ndash; adds `--overwrite --dry-run` if linking fails;
* `brew_uninstall` &ndash; adds `--force` to `brew uninstall` if multiple versions were installed;
* `brew_unknown_command` &ndash; fixes wrong brew commands, for example `brew docto/brew doctor`;
* `brew_update_formula` &ndash; turns `brew update <formula>` into `brew upgrade <formula>`;
* `brew_upgrade` &ndash; appends `--all` to `brew upgrade` as per Homebrew's new behaviour;
@@ -266,7 +279,9 @@ side_effect(old_command: Command, fixed_command: str) -> None
```
and optional `enabled_by_default`, `requires_output` and `priority` variables.
`Command` has three attributes: `script`, `stdout` and `stderr`.
`Command` has three attributes: `script`, `stdout`, `stderr` and `script_parts`.
Rule shouldn't change `Command`.
*Rules api changed in 3.0:* For accessing settings in rule you need to import it with `from thefuck.conf import settings`.
`settings` is a special object filled with `~/.config/thefuck/settings.py` and values from env ([see more below](#settings)).

View File

@@ -6,6 +6,7 @@ environment:
- PYTHON: "C:/Python33"
- PYTHON: "C:/Python34"
- PYTHON: "C:/Python35"
- PYTHON: "C:/Python36"
init:
- "ECHO %PYTHON%"

View File

@@ -29,9 +29,9 @@ elif (3, 0) < version < (3, 3):
' ({}.{} detected).'.format(*version))
sys.exit(-1)
VERSION = '3.11'
VERSION = '3.12'
install_requires = ['psutil', 'colorama', 'six', 'decorator']
install_requires = ['psutil', 'colorama', 'six', 'decorator', 'bashlex']
extras_require = {':python_version<"3.4"': ['pathlib2'],
":sys_platform=='win32'": ['win_unicode_console']}
@@ -51,5 +51,4 @@ setup(name='thefuck',
extras_require=extras_require,
entry_points={'console_scripts': [
'thefuck = thefuck.main:main',
'thefuck-alias = thefuck.main:print_alias',
'fuck = thefuck.main:how_to_configure_alias']})

View File

@@ -1,10 +1,7 @@
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
import pytest
from thefuck import shells
from thefuck import conf, const
from thefuck.system import Path
shells.shell = shells.Generic()

View File

@@ -3,18 +3,11 @@ from tests.functional.plots import with_confirmation, without_confirmation, \
refuse_with_confirmation, history_changed, history_not_changed, \
select_command_with_arrows, how_to_configure
containers = ((u'thefuck/ubuntu-python3-bash',
u'''FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -yy python3 python3-pip python3-dev git
RUN pip3 install -U setuptools
RUN ln -s /usr/bin/pip3 /usr/bin/pip''',
containers = ((u'thefuck/python3-bash',
u'FROM python:3',
u'bash'),
(u'thefuck/ubuntu-python2-bash',
u'''FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -yy python python-pip python-dev git
RUN pip2 install -U pip setuptools''',
(u'thefuck/python2-bash',
u'FROM python:2',
u'bash'))

View File

@@ -2,19 +2,20 @@ import pytest
from tests.functional.plots import with_confirmation, without_confirmation, \
refuse_with_confirmation, select_command_with_arrows
containers = (('thefuck/ubuntu-python3-fish',
u'''FROM ubuntu:latest
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 python3 python3-pip python3-dev fish git
RUN pip3 install -U setuptools
RUN ln -s /usr/bin/pip3 /usr/bin/pip
RUN apt-get install -yy fish''',
u'fish'),
('thefuck/ubuntu-python2-fish',
u'''FROM ubuntu:latest
('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 python python-pip python-dev git
RUN pip2 install -U pip setuptools
RUN apt-get install -yy fish''',
u'fish'))

View File

@@ -2,11 +2,7 @@ import pytest
import time
dockerfile = u'''
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -yy python3 python3-pip python3-dev git
RUN pip3 install -U setuptools
RUN ln -s /usr/bin/pip3 /usr/bin/pip
FROM python:3
RUN adduser --disabled-password --gecos '' test
ENV SEED "{seed}"
WORKDIR /src
@@ -42,7 +38,7 @@ def plot(proc, TIMEOUT):
@pytest.mark.functional
@pytest.mark.benchmark(min_rounds=10)
def test_performance(spawnu, TIMEOUT, benchmark):
proc = spawnu(u'thefuck/ubuntu-python3-bash-performance',
proc = spawnu(u'thefuck/python3-bash-performance',
dockerfile, u'bash')
proc.sendline(u'pip install /src')
proc.sendline(u'su test')

View File

@@ -2,19 +2,14 @@ import pytest
from tests.functional.plots import with_confirmation, without_confirmation, \
refuse_with_confirmation, select_command_with_arrows
containers = (('thefuck/ubuntu-python3-tcsh',
u'''FROM ubuntu:latest
containers = (('thefuck/python3-tcsh',
u'''FROM python:3
RUN apt-get update
RUN apt-get install -yy python3 python3-pip python3-dev git
RUN pip3 install -U setuptools
RUN ln -s /usr/bin/pip3 /usr/bin/pip
RUN apt-get install -yy tcsh''',
u'tcsh'),
('thefuck/ubuntu-python2-tcsh',
u'''FROM ubuntu:latest
('thefuck/python2-tcsh',
u'''FROM python:2
RUN apt-get update
RUN apt-get install -yy python python-pip python-dev git
RUN pip2 install -U pip setuptools
RUN apt-get install -yy tcsh''',
u'tcsh'))

View File

@@ -3,19 +3,14 @@ from tests.functional.plots import with_confirmation, without_confirmation, \
refuse_with_confirmation, history_changed, history_not_changed, \
select_command_with_arrows, how_to_configure
containers = (('thefuck/ubuntu-python3-zsh',
u'''FROM ubuntu:latest
containers = (('thefuck/python3-zsh',
u'''FROM python:3
RUN apt-get update
RUN apt-get install -yy python3 python3-pip python3-dev git
RUN pip3 install -U setuptools
RUN ln -s /usr/bin/pip3 /usr/bin/pip
RUN apt-get install -yy zsh''',
u'zsh'),
('thefuck/ubuntu-python2-zsh',
u'''FROM ubuntu:latest
('thefuck/python2-zsh',
u'''FROM python:2
RUN apt-get update
RUN apt-get install -yy python python-pip python-dev git
RUN pip2 install -U pip setuptools
RUN apt-get install -yy zsh''',
u'zsh'))

View File

@@ -0,0 +1,25 @@
import pytest
from thefuck.rules.ag_literal import get_new_command, match
from tests.utils import Command
@pytest.fixture
def stderr():
return ('ERR: Bad regex! pcre_compile() failed at position 1: missing )\n'
'If you meant to search for a literal string, run ag with -Q\n')
@pytest.mark.parametrize('script', ['ag \('])
def test_match(script, stderr):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script', ['ag foo'])
def test_not_match(script):
assert not match(Command(script=script))
@pytest.mark.parametrize('script, new_cmd', [
('ag \(', 'ag -Q \(')])
def test_get_new_command(script, new_cmd, stderr):
assert get_new_command((Command(script=script, stderr=stderr))) == new_cmd

101
tests/rules/test_aws_cli.py Normal file
View File

@@ -0,0 +1,101 @@
import pytest
from thefuck.rules.aws_cli import match, get_new_command
from tests.utils import Command
no_suggestions = '''\
usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:
aws help
aws <command> help
aws <command> <subcommand> help
aws: error: argument command: Invalid choice, valid choices are:
dynamodb | dynamodbstreams
ec2 | ecr
'''
misspelled_command = '''\
usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:
aws help
aws <command> help
aws <command> <subcommand> help
aws: error: argument command: Invalid choice, valid choices are:
dynamodb | dynamodbstreams
ec2 | ecr
Invalid choice: 'dynamdb', maybe you meant:
* dynamodb
'''
misspelled_subcommand = '''\
usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:
aws help
aws <command> help
aws <command> <subcommand> help
aws: error: argument operation: Invalid choice, valid choices are:
query | scan
update-item | update-table
Invalid choice: 'scn', maybe you meant:
* scan
'''
misspelled_subcommand_with_multiple_options = '''\
usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:
aws help
aws <command> help
aws <command> <subcommand> help
aws: error: argument operation: Invalid choice, valid choices are:
describe-table | get-item
list-tables | put-item
Invalid choice: 't-item', maybe you meant:
* put-item
* get-item
'''
@pytest.mark.parametrize('command', [
Command('aws dynamdb scan', stderr=misspelled_command),
Command('aws dynamodb scn', stderr=misspelled_subcommand),
Command('aws dynamodb t-item',
stderr=misspelled_subcommand_with_multiple_options)])
def test_match(command):
assert match(command)
def test_not_match():
assert not match(Command('aws dynamodb invalid', stderr=no_suggestions))
@pytest.mark.parametrize('command, result', [
(Command('aws dynamdb scan', stderr=misspelled_command),
['aws dynamodb scan']),
(Command('aws dynamodb scn', stderr=misspelled_subcommand),
['aws dynamodb scan']),
(Command('aws dynamodb t-item',
stderr=misspelled_subcommand_with_multiple_options),
['aws dynamodb put-item', 'aws dynamodb get-item'])])
def test_get_new_command(command, result):
assert get_new_command(command) == result

View File

@@ -0,0 +1,38 @@
import pytest
from tests.utils import Command
from thefuck.rules.brew_link import get_new_command, match
@pytest.fixture
def stderr():
return ("Error: Could not symlink bin/gcp\n"
"Target /usr/local/bin/gcp\n"
"already exists. You may want to remove it:\n"
"rm '/usr/local/bin/gcp'\n"
"\n"
"To force the link and overwrite all conflicting files:\n"
"brew link --overwrite coreutils\n"
"\n"
"To list all files that would be deleted:\n"
"brew link --overwrite --dry-run coreutils\n")
@pytest.fixture
def new_command(formula):
return 'brew link --overwrite --dry-run {}'.format(formula)
@pytest.mark.parametrize('script', ['brew link coreutils', 'brew ln coreutils'])
def test_match(stderr, script):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script', ['brew link coreutils'])
def test_not_match(script):
stderr=''
assert not match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script, formula, ', [('brew link coreutils', 'coreutils')])
def test_get_new_command(stderr, new_command, script, formula):
assert get_new_command(Command(script=script, stderr=stderr)) == new_command

View File

@@ -0,0 +1,31 @@
import pytest
from tests.utils import Command
from thefuck.rules.brew_uninstall import get_new_command, match
@pytest.fixture
def stdout():
return ("Uninstalling /usr/local/Cellar/tbb/4.4-20160916... (118 files, 1.9M)\n"
"tbb 4.4-20160526, 4.4-20160722 are still installed.\n"
"Remove all versions with `brew uninstall --force tbb`.\n")
@pytest.fixture
def new_command(formula):
return 'brew uninstall --force {}'.format(formula)
@pytest.mark.parametrize('script', ['brew uninstall tbb', 'brew rm tbb', 'brew remove tbb'])
def test_match(stdout, script):
assert match(Command(script=script, stdout=stdout))
@pytest.mark.parametrize('script', ['brew remove gnuplot'])
def test_not_match(script):
stdout='Uninstalling /usr/local/Cellar/gnuplot/5.0.4_1... (44 files, 2.3M)\n'
assert not match(Command(script=script, stdout=stdout))
@pytest.mark.parametrize('script, formula, ', [('brew uninstall tbb', 'tbb')])
def test_get_new_command(stdout, new_command, script, formula):
assert get_new_command(Command(script=script, stdout=stdout)) == new_command

View File

@@ -3,6 +3,12 @@ from thefuck.rules.git_add import match, get_new_command
from tests.utils import Command
@pytest.fixture(autouse=True)
def path_exists(mocker):
return mocker.patch('thefuck.rules.git_add.Path.exists',
return_value=True)
@pytest.fixture
def stderr(target):
return ("error: pathspec '{}' did not match any "
@@ -16,10 +22,13 @@ def test_match(stderr, script, target):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script', [
'git submodule update known', 'git commit known'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('script, target, exists', [
('git submodule update known', '', True),
('git commit known', '', True),
('git submodule update known', stderr, False)])
def test_not_match(path_exists, stderr, script, target, exists):
path_exists.return_value = exists
assert not match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script, target, new_command', [

View File

@@ -0,0 +1,30 @@
import pytest
from tests.utils import Command
from thefuck.rules.git_bisect_usage import match, get_new_command
@pytest.fixture
def stderr():
return ("usage: git bisect [help|start|bad|good|new|old"
"|terms|skip|next|reset|visualize|replay|log|run]")
@pytest.mark.parametrize('script', [
'git bisect strt', 'git bisect rset', 'git bisect goood'])
def test_match(stderr, script):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script', [
'git bisect', 'git bisect start', 'git bisect good'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('script, new_cmd, ', [
('git bisect goood', ['good', 'old', 'log']),
('git bisect strt', ['start', 'terms', 'reset']),
('git bisect rset', ['reset', 'next', 'start'])])
def test_get_new_command(stderr, script, new_cmd):
new_cmd = ['git bisect %s' % cmd for cmd in new_cmd]
assert get_new_command(Command(script=script, stderr=stderr)) == new_cmd

View File

@@ -0,0 +1,23 @@
import pytest
from thefuck.rules.git_diff_no_index import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='git diff foo bar')])
def test_match(command):
assert match(command)
@pytest.mark.parametrize('command', [
Command(script='git diff --no-index foo bar'),
Command(script='git diff foo'),
Command(script='git diff foo bar baz')])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('command, new_command', [
(Command('git diff foo bar'), 'git diff --no-index foo bar')])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -0,0 +1,31 @@
import pytest
from thefuck.rules.git_flag_after_filename import match, get_new_command
from tests.utils import Command
command1 = Command('git log README.md -p',
stderr="fatal: bad flag '-p' used after filename")
command2 = Command('git log README.md -p CONTRIBUTING.md',
stderr="fatal: bad flag '-p' used after filename")
command3 = Command('git log -p README.md --name-only',
stderr="fatal: bad flag '--name-only' used after filename")
@pytest.mark.parametrize('command', [
command1, command2, command3])
def test_match(command):
assert match(command)
@pytest.mark.parametrize('command', [
Command('git log README.md'),
Command('git log -p README.md')])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('command, result', [
(command1, "git log -p README.md"),
(command2, "git log -p README.md CONTRIBUTING.md"),
(command3, "git log -p --name-only README.md")])
def test_get_new_command(command, result):
assert get_new_command(command) == result

View File

@@ -0,0 +1,19 @@
import pytest
from thefuck.rules.git_pull_uncommitted_changes import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stderr():
return '''error: Cannot pull with rebase: You have unstaged changes.'''
def test_match(stderr):
assert match(Command('git pull', stderr=stderr))
assert not match(Command('git pull'))
assert not match(Command('ls', stderr=stderr))
def test_get_new_command(stderr):
assert get_new_command(Command('git pull', stderr=stderr)) \
== "git stash && git pull && git stash pop"

View File

@@ -0,0 +1,19 @@
import pytest
from thefuck.rules.git_pull_uncommitted_changes import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stderr():
return '''error: Cannot pull with rebase: Your index contains uncommitted changes.'''
def test_match(stderr):
assert match(Command('git pull', stderr=stderr))
assert not match(Command('git pull'))
assert not match(Command('ls', stderr=stderr))
def test_get_new_command(stderr):
assert get_new_command(Command('git pull', stderr=stderr)) \
== "git stash && git pull && git stash pop"

View File

@@ -14,6 +14,7 @@ To push the current branch and set the remote as upstream, use
def test_match(stderr):
assert match(Command('git push', stderr=stderr))
assert match(Command('git push master', stderr=stderr))
assert not match(Command('git push master'))
assert not match(Command('ls', stderr=stderr))
@@ -22,3 +23,11 @@ def test_match(stderr):
def test_get_new_command(stderr):
assert get_new_command(Command('git push', stderr=stderr))\
== "git push --set-upstream origin master"
assert get_new_command(Command('git push -u', stderr=stderr))\
== "git push --set-upstream origin master"
assert get_new_command(Command('git push -u origin', stderr=stderr))\
== "git push --set-upstream origin master"
assert get_new_command(Command('git push --set-upstream origin', stderr=stderr))\
== "git push --set-upstream origin master"
assert get_new_command(Command('git push --quiet', stderr=stderr))\
== "git push --set-upstream origin master --quiet"

View File

@@ -0,0 +1,40 @@
import pytest
from thefuck.rules.git_rebase_merge_dir import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stderr():
return ('\n\nIt seems that there is already a rebase-merge directory, and\n'
'I wonder if you are in the middle of another rebase. If that is the\n'
'case, please try\n'
'\tgit rebase (--continue | --abort | --skip)\n'
'If that is not the case, please\n'
'\trm -fr "/foo/bar/baz/egg/.git/rebase-merge"\n'
'and run me again. I am stopping in case you still have something\n'
'valuable there.\n')
@pytest.mark.parametrize('script', [
('git rebase master'), ('git rebase -skip'), ('git rebase')])
def test_match(stderr, script):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script', ['git rebase master', 'git rebase -abort'])
def test_not_match(script):
assert not match(Command(script=script))
@pytest.mark.parametrize('script, result', [
('git rebase master', [
'git rebase --abort', 'git rebase --skip', 'git rebase --continue',
'rm -fr "/foo/bar/baz/egg/.git/rebase-merge"']),
('git rebase -skip', [
'git rebase --skip', 'git rebase --abort', 'git rebase --continue',
'rm -fr "/foo/bar/baz/egg/.git/rebase-merge"']),
('git rebase', [
'git rebase --skip', 'git rebase --abort', 'git rebase --continue',
'rm -fr "/foo/bar/baz/egg/.git/rebase-merge"'])])
def test_get_new_command(stderr, script, result):
assert get_new_command(Command(script=script, stderr=stderr)) == result

View File

@@ -0,0 +1,28 @@
import pytest
from thefuck.rules.git_rm_local_modifications import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stderr(target):
return ('error: the following file has local modifications:\n {}\n(use '
'--cached to keep the file, or -f to force removal)').format(target)
@pytest.mark.parametrize('script, target', [
('git rm foo', 'foo'),
('git rm foo bar', 'bar')])
def test_match(stderr, script, target):
assert match(Command(script=script, stderr=stderr))
@pytest.mark.parametrize('script', ['git rm foo', 'git rm foo bar', 'git rm'])
def test_not_match(script):
assert not match(Command(script=script, stderr=''))
@pytest.mark.parametrize('script, target, new_command', [
('git rm foo', 'foo', ['git rm --cached foo', 'git rm -f foo']),
('git rm foo bar', 'bar', ['git rm --cached foo bar', 'git rm -f foo bar'])])
def test_get_new_command(stderr, script, target, new_command):
assert get_new_command(Command(script=script, stderr=stderr)) == new_command

View File

@@ -0,0 +1,18 @@
import pytest
from thefuck.rules.git_stash_pop import match, get_new_command
from tests.utils import Command
@pytest.fixture
def stderr():
return '''error: Your local changes to the following files would be overwritten by merge:'''
def test_match(stderr):
assert match(Command('git stash pop', stderr=stderr))
assert not match(Command('git stash'))
def test_get_new_command(stderr):
assert get_new_command(Command('git stash pop', stderr=stderr)) \
== "git add . && git stash pop && git reset ."

View File

@@ -0,0 +1,53 @@
import pytest
from six import BytesIO
from thefuck.rules.ifconfig_device_not_found import match, get_new_command
from tests.utils import Command
stderr = '{}: error fetching interface information: Device not found'
stdout = b'''
wlp2s0 Link encap:Ethernet HWaddr 5c:51:4f:7c:58:5d
inet addr:192.168.0.103 Bcast:192.168.0.255 Mask:255.255.255.0
inet6 addr: fe80::be23:69b9:96d2:6d39/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:23581604 errors:0 dropped:0 overruns:0 frame:0
TX packets:17017655 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:16148429061 (16.1 GB) TX bytes:7067533695 (7.0 GB)
'''
@pytest.fixture(autouse=True)
def ifconfig(mocker):
mock = mocker.patch(
'thefuck.rules.ifconfig_device_not_found.subprocess.Popen')
mock.return_value.stdout = BytesIO(stdout)
return mock
@pytest.mark.parametrize('script, stderr', [
('ifconfig wlan0', stderr.format('wlan0')),
('ifconfig -s eth0', stderr.format('eth0')),
])
def test_match(script, stderr):
assert match(Command(script, stderr=stderr))
@pytest.mark.parametrize('script, stderr', [
('config wlan0',
'wlan0: error fetching interface information: Device not found'),
('ifconfig eth0', ''),
])
def test_not_match(script, stderr):
assert not match(Command(script, stderr=stderr))
@pytest.mark.parametrize('script, result', [
('ifconfig wlan0', ['ifconfig wlp2s0']),
('ifconfig -s wlan0', ['ifconfig -s wlp2s0']),
])
def test_get_new_comman(script, result):
new_command = get_new_command(
Command(script, stderr=stderr.format('wlan0')))
assert new_command == result

View File

@@ -0,0 +1,12 @@
from thefuck.rules.ls_all import match, get_new_command
from tests.utils import Command
def test_match():
assert match(Command(script='ls'))
assert not match(Command(script='ls', stdout='file.py\n'))
def test_get_new_command():
assert get_new_command(Command(script='ls empty_dir')) == 'ls -A empty_dir'
assert get_new_command(Command(script='ls')) == 'ls -A'

View File

@@ -23,7 +23,8 @@ def test_not_match(command):
@pytest.mark.parametrize('command, new_command', [
(Command('man read'), ['man 3 read', 'man 2 read']),
(Command('man read'), ['man 3 read', 'man 2 read', 'read --help']),
(Command('man missing', stderr="No manual entry for missing\n"), ['missing --help']),
(Command('man 2 read'), 'man 3 read'),
(Command('man 3 read'), 'man 2 read'),
(Command('man -s2 read'), 'man -s3 read'),

View File

@@ -0,0 +1,17 @@
import pytest
from thefuck.rules.remove_trailing_cedilla import match, get_new_command, CEDILLA
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='wrong' + CEDILLA),
Command(script='wrong with args' + CEDILLA)])
def test_match(command):
assert match(command)
@pytest.mark.parametrize('command, new_command', [
(Command('wrong' + CEDILLA), 'wrong'),
(Command('wrong with args' + CEDILLA), 'wrong with args')])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -56,3 +56,13 @@ class TestBash(object):
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
assert list(shell.get_history()) == ['ls', 'rm']
def test_split_command(self, shell):
command = 'git log $(git ls-files thefuck | grep python_command) -p'
command_parts = ['git', 'log', '$(git ls-files thefuck | grep python_command)', '-p']
assert shell.split_command(command) == command_parts
# bashlex doesn't support parsing arithmetic expressions, so make sure
# shlex is used a fallback
# See https://github.com/idank/bashlex#limitations
assert shell.split_command('$((1 + 2))') == ['$((1', '+', '2))']

View File

@@ -76,11 +76,11 @@ class TestFish(object):
def test_app_alias_alter_history(self, settings, shell):
settings.alter_history = True
assert 'history --delete' in shell.app_alias('FUCK')
assert 'history --merge' in shell.app_alias('FUCK')
assert 'builtin history delete' in shell.app_alias('FUCK')
assert 'builtin history merge' in shell.app_alias('FUCK')
settings.alter_history = False
assert 'history --delete' not in shell.app_alias('FUCK')
assert 'history --merge' not in shell.app_alias('FUCK')
assert 'builtin history delete' not in shell.app_alias('FUCK')
assert 'builtin history merge' not in shell.app_alias('FUCK')
def test_get_history(self, history_lines, shell):
history_lines(['- cmd: ls', ' when: 1432613911',

View File

@@ -1,5 +1,6 @@
import pytest
import six
import os
from mock import Mock
from thefuck import const
@@ -101,3 +102,22 @@ class TestInitializeSettingsFile(object):
for setting in const.DEFAULT_SETTINGS.items():
assert '# {} = {}\n'.format(*setting) in settings_file_contents
settings_file.close()
@pytest.mark.parametrize('legacy_dir_exists, xdg_config_home, result', [
(False, '~/.config', '~/.config/thefuck'),
(False, '/user/test/config/', '/user/test/config/thefuck'),
(True, '~/.config', '~/.thefuck'),
(True, '/user/test/config/', '~/.thefuck')])
def test_get_user_dir_path(mocker, environ, settings, legacy_dir_exists,
xdg_config_home, result):
mocker.patch('thefuck.conf.Path.is_dir',
return_value=legacy_dir_exists)
if xdg_config_home is not None:
environ['XDG_CONFIG_HOME'] = xdg_config_home
else:
environ.pop('XDG_CONFIG_HOME', None)
path = settings._get_user_dir_path().as_posix()
assert path == os.path.expanduser(result)

View File

@@ -1,13 +1,8 @@
# -*- coding: utf-8 -*-
import pytest
try:
from pathlib import Path
pathlib_name = 'pathlib'
except ImportError:
from pathlib2 import Path
pathlib_name = 'pathlib2'
from thefuck import corrector, const
from thefuck.system import Path
from tests.utils import Rule, Command, CorrectedCommand
from thefuck.corrector import get_corrected_commands, organize_commands
@@ -16,7 +11,7 @@ class TestGetRules(object):
@pytest.fixture
def glob(self, mocker):
results = {}
mocker.patch(pathlib_name + '.Path.glob',
mocker.patch('thefuck.system.Path.glob',
new_callable=lambda: lambda *_: results.pop('value', []))
return lambda value: results.update({'value': value})

View File

@@ -3,14 +3,11 @@
import os
from subprocess import PIPE
from mock import Mock
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
import pytest
from tests.utils import CorrectedCommand, Rule, Command
from thefuck import const
from thefuck.exceptions import EmptyCommand
from thefuck.system import Path
class TestCorrectedCommand(object):

View File

@@ -6,7 +6,7 @@ from mock import Mock
import six
from thefuck.utils import default_settings, \
memoize, get_closest, get_all_executables, replace_argument, \
get_all_matched_commands, is_app, for_app, cache, compatibility_call, \
get_all_matched_commands, is_app, for_app, cache, \
get_valid_history_without_current
from tests.utils import Command
@@ -188,56 +188,6 @@ class TestCache(object):
assert shelve == {key: {'etag': '0', 'value': 'test'}}
class TestCompatibilityCall(object):
def test_match(self):
def match(command):
assert command == Command()
return True
assert compatibility_call(match, Command())
def test_old_match(self, settings):
def match(command, _settings):
assert command == Command()
assert settings == _settings
return True
with pytest.warns(UserWarning):
assert compatibility_call(match, Command())
def test_get_new_command(self):
def get_new_command(command):
assert command == Command()
return True
assert compatibility_call(get_new_command, Command())
def test_old_get_new_command(self, settings):
def get_new_command(command, _settings):
assert command == Command()
assert settings == _settings
return True
with pytest.warns(UserWarning):
assert compatibility_call(get_new_command, Command())
def test_side_effect(self):
def side_effect(command, new_command):
assert command == Command() == new_command
return True
assert compatibility_call(side_effect, Command(), Command())
def test_old_side_effect(self, settings):
def side_effect(command, new_command, _settings):
assert command == Command() == new_command
assert settings == _settings
return True
with pytest.warns(UserWarning):
assert compatibility_call(side_effect, Command(), Command())
class TestGetValidHistoryWithoutCurrent(object):
@pytest.yield_fixture(autouse=True)
def fail_on_warning(self):

View File

@@ -1,12 +1,10 @@
from imp import load_source
import os
import sys
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
from warnings import warn
from six import text_type
from . import const
from .system import Path
class Settings(dict):
@@ -42,15 +40,18 @@ class Settings(dict):
settings_file.write(u'# {} = {}\n'.format(*setting))
def _get_user_dir_path(self):
# for backward compatibility, use `~/.thefuck` if it exists
legacy_user_dir = Path(os.path.expanduser('~/.thefuck'))
"""Returns Path object representing the user config resource"""
xdg_config_home = os.environ.get('XDG_CONFIG_HOME', '~/.config')
user_dir = Path(xdg_config_home, 'thefuck').expanduser()
legacy_user_dir = Path('~', '.thefuck').expanduser()
# For backward compatibility use legacy '~/.thefuck' if it exists:
if legacy_user_dir.is_dir():
warn(u'Config path {} is deprecated. Please move to {}'.format(
legacy_user_dir, user_dir))
return legacy_user_dir
else:
default_xdg_config_dir = os.path.expanduser("~/.config")
xdg_config_dir = os.getenv("XDG_CONFIG_HOME", default_xdg_config_dir)
return Path(os.path.join(xdg_config_dir, 'thefuck'))
return user_dir
def _setup_user_dir(self):
"""Returns user config dir, create it when it doesn't exist."""

View File

@@ -1,9 +1,6 @@
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
from .conf import settings
from .types import Rule
from .system import Path
from . import logs

View File

@@ -4,7 +4,6 @@ from .system import init_output
init_output()
from argparse import ArgumentParser
from warnings import warn
from pprint import pformat
import sys
from . import logs, types
@@ -37,17 +36,13 @@ def fix_command():
sys.exit(1)
def print_alias(entry_point=True):
def print_alias():
"""Prints alias for current shell."""
if entry_point:
warn('`thefuck-alias` is deprecated, use `thefuck --alias` instead.')
position = 1
else:
position = 2
try:
alias = sys.argv[2]
except IndexError:
alias = get_alias()
alias = get_alias()
if len(sys.argv) > position:
alias = sys.argv[position]
print(shell.app_alias(alias))
@@ -76,8 +71,9 @@ def main():
nargs='*',
help='command that should be fixed')
known_args = parser.parse_args(sys.argv[1:2])
if known_args.alias:
print_alias(False)
print_alias()
elif known_args.command:
fix_command()
else:

View File

@@ -0,0 +1,10 @@
from thefuck.utils import for_app
@for_app('ag')
def match(command):
return command.stderr.endswith('run ag with -Q\n')
def get_new_command(command):
return command.script.replace('ag', 'ag -Q', 1)

17
thefuck/rules/aws_cli.py Normal file
View File

@@ -0,0 +1,17 @@
import re
from thefuck.utils import for_app, replace_argument
INVALID_CHOICE = "(?<=Invalid choice: ')(.*)(?=', maybe you meant:)"
OPTIONS = "^\s*\*\s(.*)"
@for_app('aws')
def match(command):
return "usage:" in command.stderr and "maybe you meant:" in command.stderr
def get_new_command(command):
mistake = re.search(INVALID_CHOICE, command.stderr).group(0)
options = re.findall(OPTIONS, command.stderr, flags=re.MULTILINE)
return [replace_argument(command.script, mistake, o) for o in options]

View File

@@ -0,0 +1,15 @@
from thefuck.utils import for_app
@for_app('brew', at_least=2)
def match(command):
return (command.script_parts[1] in ['ln', 'link']
and "brew link --overwrite --dry-run" in command.stderr)
def get_new_command(command):
command_parts = command.script_parts[:]
command_parts[1] = 'link'
command_parts.insert(2, '--overwrite')
command_parts.insert(3, '--dry-run')
return ' '.join(command_parts)

View File

@@ -0,0 +1,14 @@
from thefuck.utils import for_app
@for_app('brew', at_least=2)
def match(command):
return (command.script_parts[1] in ['uninstall', 'rm', 'remove']
and "brew uninstall --force" in command.stdout)
def get_new_command(command):
command_parts = command.script_parts[:]
command_parts[1] = 'uninstall'
command_parts.insert(2, '--force')
return ' '.join(command_parts)

View File

@@ -1,18 +1,27 @@
import re
from thefuck.shells import shell
from thefuck.specific.git import git_support
from thefuck.system import Path
from thefuck.utils import memoize
@memoize
def _get_missing_file(command):
pathspec = re.findall(
r"error: pathspec '([^']*)' "
r'did not match any file\(s\) known to git.', command.stderr)[0]
if Path(pathspec).exists():
return pathspec
@git_support
def match(command):
return 'did not match any file(s) known to git.' in command.stderr
return ('did not match any file(s) known to git.' in command.stderr
and _get_missing_file(command))
@git_support
def get_new_command(command):
missing_file = re.findall(
r"error: pathspec '([^']*)' "
r'did not match any file\(s\) known to git.', command.stderr)[0]
missing_file = _get_missing_file(command)
formatme = shell.and_('git add -- {}', '{}')
return formatme.format(missing_file, command.script)

View File

@@ -0,0 +1,16 @@
import re
from thefuck.utils import replace_command
from thefuck.specific.git import git_support
@git_support
def match(command):
return ('bisect' in command.script_parts and
'usage: git bisect' in command.stderr)
@git_support
def get_new_command(command):
broken = re.findall(r'git bisect ([^ $]*).*', command.script)[0]
usage = re.findall(r'usage: git bisect \[([^\]]+)\]', command.stderr)[0]
return replace_command(command, broken, usage.split('|'))

View File

@@ -0,0 +1,17 @@
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@git_support
def match(command):
files = [arg for arg in command.script_parts[2:]
if not arg.startswith('-')]
return ('diff' in command.script
and '--no-index' not in command.script
and not command.stdout
and len(files) == 2)
@git_support
def get_new_command(command):
return replace_argument(command.script, 'diff', 'diff --no-index')

View File

@@ -0,0 +1,30 @@
import re
from thefuck.specific.git import git_support
error_pattern = "fatal: bad flag '(.*?)' used after filename"
@git_support
def match(command):
return re.search(error_pattern, command.stderr)
@git_support
def get_new_command(command):
command_parts = command.script_parts[:]
# find the bad flag
bad_flag = re.search(error_pattern, command.stderr).group(1)
bad_flag_index = command_parts.index(bad_flag)
# find the filename
for index in reversed(range(bad_flag_index)):
if command_parts[index][0] != '-':
filename_index = index
break
# swap them
command_parts[bad_flag_index], command_parts[filename_index] = \
command_parts[filename_index], command_parts[bad_flag_index] # noqa: E122
return u' '.join(command_parts)

View File

@@ -0,0 +1,14 @@
from thefuck.shells import shell
from thefuck.specific.git import git_support
@git_support
def match(command):
return ('pull' in command.script
and ('You have unstaged changes' in command.stderr
or 'contains uncommitted changes' in command.stderr))
@git_support
def get_new_command(command):
return shell.and_('git stash', 'git pull', 'git stash pop')

View File

@@ -1,3 +1,4 @@
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@@ -7,6 +8,29 @@ def match(command):
and 'set-upstream' in command.stderr)
def _get_upstream_option_index(command_parts):
if '--set-upstream' in command_parts:
return command_parts.index('--set-upstream')
elif '-u' in command_parts:
return command_parts.index('-u')
else:
return None
@git_support
def get_new_command(command):
return command.stderr.split('\n')[-3].strip()
# If --set-upstream or -u are passed, remove it and its argument. This is
# because the remaining arguments are concatenated onto the command suggested
# by git, which includes --set-upstream and its argument
command_parts = command.script_parts[:]
upstream_option_index = _get_upstream_option_index(command_parts)
if upstream_option_index is not None:
command_parts.pop(upstream_option_index)
# In case of `git push -u` we don't have next argument:
if len(command_parts) > upstream_option_index:
command_parts.pop(upstream_option_index)
push_upstream = command.stderr.split('\n')[-3].strip().partition('git ')[2]
return replace_argument(" ".join(command_parts), 'push', push_upstream)

View File

@@ -0,0 +1,17 @@
from difflib import get_close_matches
from thefuck.specific.git import git_support
@git_support
def match(command):
return (' rebase' in command.script and
'It seems that there is already a rebase-merge directory' in command.stderr and
'I wonder if you are in the middle of another rebase' in command.stderr)
@git_support
def get_new_command(command):
command_list = ['git rebase --continue', 'git rebase --abort', 'git rebase --skip']
rm_cmd = command.stderr.split('\n')[-4]
command_list.append(rm_cmd.strip())
return get_close_matches(command.script, command_list, 4, 0)

View File

@@ -0,0 +1,19 @@
from thefuck.specific.git import git_support
@git_support
def match(command):
return (' rm ' in command.script and
'error: the following file has local modifications' in command.stderr and
'use --cached to keep the file, or -f to force removal' in command.stderr)
@git_support
def get_new_command(command):
command_parts = command.script_parts[:]
index = command_parts.index('rm') + 1
command_parts.insert(index, '--cached')
command_list = [u' '.join(command_parts)]
command_parts[index] = '-f'
command_list.append(u' '.join(command_parts))
return command_list

View File

@@ -10,6 +10,7 @@ def match(command):
@git_support
def get_new_command(command):
index = command.script_parts.index('rm') + 1
command.script_parts.insert(index, '-r')
return u' '.join(command.script_parts)
command_parts = command.script_parts[:]
index = command_parts.index('rm') + 1
command_parts.insert(index, '-r')
return u' '.join(command_parts)

View File

@@ -0,0 +1,18 @@
from thefuck.shells import shell
from thefuck.specific.git import git_support
@git_support
def match(command):
return ('stash' in command.script
and 'pop' in command.script
and 'Your local changes to the following files would be overwritten by merge' in command.stderr)
@git_support
def get_new_command(command):
return shell.and_('git add .', 'git stash pop', 'git reset .')
# make it come before the other applicable rules
priority = 900

View File

@@ -0,0 +1,25 @@
import subprocess
from thefuck.utils import for_app, replace_command, eager
import sys
@for_app('ifconfig')
def match(command):
return 'error fetching interface information: Device not found' \
in command.stderr
@eager
def _get_possible_interfaces():
proc = subprocess.Popen(['ifconfig', '-a'], stdout=subprocess.PIPE)
for line in proc.stdout.readlines():
line = line.decode()
if line and line != '\n' and not line.startswith(' '):
yield line.split(' ')[0]
def get_new_command(command):
interface = command.stderr.split(' ')[0][:-1]
possible_interfaces = _get_possible_interfaces()
return replace_command(command, interface, possible_interfaces)

10
thefuck/rules/ls_all.py Normal file
View File

@@ -0,0 +1,10 @@
from thefuck.utils import for_app
@for_app('ls')
def match(command):
return command.stdout.strip() == ''
def get_new_command(command):
return ' '.join(['ls', '-A'] + command.script_parts[1:])

View File

@@ -12,10 +12,22 @@ def get_new_command(command):
if '2' in command.script:
return command.script.replace("2", "3")
last_arg = command.script_parts[-1]
help_command = last_arg + ' --help'
# If there are no man pages for last_arg, suggest `last_arg --help` instead.
# Otherwise, suggest `--help` after suggesting other man page sections.
if command.stderr.strip() == 'No manual entry for ' + last_arg:
return [help_command]
split_cmd2 = command.script_parts
split_cmd3 = split_cmd2[:]
split_cmd2.insert(1, ' 2 ')
split_cmd3.insert(1, ' 3 ')
return ["".join(split_cmd3), "".join(split_cmd2)]
return [
"".join(split_cmd3),
"".join(split_cmd2),
help_command,
]

View File

@@ -6,9 +6,8 @@ from thefuck.specific.sudo import sudo_support
@sudo_support
def match(command):
toks = command.script_parts
return (toks
and toks[0].endswith('.py')
return (command.script_parts
and command.script_parts[0].endswith('.py')
and ('Permission denied' in command.stderr or
'command not found' in command.stderr))

View File

@@ -0,0 +1,11 @@
# -*- encoding: utf-8 -*-
CEDILLA = u"ç"
def match(command):
return command.script.endswith(CEDILLA)
def get_new_command(command):
return command.script[:-1]

View File

@@ -19,7 +19,8 @@ patterns = ['permission denied',
'you don\'t have access to the history db.',
'authentication is required',
'edspermissionerror',
'you don\'t have write permissions']
'you don\'t have write permissions',
'use `sudo`']
def match(command):

View File

@@ -17,6 +17,6 @@ def match(command):
@sudo_support
def get_new_command(command):
cmd = command.script_parts
cmd = command.script_parts[:]
cmd[-1], cmd[-2] = cmd[-2], cmd[-1]
return ' '.join(cmd)

View File

@@ -1,9 +1,5 @@
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
from thefuck.utils import for_app, replace_command, eager, memoize
from thefuck.system import Path
@memoize

View File

@@ -22,7 +22,7 @@ shells = {'bash': Bash,
def _get_shell():
proc = Process(os.getpid())
while proc is not None:
while proc is not None and proc.pid > 0:
try:
name = proc.name()
except TypeError:

View File

@@ -14,7 +14,7 @@ class Bash(Generic):
" eval $TF_CMD".format(fuck)
if settings.alter_history:
return alias + " && history -s $TF_CMD'"
return alias + "; history -s $TF_CMD'"
else:
return alias + "'"

View File

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

View File

@@ -1,6 +1,7 @@
import io
import os
import shlex
import bashlex
import six
from ..utils import memoize
from ..conf import settings
@@ -64,10 +65,29 @@ class Generic(object):
return
def split_command(self, command):
"""Split the command using shell-like syntax."""
"""Split the command using shell-like syntax.
If bashlex fails for some reason, fallback to shlex
See https://github.com/idank/bashlex#limitations
"""
encoded = self.encode_utf8(command)
try:
splitted = list(bashlex.split(encoded))
except Exception:
splitted = shlex.split(encoded)
return self.decode_utf8(splitted)
def encode_utf8(self, command):
if six.PY2:
return [s.decode('utf8') for s in shlex.split(command.encode('utf8'))]
return shlex.split(command)
return command.encode('utf8')
return command
def decode_utf8(self, command_parts):
if six.PY2:
return [s.decode('utf8') for s in command_parts]
return command_parts
def quote(self, s):
"""Return a shell-escaped version of the string s."""

View File

@@ -15,7 +15,7 @@ class Zsh(Generic):
" eval $TF_CMD".format(alias_name)
if settings.alter_history:
return alias + " && print -s $TF_CMD'"
return alias + " ; test -n \"$TF_CMD\" && print -s $TF_CMD'"
else:
return alias + "'"

View File

@@ -1,3 +1,4 @@
import os
import sys
import tty
import termios
@@ -33,3 +34,15 @@ def get_key():
return const.KEY_DOWN
return ch
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
def _expanduser(self):
return self.__class__(os.path.expanduser(str(self)))
if not hasattr(Path, 'expanduser'):
Path.expanduser = _expanduser

View File

@@ -25,3 +25,15 @@ def get_key():
encoding = sys.stdout.encoding or os.environ.get('PYTHONIOENCODING', 'utf-8')
return ch.decode(encoding)
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
def _expanduser(self):
return self.__class__(os.path.expanduser(str(self)))
# pathlib's expanduser fails on windows, see http://bugs.python.org/issue19776
Path.expanduser = _expanduser

View File

@@ -9,7 +9,6 @@ from .shells import shell
from .conf import settings
from .const import DEFAULT_PRIORITY, ALL_ENABLED
from .exceptions import EmptyCommand
from .utils import compatibility_call
class Command(object):
@@ -35,7 +34,8 @@ class Command(object):
except Exception:
logs.debug(u"Can't split command script {} because:\n {}".format(
self, sys.exc_info()))
self._script_parts = None
self._script_parts = []
return self._script_parts
def __eq__(self, other):
@@ -225,7 +225,7 @@ class Rule(object):
try:
with logs.debug_time(u'Trying rule: {};'.format(self.name)):
if compatibility_call(self.match, command):
if self.match(command):
return True
except Exception:
logs.rule_failed(self, sys.exc_info())
@@ -237,7 +237,7 @@ class Rule(object):
:rtype: Iterable[CorrectedCommand]
"""
new_commands = compatibility_call(self.get_new_command, command)
new_commands = self.get_new_command(command)
if not isinstance(new_commands, list):
new_commands = (new_commands,)
for n, new_command in enumerate(new_commands):
@@ -283,7 +283,7 @@ class CorrectedCommand(object):
"""
if self.side_effect:
compatibility_call(self.side_effect, old_cmd, self.script)
self.side_effect(old_cmd, self.script)
if settings.alter_history:
shell.put_to_history(self.script)
# This depends on correct setting of PYTHONIOENCODING by the alias:

View File

@@ -4,17 +4,13 @@ import pkg_resources
import re
import shelve
import six
from .conf import settings
from contextlib import closing
from decorator import decorator
from difflib import get_close_matches
from functools import wraps
from inspect import getargspec
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path
from warnings import warn
from .conf import settings
from .system import Path
DEVNULL = open(os.devnull, 'w')
@@ -163,7 +159,7 @@ def is_app(command, *app_names, **kwargs):
if kwargs:
raise TypeError("got an unexpected keyword argument '{}'".format(kwargs.keys()))
if command.script_parts is not None and len(command.script_parts) > at_least:
if len(command.script_parts) > at_least:
return command.script_parts[0] in app_names
return False
@@ -245,27 +241,6 @@ def cache(*depends_on):
cache.disabled = False
def compatibility_call(fn, *args):
"""Special call for compatibility with user-defined old-style rules
with `settings` param.
"""
fn_args_count = len(getargspec(fn).args)
if fn.__name__ in ('match', 'get_new_command') and fn_args_count == 2:
warn("Two arguments `{}` from rule `{}` is deprecated, please "
"remove `settings` argument and use "
"`from thefuck.conf import settings` instead."
.format(fn.__name__, fn.__module__))
args += (settings,)
if fn.__name__ == 'side_effect' and fn_args_count == 3:
warn("Three arguments `side_effect` from rule `{}` is deprecated, "
"please remove `settings` argument and use `from thefuck.conf "
"import settings` instead."
.format(fn.__name__, fn.__module__))
args += (settings,)
return fn(*args)
def get_installation_info():
return pkg_resources.require('thefuck')[0]

View File

@@ -1,5 +1,5 @@
[tox]
envlist = py27,py33,py34,py35
envlist = py27,py33,py34,py35,py36
[testenv]
deps = -rrequirements.txt