1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-10-31 23:22:10 +00:00

Compare commits

...

29 Commits
3.32 ... master

Author SHA1 Message Date
Alan Gabbianelli
c7e7e1d884 Fix typos in README.md 2024-01-25 20:52:35 +01:00
Pablo Santiago Blum de Aguiar
62e0767c50 #N/A: Fix a couple of issues after new flake8 release 2023-07-30 22:22:37 +02:00
Gerardo Grignoli
3cd187a3bb #1329: Add support for Windows CMD and easier setup for Powershell
* feat: Added nicer support for Windows CMD & Powershell

* Fix  typo

* Fix CMD console color after thefuck Ctrl-C

* Update setup.py

Co-authored-by: Pablo Aguiar <scorphus@gmail.com>

* Update setup.py

Co-authored-by: Pablo Aguiar <scorphus@gmail.com>

* Addressed PR comments

* fix spacing and newline issues

---------

Co-authored-by: Pablo Aguiar <scorphus@gmail.com>
2023-07-10 15:09:27 +02:00
Pablo Santiago Blum de Aguiar
0420442e77 #1248: Use imp only when importlib.util not available
The imp module is deprecated and will be removed in Python 12.
2023-07-10 14:43:45 +02:00
Pablo Santiago Blum de Aguiar
617aaa1fd0 #1248: Skip a failing test when running on Windows 2023-07-10 14:40:45 +02:00
Pablo Santiago Blum de Aguiar
cf0921be4a #1248: Check for multiple patterns in functional tests 2023-07-10 14:39:58 +02:00
Pablo Santiago Blum de Aguiar
ef1ea4b4dd #1248: Check container status before test functions 2023-07-10 14:36:42 +02:00
Pablo Santiago Blum de Aguiar
2cadcca904 #1248: Reuse Docker images in functional tests 2023-07-10 14:34:46 +02:00
Pablo Santiago Blum de Aguiar
d81929f294 #1248: Move deprecated Python versions to separate workflow job 2023-07-10 14:22:23 +02:00
Pablo Santiago Blum de Aguiar
b03e0913d3 #1248: Run workflows on push and pull_request 2023-07-10 14:20:40 +02:00
Hugo van Kemenade
b79e104df8 Add support for Python 3.11 2023-07-09 13:03:32 +02:00
Hugo van Kemenade
3f71959b1b Add python_requires to help pip 2023-07-09 13:03:32 +02:00
Hugo van Kemenade
51b82c5377 Update min Python 3 version required in README 2023-07-09 13:03:32 +02:00
Hugo van Kemenade
5f562a185c Drop the dot https://twitter.com/pytestdotorg/status/753767547866972160 2023-07-09 13:03:27 +02:00
Hugo van Kemenade
1a242c7daa Test on Python 3.10 final 2023-07-09 13:01:48 +02:00
josepdaniel
ceeaeab94b Add terraform 'no command' rule (#1317)
* Add terraform 'no command' rule

* Feedback from PR

Co-authored-by: Joseph Daniel <jdn@logpoint.com>
2022-09-25 21:37:47 +02:00
Pablo Aguiar
77627a3140 #N/A: Define branches in workflow (#1328) 2022-09-07 21:33:40 +02:00
Pablo Santiago Blum de Aguiar
03a032295d #N/A: Change workflow triggers 2022-08-31 22:36:44 +02:00
Dan
56c16b737f Added Arch based installation (#1319)
Co-authored-by: Dan <dan@arch.localdomain>
2022-08-31 20:42:41 +02:00
Miguel Guthridge
f9768cf929 #1302: Add new git_clone_missing rule
* Add git clone missing rule

* Clean up tests and improve matching

* Make rules behave correctly?

* Improve tests and redo matcher

* Remove unnecessary tests

* Improvements as per code review

* Remove dead tests

* Improve match function for git clone missing

* Improve tests

* Fix more tests

* Fix failing test

* Add Macos's /bin/sh command output to match

* Add output for lines uncovered by tests

Co-authored-by: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
2022-07-03 13:22:36 +02:00
Peter
ed40463105 #1299: Update output for brew_install and cleanup (#1316)
* fix: Update output for brew_install test: fixed

* chore: fixing flake8 styles

* feat: show more suggestions

* test: new functions added and multi suggestions

* refactor: rename to _get_suggestions
2022-07-02 15:06:00 +02:00
Peter
f1b7d879bd #1290: Update output for brew_update_formula
* fix: brew_update_formula change output string

* fix: regex removed, test: backtick added

* Update tests/rules/test_brew_update_formula.py

* Update thefuck/rules/brew_update_formula.py

Co-authored-by: Pablo Aguiar <scorphus@gmail.com>
2022-06-28 18:28:38 +02:00
Nikos Kakonas
5198b34f24 #1282: Keep quotes in the script on no_command rule
Co-authored-by: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
2022-06-13 23:29:15 +02:00
Pablo Santiago Blum de Aguiar
1a1b5200a9 #N/A: Lock pyte<=0.8.0 for Python 2 2022-06-08 23:39:19 +02:00
0xphk
06cb50b1e3 #1308: Add updatedb (mlocate) pattern to sudo rule
* added updatedb(mlocate) to sudo rules

* Add test

Co-authored-by: phk <phk@terminal21.de>
Co-authored-by: Pablo Santiago Blum de Aguiar <scorphus@gmail.com>
2022-06-05 22:58:31 +02:00
Oļegs Čapligins
16eb823066 #1261: Add two more patterns to sudo rule (#1307)
* macos shutdown sudo fix

* Update tests/rules/test_sudo.py

* Update thefuck/rules/sudo.py

* Update tests/rules/test_sudo.py

* Update thefuck/rules/sudo.py

Co-authored-by: Pablo Aguiar <scorphus@gmail.com>
2022-05-29 21:40:31 +02:00
Tagada
d8ddf5a2be #1279: Add pikaur AUR manager to Arch Linux's commands
* Add pikaur AUR manager to Arch Linux's commands

* Update README.md

* Update test_pacman_not_found.py

* Update pacman_not_found.py

* Update README.md

Co-authored-by: Pablo Aguiar <scorphus@gmail.com>
2022-03-27 22:06:40 +02:00
Marek Šuppa
cf1beb6b89 fix: Add missing comma
* Add missing comma in a test
2022-01-30 22:34:38 +01:00
Anupam Patil
925e562d96 Updated license date to 2022 2022-01-20 22:01:21 +01:00
39 changed files with 367 additions and 154 deletions

View File

@@ -3,14 +3,14 @@ name: Tests
on: [push, pull_request]
env:
PYTHON_LATEST: 3.9
PYTHON_LATEST: "3.11"
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10-dev]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12-dev"]
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
@@ -41,9 +41,28 @@ jobs:
run: coverage run --source=thefuck,tests -m pytest -v --capture=sys tests
- name: Run tests (including functional)
if: matrix.os == 'ubuntu-latest' && matrix.python-version == env.PYTHON_LATEST
run: coverage run --source=thefuck,tests -m pytest -v --capture=sys tests --enable-functional
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

View File

@@ -51,13 +51,13 @@ flake8
Run unit tests:
```bash
py.test
pytest
```
Run unit and functional tests (requires docker):
```bash
py.test --enable-functional
pytest --enable-functional
```
For sending package to pypi:
@@ -89,4 +89,4 @@ Assuming you have the prerequisites:
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.
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-2021 Vladimir Iakovlev
Copyright (c) 2015-2022 Vladimir Iakovlev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -106,7 +106,7 @@ Reading package lists... Done
## Requirements
- python (3.4+)
- python (3.5+)
- pip
- python-dev
@@ -137,6 +137,11 @@ 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
@@ -199,17 +204,17 @@ following rules are enabled by default:
* `aws_cli` &ndash; fixes misspelled commands like `aws dynamdb scan`;
* `az_cli` &ndash; fixes misspelled commands like `az providers`;
* `cargo` &ndash; runs `cargo build` instead of `cargo`;
* `cargo_no_command` &ndash; fixes wrongs commands like `cargo buid`;
* `cargo_no_command` &ndash; fixes wrong commands like `cargo buid`;
* `cat_dir` &ndash; replaces `cat` with `ls` when you try to `cat` a directory;
* `cd_correction` &ndash; spellchecks and correct failed cd commands;
* `cd_correction` &ndash; spellchecks and corrects failed cd commands;
* `cd_cs` &ndash; changes `cs` to `cd`;
* `cd_mkdir` &ndash; creates directories before cd'ing into them;
* `cd_parent` &ndash; changes `cd..` to `cd ..`;
* `chmod_x` &ndash; add execution bit;
* `choco_install` &ndash; append common suffixes for chocolatey packages;
* `chmod_x` &ndash; adds execution bit;
* `choco_install` &ndash; appends common suffixes for chocolatey packages;
* `composer_not_command` &ndash; fixes composer command name;
* `conda_mistype` &ndash; fixes conda commands;
* `cp_create_destination` &ndash; creates a new directory when you attempt to `cp` or `mv` to a non existent one
* `cp_create_destination` &ndash; creates a new directory when you attempt to `cp` or `mv` to a non-existent one
* `cp_omitting_directory` &ndash; adds `-a` when you `cp` directory;
* `cpp11` &ndash; adds missing `-std=c++11` to `g++` or `clang++`;
* `dirty_untar` &ndash; fixes `tar x` command that untarred in the current directory;
@@ -220,7 +225,7 @@ following rules are enabled by default:
* `docker_not_command` &ndash; fixes wrong docker commands like `docker tags`;
* `docker_image_being_used_by_container` &dash; removes the container that is using the image before removing the image;
* `dry` &ndash; fixes repetitions like `git git push`;
* `fab_command_not_found` &ndash; fix misspelled fabric commands;
* `fab_command_not_found` &ndash; fixes misspelled fabric commands;
* `fix_alt_space` &ndash; replaces Alt+Space with Space character;
* `fix_file` &ndash; opens a file with an error in your `$EDITOR`;
* `gem_unknown_command` &ndash; fixes wrong `gem` commands;
@@ -234,6 +239,7 @@ following rules are enabled by default:
* `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;
@@ -254,10 +260,10 @@ following rules are enabled by default:
* `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_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_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;
* `git_remote_delete` &ndash; replaces `git remote delete remote_name` with `git remote remove remote_name`;
* `git_rm_local_modifications` &ndash; adds `-f` or `--cached` when you try to `rm` a locally modified file;
* `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_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;
@@ -275,7 +281,7 @@ following rules are enabled by default:
* `grunt_task_not_found` &ndash; fixes misspelled `grunt` commands;
* `gulp_not_task` &ndash; fixes misspelled `gulp` tasks;
* `has_exists_script` &ndash; prepends `./` when script/binary exists;
* `heroku_multiple_apps` &ndash; add `--app <app>` to `heroku` commands like `heroku pg`;
* `heroku_multiple_apps` &ndash; adds `--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;
* `hostscli` &ndash; tries to fix `hostscli` usage;
@@ -301,7 +307,7 @@ following rules are enabled by default:
* `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`;
* `open` &ndash; either prepends `http://` to address passed to `open` or creates 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;
@@ -314,8 +320,8 @@ following rules are enabled by default:
* `path_from_history` &ndash; replaces not found path with a similar absolute path from history;
* `rails_migrations_pending` &ndash; runs pending migrations;
* `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_shell_prompt_literal` &ndash; removes leading shell prompt symbol `$`, common when copying commands from documentations;
* `remove_trailing_cedilla` &ndash; removes trailing cedillas `ç`, a common typo for European keyboard layouts;
* `rm_dir` &ndash; adds `-rf` when you try to remove a directory;
* `scm_correction` &ndash; corrects wrong scm like `hg log` to `git log`;
* `sed_unterminated_s` &ndash; adds missing '/' to `sed`'s `s` commands;
@@ -325,8 +331,9 @@ following rules are enabled by default:
* `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;
* `test.py` &ndash; runs `py.test` instead of `test.py`;
* `terraform_init.py` &ndash; runs `terraform init` before plan or apply;
* `terraform_no_command.py` &ndash; fixes unrecognized `terraform` commands;
* `test.py` &ndash; runs `pytest` 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`;
@@ -360,9 +367,9 @@ The following rules are enabled by default on specific platforms only:
* `brew_update_formula` &ndash; turns `brew update <formula>` into `brew upgrade <formula>`;
* `dnf_no_such_command` &ndash; fixes mistyped DNF commands;
* `nixos_cmd_not_found` &ndash; installs apps on NixOS;
* `pacman` &ndash; installs app with `pacman` if it is not installed (uses `yay` or `yaourt` if available);
* `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` or `yaourt`.
* `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`;
The following commands are bundled with *The Fuck*, but are not enabled by

2
scripts/fuck.bat Normal file
View File

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

22
scripts/fuck.ps1 Normal file
View File

@@ -0,0 +1,22 @@
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

@@ -33,12 +33,24 @@ elif (3, 0) < version < (3, 5):
VERSION = '3.32'
install_requires = ['psutil', 'colorama', 'six', 'decorator', 'pyte']
install_requires = ['psutil', 'colorama', 'six']
extras_require = {':python_version<"3.4"': ['pathlib2'],
':python_version<"3.3"': ['backports.shutil_get_terminal_size'],
':python_version<="2.7"': ['decorator<5'],
':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",
@@ -51,8 +63,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,
entry_points={'console_scripts': [
'thefuck = thefuck.entrypoints.main:main',
'fuck = thefuck.entrypoints.not_configured:main']})
scripts=scripts,
entry_points=entry_points)

7
tests/Dockerfile Normal file
View File

@@ -0,0 +1,7 @@
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

@@ -0,0 +1,20 @@
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,10 +20,12 @@ 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')
assert proc.expect([TIMEOUT, to])
pattern = [TIMEOUT]
pattern.extend(to)
assert proc.expect(pattern)
def history_not_changed(proc, TIMEOUT):
@@ -44,14 +46,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'])
assert proc.expect([TIMEOUT, u'git help', u'git hook'])
proc.send('\033[A')
assert proc.expect([TIMEOUT, u'git push'])
proc.send('\033[B')
assert proc.expect([TIMEOUT, u'git help'])
assert proc.expect([TIMEOUT, u'git help', u'git hook'])
proc.send('\n')
assert proc.expect([TIMEOUT, u'usage'])
assert proc.expect([TIMEOUT, u'usage', u'fatal: not a git repository'])
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-bash',
u'FROM python:3',
python_3 = (u'thefuck/python3',
u'',
u'sh')
python_2 = (u'thefuck/python2-bash',
u'FROM python:2',
python_2 = (u'thefuck/python2',
u'',
u'sh')
@@ -28,8 +28,6 @@ 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")
@@ -47,7 +45,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')
history_changed(proc, TIMEOUT, u'git help', u'git hook')
@pytest.mark.functional

View File

@@ -2,29 +2,13 @@ import pytest
from tests.functional.plots import with_confirmation, without_confirmation, \
refuse_with_confirmation, select_command_with_arrows
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'))
containers = ((u'thefuck/python3', u'', u'fish'),
(u'thefuck/python2', u'', 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,23 +2,13 @@ import pytest
from tests.functional.plots import with_confirmation, without_confirmation, \
refuse_with_confirmation, select_command_with_arrows
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'))
containers = ((u'thefuck/python3', u'', u'tcsh'),
(u'thefuck/python2', u'', 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,17 +4,8 @@ from tests.functional.plots import with_confirmation, without_confirmation, \
select_command_with_arrows, how_to_configure
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')
python_3 = (u'thefuck/python3', u'', u'sh')
python_2 = (u'thefuck/python2', u'', u'sh')
init_zshrc = u'''echo '
@@ -35,8 +26,6 @@ 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")
@@ -54,7 +43,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')
history_changed(proc, TIMEOUT, u'git help', u'git hook')
@pytest.mark.functional

View File

@@ -1,5 +1,7 @@
# -*- encoding: utf-8 -*-
import pytest
import sys
from mock import Mock, patch
from psutil import AccessDenied, TimeoutExpired
@@ -30,6 +32,7 @@ class TestRerun(object):
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')

View File

@@ -1,17 +1,26 @@
import pytest
from thefuck.rules.brew_install import match, get_new_command
from thefuck.rules.brew_install import _get_formulas
from thefuck.rules.brew_install import match, get_new_command, _get_suggestions
from thefuck.types import Command
@pytest.fixture
def brew_no_available_formula():
return '''Error: No available formula for elsticsearch '''
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?'''
@pytest.fixture
def brew_install_no_argument():
return '''This command requires a formula argument'''
return '''Install a formula or cask. Additional options specific to a formula may be'''
@pytest.fixture
@@ -19,28 +28,38 @@ def brew_already_installed():
return '''Warning: git-2.3.5 already installed'''
def _is_not_okay_to_test():
return 'elasticsearch' not in _get_formulas()
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']
@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,
def test_match(brew_no_available_formula_one, brew_no_available_formula_two,
brew_no_available_formula_three, brew_already_installed,
brew_install_no_argument):
assert match(Command('brew install elsticsearch',
brew_no_available_formula))
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 not match(Command('brew install git',
brew_already_installed))
assert not match(Command('brew install', brew_install_no_argument))
@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'
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']
assert get_new_command(Command('brew install aa',
brew_no_available_formula))\
brew_no_available_formula_one))\
!= '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

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

View File

@@ -0,0 +1,50 @@
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

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

View File

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

@@ -13,7 +13,7 @@ def output():
[
"$ cd newdir",
" $ cd newdir",
"$ $ cd newdir"
"$ $ cd newdir",
" $ $ cd newdir",
],
)

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,6 +10,9 @@ 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

@@ -0,0 +1,27 @@
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

@@ -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

@@ -245,7 +245,7 @@ class TestGetValidHistoryWithoutCurrent(object):
def history(self, mocker):
mock = mocker.patch('thefuck.shells.shell.get_history')
# Passing as an argument causes `UnicodeDecodeError`
# with newer py.test and python 2.7
# with newer pytest and python 2.7
mock.return_value = ['le cat', 'fuck', 'ls cat',
'diff x', 'nocommand x', u'café ô']
return mock

View File

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

View File

@@ -1,42 +1,24 @@
import os
import re
from thefuck.utils import get_closest, replace_argument
from thefuck.specific.brew import get_brew_path_prefix, brew_available
from thefuck.utils import for_app
from thefuck.specific.brew import brew_available
enabled_by_default = brew_available
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)
def _get_suggestions(str):
suggestions = str.replace(" or ", ", ").split(", ")
return suggestions
@for_app('brew', at_least=2)
def match(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
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
def get_new_command(command):
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)
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]

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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