1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-11-01 15:42:06 +00:00

Compare commits

...

9 Commits
3.24 ... 3.25

Author SHA1 Message Date
Vladimir Iakovlev
4780027d63 Bump to 3.25 2017-11-23 20:51:04 +01:00
Vladimir Iakovlev
4847078f37 #737: Add support of third-party rules 2017-11-23 20:21:44 +01:00
Joseph Frazier
d582159670 Add apt_list_upgradable rule (#732)
This helps you run `apt list --upgradable` after `apt update`,
as it suggests.
2017-11-16 20:19:44 +01:00
Jarrod Moore
97123dbf73 Fix zsh alias (#733) 2017-11-16 20:19:16 +01:00
Joseph Frazier
10ac1a3b38 #728: Add heroku_multiple_apps rule (#729)
Closes https://github.com/nvbn/thefuck/issues/728
2017-11-09 18:42:23 -05:00
Joseph Frazier
8fb5ddefb6 git_flag_after_filename: Handle new error message
See 2a5aa826ee
2017-11-01 09:42:52 -04:00
Joseph Frazier
f1fab0dbb2 git_flag_after_filename: Call match() instead of copying its body 2017-11-01 09:42:52 -04:00
Vladimir Iakovlev
68df7154e5 #N/A: FIx new flake8 warnings 2017-10-25 19:31:03 +02:00
Vladimir Iakovlev
08082e606b #715: Fix work on Windows 2017-10-25 19:16:28 +02:00
14 changed files with 229 additions and 21 deletions

View File

@@ -218,6 +218,7 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `grunt_task_not_found` – fixes misspelled `grunt` commands;
* `gulp_not_task` – fixes misspelled `gulp` tasks;
* `has_exists_script` – prepends `./` when script/binary exists;
* `heroku_multiple_apps` &ndash; add `--app <app>` to `heroku` commands like `heroku pg`;
* `heroku_not_command` &ndash; fixes wrong `heroku` commands like `heroku log`;
* `history` &ndash; tries to replace command with most similar command from history;
* `hostscli` &ndash; tries to fix `hostscli` usage;
@@ -280,6 +281,7 @@ Enabled by default only on specific platforms:
* `apt_get` &ndash; installs app from apt if it not installed (requires `python-commandnotfound` / `python3-commandnotfound`);
* `apt_get_search` &ndash; changes trying to search using `apt-get` with searching using `apt-cache`;
* `apt_invalid_operation` &ndash; fixes invalid `apt` and `apt-get` calls, like `apt-get isntall vim`;
* `apt_list_upgradable` &ndash; helps you run `apt list --upgradable` after `apt update`;
* `brew_cask_dependency` &ndash; installs cask dependencies;
* `brew_install` &ndash; fixes formula name for `brew install`;
* `brew_link` &ndash; adds `--overwrite --dry-run` if linking fails;
@@ -403,6 +405,25 @@ export THEFUCK_PRIORITY='no_command=9999:apt_get=100'
export THEFUCK_HISTORY_LIMIT='2000'
```
## Third-party packages with rules
If you want to make very specific rules or rules, that you don't want to make public,
but share with other people.
You can create a special package with name `thefuck_contrib_*` with following structure:
```
thefuck_contrib_foo
thefuck_contrib_foo
rules
__init__.py
*third-party rules*
__init__.py
*third-party-utils*
setup.py
```
And thefuck will find all rules from `rules` module.
## Experimental instant mode
By default The Fuck reruns a previous command and that takes time,

View File

@@ -31,7 +31,7 @@ elif (3, 0) < version < (3, 3):
' ({}.{} detected).'.format(*version))
sys.exit(-1)
VERSION = '3.24'
VERSION = '3.25'
install_requires = ['psutil', 'colorama', 'six', 'decorator', 'pyte']
extras_require = {':python_version<"3.4"': ['pathlib2'],

View File

@@ -0,0 +1,72 @@
import pytest
from thefuck.rules.apt_list_upgradable import get_new_command, match
from thefuck.types import Command
match_output = '''
Hit:1 http://us.archive.ubuntu.com/ubuntu zesty InRelease
Hit:2 http://us.archive.ubuntu.com/ubuntu zesty-updates InRelease
Get:3 http://us.archive.ubuntu.com/ubuntu zesty-backports InRelease [89.2 kB]
Hit:4 http://security.ubuntu.com/ubuntu zesty-security InRelease
Hit:5 http://ppa.launchpad.net/ubuntu-mozilla-daily/ppa/ubuntu zesty InRelease
Hit:6 https://download.docker.com/linux/ubuntu zesty InRelease
Hit:7 https://cli-assets.heroku.com/branches/stable/apt ./ InRelease
Fetched 89.2 kB in 0s (122 kB/s)
Reading package lists... Done
Building dependency tree
Reading state information... Done
8 packages can be upgraded. Run 'apt list --upgradable' to see them.
'''
no_match_output = '''
Hit:1 http://us.archive.ubuntu.com/ubuntu zesty InRelease
Get:2 http://us.archive.ubuntu.com/ubuntu zesty-updates InRelease [89.2 kB]
Get:3 http://us.archive.ubuntu.com/ubuntu zesty-backports InRelease [89.2 kB]
Get:4 http://security.ubuntu.com/ubuntu zesty-security InRelease [89.2 kB]
Hit:5 https://cli-assets.heroku.com/branches/stable/apt ./ InRelease
Hit:6 http://ppa.launchpad.net/ubuntu-mozilla-daily/ppa/ubuntu zesty InRelease
Hit:7 https://download.docker.com/linux/ubuntu zesty InRelease
Get:8 http://us.archive.ubuntu.com/ubuntu zesty-updates/main i386 Packages [232 kB]
Get:9 http://us.archive.ubuntu.com/ubuntu zesty-updates/main amd64 Packages [235 kB]
Get:10 http://us.archive.ubuntu.com/ubuntu zesty-updates/main amd64 DEP-11 Metadata [55.2 kB]
Get:11 http://us.archive.ubuntu.com/ubuntu zesty-updates/main DEP-11 64x64 Icons [32.3 kB]
Get:12 http://us.archive.ubuntu.com/ubuntu zesty-updates/universe amd64 Packages [156 kB]
Get:13 http://us.archive.ubuntu.com/ubuntu zesty-updates/universe i386 Packages [156 kB]
Get:14 http://us.archive.ubuntu.com/ubuntu zesty-updates/universe amd64 DEP-11 Metadata [175 kB]
Get:15 http://us.archive.ubuntu.com/ubuntu zesty-updates/universe DEP-11 64x64 Icons [253 kB]
Get:16 http://us.archive.ubuntu.com/ubuntu zesty-updates/multiverse amd64 DEP-11 Metadata [5,840 B]
Get:17 http://us.archive.ubuntu.com/ubuntu zesty-backports/universe amd64 DEP-11 Metadata [4,588 B]
Get:18 http://security.ubuntu.com/ubuntu zesty-security/main amd64 DEP-11 Metadata [12.7 kB]
Get:19 http://security.ubuntu.com/ubuntu zesty-security/main DEP-11 64x64 Icons [17.6 kB]
Get:20 http://security.ubuntu.com/ubuntu zesty-security/universe amd64 DEP-11 Metadata [21.6 kB]
Get:21 http://security.ubuntu.com/ubuntu zesty-security/universe DEP-11 64x64 Icons [47.7 kB]
Get:22 http://security.ubuntu.com/ubuntu zesty-security/multiverse amd64 DEP-11 Metadata [208 B]
Fetched 1,673 kB in 0s (1,716 kB/s)
Reading package lists... Done
Building dependency tree
Reading state information... Done
All packages are up to date.
'''
def test_match():
assert match(Command('sudo apt update', match_output))
@pytest.mark.parametrize('command', [
Command('apt-cache search foo', ''),
Command('aptitude search foo', ''),
Command('apt search foo', ''),
Command('apt-get install foo', ''),
Command('apt-get source foo', ''),
Command('apt-get clean', ''),
Command('apt-get remove', ''),
Command('apt-get update', ''),
Command('sudo apt update', no_match_output)
])
def test_not_match(command):
assert not match(command)
def test_get_new_command():
new_command = get_new_command(Command('sudo apt update', match_output))
assert new_command == 'apt list --upgradable'

View File

@@ -8,10 +8,16 @@ command2 = Command('git log README.md -p CONTRIBUTING.md',
"fatal: bad flag '-p' used after filename")
command3 = Command('git log -p README.md --name-only',
"fatal: bad flag '--name-only' used after filename")
command4 = Command('git log README.md -p',
"fatal: option '-p' must come before non-option arguments")
command5 = Command('git log README.md -p CONTRIBUTING.md',
"fatal: option '-p' must come before non-option arguments")
command6 = Command('git log -p README.md --name-only',
"fatal: option '--name-only' must come before non-option arguments")
@pytest.mark.parametrize('command', [
command1, command2, command3])
command1, command2, command3, command4, command5, command6])
def test_match(command):
assert match(command)
@@ -26,6 +32,9 @@ def test_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")])
(command3, "git log -p --name-only README.md"),
(command4, "git log -p README.md"),
(command5, "git log -p README.md CONTRIBUTING.md"),
(command6, "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,55 @@
# -*- coding: utf-8 -*-
import pytest
from thefuck.types import Command
from thefuck.rules.heroku_multiple_apps import match, get_new_command
suggest_output = '''
▸ Multiple apps in git remotes
▸ Usage: --remote heroku-dev
▸ or: --app myapp-dev
▸ Your local git repository has more than 1 app referenced in git remotes.
▸ Because of this, we can't determine which app you want to run this command against.
▸ Specify the app you want with --app or --remote.
▸ Heroku remotes in repo:
▸ myapp (heroku)
▸ myapp-dev (heroku-dev)
▸ https://devcenter.heroku.com/articles/multiple-environments
'''
not_match_output = '''
=== HEROKU_POSTGRESQL_TEAL_URL, DATABASE_URL
Plan: Hobby-basic
Status: Available
Connections: 20/20
PG Version: 9.6.4
Created: 2017-01-01 00:00 UTC
Data Size: 99.9 MB
Tables: 99
Rows: 12345/10000000 (In compliance)
Fork/Follow: Unsupported
Rollback: Unsupported
Continuous Protection: Off
Add-on: postgresql-round-12345
'''
@pytest.mark.parametrize('cmd', ['pg'])
def test_match(cmd):
assert match(
Command('heroku {}'.format(cmd), suggest_output))
@pytest.mark.parametrize('script, output', [
('heroku pg', not_match_output)])
def test_not_match(script, output):
assert not match(Command(script, output))
@pytest.mark.parametrize('cmd, result', [
('pg', ['heroku pg --app myapp', 'heroku pg --app myapp-dev'])])
def test_get_new_command(cmd, result):
command = Command('heroku {}'.format(cmd), suggest_output)
assert get_new_command(command) == result

View File

@@ -1,3 +1,4 @@
import sys
from .conf import settings
from .types import Rule
from .system import Path
@@ -18,17 +19,33 @@ def get_loaded_rules(rules_paths):
yield rule
def get_rules_import_paths():
"""Yields all rules import paths.
:rtype: Iterable[Path]
"""
# Bundled rules:
yield Path(__file__).parent.joinpath('rules')
# Rules defined by user:
yield settings.user_dir.joinpath('rules')
# Packages with third-party rules:
for path in sys.path:
for contrib_module in Path(path).glob('thefuck_contrib_*'):
contrib_rules = contrib_module.joinpath('rules')
if contrib_rules.is_dir():
yield contrib_rules
def get_rules():
"""Returns all enabled rules.
:rtype: [Rule]
"""
bundled = Path(__file__).parent \
.joinpath('rules') \
.glob('*.py')
user = settings.user_dir.joinpath('rules').glob('*.py')
return sorted(get_loaded_rules(sorted(bundled) + sorted(user)),
paths = [rule_path for path in get_rules_import_paths()
for rule_path in sorted(path.glob('*.py'))]
return sorted(get_loaded_rules(paths),
key=lambda rule: rule.priority)

View File

@@ -10,7 +10,6 @@ from ..argument_parser import Parser # noqa: E402
from ..utils import get_installation_info # noqa: E402
from .alias import print_alias # noqa: E402
from .fix_command import fix_command # noqa: E402
from .shell_logger import shell_logger # noqa: E402
def main():
@@ -27,6 +26,11 @@ def main():
elif known_args.alias:
print_alias(known_args)
elif known_args.shell_logger:
shell_logger(known_args.shell_logger)
try:
from .shell_logger import shell_logger # noqa: E402
except ImportError:
logs.warn('Shell logger supports only Linux and macOS')
else:
shell_logger(known_args.shell_logger)
else:
parser.print_usage()

View File

@@ -0,0 +1,15 @@
from thefuck.specific.apt import apt_available
from thefuck.specific.sudo import sudo_support
from thefuck.utils import for_app
enabled_by_default = apt_available
@sudo_support
@for_app('apt')
def match(command):
return "Run 'apt list --upgradable' to see them." in command.output
def get_new_command(command):
return 'apt list --upgradable'

View File

@@ -15,7 +15,7 @@ def _get_formulas():
for file_name in os.listdir(brew_formula_path):
if file_name.endswith('.rb'):
yield file_name[:-3]
except:
except Exception:
pass

View File

@@ -8,7 +8,7 @@ def _is_bad_zip(file):
try:
with zipfile.ZipFile(file, 'r') as archive:
return len(archive.namelist()) > 1
except:
except Exception:
return False

View File

@@ -2,11 +2,12 @@ import re
from thefuck.specific.git import git_support
error_pattern = "fatal: bad flag '(.*?)' used after filename"
error_pattern2 = "fatal: option '(.*?)' must come before non-option arguments"
@git_support
def match(command):
return re.search(error_pattern, command.output)
return re.search(error_pattern, command.output) or re.search(error_pattern2, command.output)
@git_support
@@ -14,7 +15,7 @@ def get_new_command(command):
command_parts = command.script_parts[:]
# find the bad flag
bad_flag = re.search(error_pattern, command.output).group(1)
bad_flag = match(command).group(1)
bad_flag_index = command_parts.index(bad_flag)
# find the filename

View File

@@ -0,0 +1,12 @@
import re
from thefuck.utils import for_app
@for_app('heroku')
def match(command):
return 'https://devcenter.heroku.com/articles/multiple-environments' in command.output
def get_new_command(command):
apps = re.findall('([^ ]*) \([^)]*\)', command.output)
return [command.script + ' --app ' + app for app in apps]

View File

@@ -13,14 +13,16 @@ class Zsh(Generic):
# It is VERY important to have the variables declared WITHIN the function
return '''
{name} () {{
TF_HISTORY=$(fc -ln -10)
TF_PYTHONIOENCODING=$PYTHONIOENCODING;
export TF_ALIAS={name};
export TF_SHELL_ALIASES=$(alias);
export TF_HISTORY="$(fc -ln -10)";
export PYTHONIOENCODING=utf-8;
TF_CMD=$(
TF_ALIAS={name}
TF_SHELL_ALIASES=$(alias)
TF_HISTORY=$TF_HISTORY
PYTHONIOENCODING=utf-8
thefuck {argument_placeholder} $*
thefuck {argument_placeholder} $@
) && eval $TF_CMD;
unset TF_HISTORY;
export PYTHONIOENCODING=$TF_PYTHONIOENCODING;
{alter_history}
}}
'''.format(

View File

@@ -11,5 +11,5 @@ def get_brew_path_prefix():
try:
return subprocess.check_output(['brew', '--prefix'],
universal_newlines=True).strip()
except:
except Exception:
return None