1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-11-17 07:16:04 +00:00

Compare commits

...

11 Commits
3.4 ... 3.6

Author SHA1 Message Date
nvbn
39f7cc37eb Bump to 3.6 2016-03-13 17:46:14 +03:00
nvbn
251b69b5a0 #475: Try to use already used executable in no_command 2016-03-13 15:10:37 +03:00
nvbn
8b1f078e27 Bump to 3.5 2016-03-13 03:14:18 +03:00
Vladimir Iakovlev
a47e84fa6b Merge pull request #476 from scorphus/git-help-aliased
#N/A: Add new `git_help_aliased` rule
2016-03-13 01:24:57 +03:00
Pablo Santiago Blum de Aguiar
bcab700215 #N/A: Add new git_help_aliased rule 2016-03-12 18:51:47 -03:00
Vladimir Iakovlev
4fb99fd7a8 Merge pull request #474 from scorphus/alias-variables
#301: Set variables within the alias
2016-03-10 13:25:50 +03:00
Pablo Santiago Blum de Aguiar
bb5f6bb705 #301: Set variables within the alias
Fix #301
2016-03-09 21:58:18 -03:00
Vladimir Iakovlev
d92765d5df Merge pull request #473 from scorphus/gdbm-unavailable
#439 & #447: Remove cache if created with unavailable db
2016-03-10 02:52:33 +03:00
Pablo Santiago Blum de Aguiar
d8de5cfd20 #439 & #447: Remove cache if created with unavailable db
When switching between Python versions, the database package used to
create the cache might be unavailable and an ImportError is raised,
such as `ImportError: No module named gdbm`.
2016-03-08 14:38:16 -03:00
Vladimir Iakovlev
d73b14ce4b Merge pull request #472 from PLNech/master
Rules: git remote add instead of set-url if remote does not exist
2016-03-06 13:33:14 +03:00
Paul-Louis NECH
04b83cf7e8 Rules: git remote add instead of set-url if remote does not exist
Tests: Added test for git_remote_seturl_add

Rules: renamed new rule with a more appropriate name

README: added new rule

Style: Formatting

New rule: corrected test name

Developed tests
2016-03-04 22:35:55 +01:00
18 changed files with 213 additions and 71 deletions

View File

@@ -161,11 +161,13 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `git_checkout` – fixes branch name or creates new branch;
* `git_diff_staged` – adds `--staged` to previous `git diff` with unexpected output;
* `git_fix_stash` – fixes `git stash` commands (misspelled subcommand and missing `save`);
* `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_push` &ndash; adds `--set-upstream origin $branch` to previous failed `git push`;
* `git_push_pull` &ndash; runs `git pull` when `push` was rejected;
* `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_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;

View File

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

View File

@@ -0,0 +1,24 @@
import pytest
from thefuck.rules.git_help_aliased import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('script, stdout', [
('git help st', "`git st' is aliased to `status'"),
('git help ds', "`git ds' is aliased to `diff --staged'")])
def test_match(script, stdout):
assert match(Command(script=script, stdout=stdout))
@pytest.mark.parametrize('script, stdout', [
('git help status', "GIT-STATUS(1)...Git Manual...GIT-STATUS(1)"),
('git help diff', "GIT-DIFF(1)...Git Manual...GIT-DIFF(1)")])
def test_not_match(script, stdout):
assert not match(Command(script=script, stdout=stdout))
@pytest.mark.parametrize('script, stdout, new_command', [
('git help st', "`git st' is aliased to `status'", 'git help status'),
('git help ds', "`git ds' is aliased to `diff --staged'", 'git help diff')])
def test_get_new_command(script, stdout, new_command):
assert get_new_command(Command(script=script, stdout=stdout)) == new_command

View File

@@ -0,0 +1,26 @@
import pytest
from thefuck.rules.git_remote_seturl_add import match, get_new_command
from tests.utils import Command
@pytest.mark.parametrize('command', [
Command(script='git remote set-url origin url', stderr="fatal: No such remote")])
def test_match(command):
assert match(command)
@pytest.mark.parametrize('command', [
Command('git remote set-url origin url', stderr=""),
Command('git remote add origin url'),
Command('git remote remove origin'),
Command('git remote prune origin'),
Command('git remote set-branches origin branch')
])
def test_not_match(command):
assert not match(command)
@pytest.mark.parametrize('command, new_command', [
(Command('git remote set-url origin git@github.com:nvbn/thefuck.git'),
'git remote add origin git@github.com:nvbn/thefuck.git')])
def test_get_new_command(command, new_command):
assert get_new_command(command) == new_command

View File

@@ -3,38 +3,23 @@ from thefuck.rules.history import match, get_new_command
from tests.utils import Command
@pytest.fixture
def history(mocker):
return mocker.patch('thefuck.shells.shell.get_history',
return_value=['le cat', 'fuck', 'ls cat',
'diff x', 'nocommand x'])
@pytest.fixture(autouse=True)
def history_without_current(mocker):
return mocker.patch(
'thefuck.rules.history.get_valid_history_without_current',
return_value=['ls cat', 'diff x'])
@pytest.fixture
def alias(mocker):
return mocker.patch('thefuck.rules.history.get_alias',
return_value='fuck')
@pytest.fixture
def callables(mocker):
return mocker.patch('thefuck.rules.history.get_all_executables',
return_value=['diff', 'ls'])
@pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias')
@pytest.mark.parametrize('script', ['ls cet', 'daff x'])
def test_match(script):
assert match(Command(script=script))
@pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias')
@pytest.mark.parametrize('script', ['apt-get', 'nocommand y'])
def test_not_match(script):
assert not match(Command(script=script))
@pytest.mark.usefixtures('history', 'callables', 'no_memoize', 'alias')
@pytest.mark.parametrize('script, result', [
('ls cet', 'ls cat'),
('daff x', 'diff x')])

View File

@@ -6,22 +6,37 @@ from tests.utils import Command
@pytest.fixture(autouse=True)
def get_all_executables(mocker):
mocker.patch('thefuck.rules.no_command.get_all_executables',
return_value=['vim', 'apt-get', 'fsck'])
return_value=['vim', 'fsck', 'git', 'go'])
@pytest.fixture(autouse=True)
def history_without_current(mocker):
return mocker.patch(
'thefuck.rules.no_command.get_valid_history_without_current',
return_value=['git commit'])
@pytest.mark.usefixtures('no_memoize')
def test_match():
assert match(Command(stderr='vom: not found', script='vom file.py'))
assert match(Command(stderr='fucck: not found', script='fucck'))
assert not match(Command(stderr='qweqwe: not found', script='qweqwe'))
assert not match(Command(stderr='some text', script='vom file.py'))
@pytest.mark.parametrize('script, stderr', [
('vom file.py', 'vom: not found'),
('fucck', 'fucck: not found'),
('got commit', 'got: command not found')])
def test_match(script, stderr):
assert match(Command(script, stderr=stderr))
@pytest.mark.usefixtures('no_memoize')
def test_get_new_command():
assert get_new_command(
Command(stderr='vom: not found',
script='vom file.py')) == ['vim file.py']
assert get_new_command(
Command(stderr='fucck: not found',
script='fucck')) == ['fsck']
@pytest.mark.parametrize('script, stderr', [
('qweqwe', 'qweqwe: not found'),
('vom file.py', 'some text')])
def test_not_match(script, stderr):
assert not match(Command(script, stderr=stderr))
@pytest.mark.usefixtures('no_memoize')
@pytest.mark.parametrize('script, result', [
('vom file.py', ['vim file.py']),
('fucck', ['fsck']),
('got commit', ['git commit', 'go commit'])])
def test_get_new_command(script, result):
assert get_new_command(Command(script)) == result

View File

@@ -46,6 +46,13 @@ class TestBash(object):
assert 'TF_ALIAS=fuck' in shell.app_alias('fuck')
assert 'PYTHONIOENCODING=utf-8' in shell.app_alias('fuck')
def test_app_alias_variables_correctly_set(self, shell):
alias = shell.app_alias('fuck')
assert "alias fuck='TF_CMD=$(TF_ALIAS" in alias
assert '$(TF_ALIAS=fuck PYTHONIOENCODING' in alias
assert 'PYTHONIOENCODING=utf-8 TF_SHELL_ALIASES' in alias
assert 'ALIASES=$(alias) thefuck' in alias
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
assert list(shell.get_history()) == ['ls', 'rm']

View File

@@ -45,6 +45,13 @@ class TestZsh(object):
assert 'thefuck' in shell.app_alias('fuck')
assert 'PYTHONIOENCODING' in shell.app_alias('fuck')
def test_app_alias_variables_correctly_set(self, shell):
alias = shell.app_alias('fuck')
assert "alias fuck='TF_CMD=$(TF_ALIAS" in alias
assert '$(TF_ALIAS=fuck PYTHONIOENCODING' in alias
assert 'PYTHONIOENCODING=utf-8 TF_SHELL_ALIASES' in alias
assert 'ALIASES=$(alias) thefuck' in alias
def test_get_history(self, history_lines, shell):
history_lines([': 1432613911:0;ls', ': 1432613916:0;rm'])
assert list(shell.get_history()) == ['ls', 'rm']

View File

@@ -3,7 +3,8 @@ 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, compatibility_call, \
get_valid_history_without_current
from tests.utils import Command
@@ -229,3 +230,29 @@ class TestCompatibilityCall(object):
return True
assert compatibility_call(side_effect, Command(), Command())
class TestGetValidHistoryWithoutCurrent(object):
@pytest.fixture(autouse=True)
def history(self, mocker):
return mocker.patch('thefuck.shells.shell.get_history',
return_value=['le cat', 'fuck', 'ls cat',
'diff x', 'nocommand x'])
@pytest.fixture(autouse=True)
def alias(self, mocker):
return mocker.patch('thefuck.utils.get_alias',
return_value='fuck')
@pytest.fixture(autouse=True)
def callables(self, mocker):
return mocker.patch('thefuck.utils.get_all_executables',
return_value=['diff', 'ls'])
@pytest.mark.parametrize('script, result', [
('le cat', ['ls cat', 'diff x']),
('diff x', ['ls cat']),
('fuck', ['ls cat', 'diff x'])])
def test_get_valid_history_without_current(self, script, result):
command = Command(script=script)
assert get_valid_history_without_current(command) == result

View File

@@ -0,0 +1,12 @@
from thefuck.specific.git import git_support
@git_support
def match(command):
return 'help' in command.script and ' is aliased to ' in command.stdout
@git_support
def get_new_command(command):
aliased = command.stdout.split('`', 2)[2].split("'", 1)[0].split(' ', 1)[0]
return 'git help {}'.format(aliased)

View File

@@ -0,0 +1,14 @@
from thefuck.utils import replace_argument
from thefuck.specific.git import git_support
@git_support
def match(command):
return ('set-url' in command.script and 'fatal: No such remote' in command.stderr)
def get_new_command(command):
return replace_argument(command.script, 'set-url', 'add')
enabled_by_default = True

View File

@@ -1,37 +1,15 @@
from difflib import get_close_matches
from thefuck.shells import shell
from thefuck.utils import get_closest, memoize, get_all_executables, get_alias
def _not_corrected(history, tf_alias):
"""Returns all lines from history except that comes before `fuck`."""
previous = None
for line in history:
if previous is not None and line != tf_alias:
yield previous
previous = line
if history:
yield history[-1]
@memoize
def _history_of_exists_without_current(command):
history = shell.get_history()
tf_alias = get_alias()
executables = get_all_executables()
return [line for line in _not_corrected(history, tf_alias)
if not line.startswith(tf_alias) and not line == command.script
and line.split(' ')[0] in executables]
from thefuck.utils import get_closest, get_valid_history_without_current
def match(command):
return len(get_close_matches(command.script,
_history_of_exists_without_current(command)))
get_valid_history_without_current(command)))
def get_new_command(command):
return get_closest(command.script,
_history_of_exists_without_current(command))
get_valid_history_without_current(command))
priority = 9999

View File

@@ -1,5 +1,6 @@
from difflib import get_close_matches
from thefuck.utils import get_all_executables
from thefuck.utils import get_all_executables, \
get_valid_history_without_current, get_closest
from thefuck.specific.sudo import sudo_support
@@ -11,10 +12,29 @@ def match(command):
get_all_executables())))
def _get_used_executables(command):
for script in get_valid_history_without_current(command):
yield script.split(' ')[0]
@sudo_support
def get_new_command(command):
old_command = command.script_parts[0]
new_cmds = get_close_matches(old_command, get_all_executables(), cutoff=0.1)
# One from history:
already_used = get_closest(
old_command, _get_used_executables(command),
fallback_to_first=False)
if already_used:
new_cmds = [already_used]
else:
new_cmds = []
# Other from all executables:
new_cmds += [cmd for cmd in get_close_matches(old_command,
get_all_executables())
if cmd not in new_cmds]
return [' '.join([new_command] + command.script_parts[1:])
for new_command in new_cmds]

View File

@@ -6,9 +6,11 @@ from .generic import Generic
class Bash(Generic):
def app_alias(self, fuck):
alias = "TF_ALIAS={0}" \
" alias {0}='PYTHONIOENCODING=utf-8" \
" TF_CMD=$(TF_SHELL_ALIASES=$(alias) thefuck $(fc -ln -1)) && " \
# It is VERY important to have the variables declared WITHIN the alias
alias = "alias {0}='TF_CMD=$(TF_ALIAS={0}" \
" PYTHONIOENCODING=utf-8" \
" TF_SHELL_ALIASES=$(alias)" \
" thefuck $(fc -ln -1)) &&" \
" eval $TF_CMD".format(fuck)
if settings.alter_history:

View File

@@ -14,6 +14,7 @@ class Fish(Generic):
return ['cd', 'grep', 'ls', 'man', 'open']
def app_alias(self, fuck):
# It is VERY important to have the variables declared WITHIN the alias
return ('function {0} -d "Correct your previous console command"\n'
' set -l fucked_up_command $history[1]\n'
' env TF_ALIAS={0} PYTHONIOENCODING=utf-8'

View File

@@ -7,10 +7,11 @@ from .generic import Generic
class Zsh(Generic):
def app_alias(self, alias_name):
alias = "alias {0}='TF_ALIAS={0}" \
# It is VERY important to have the variables declared WITHIN the alias
alias = "alias {0}='TF_CMD=$(TF_ALIAS={0}" \
" PYTHONIOENCODING=utf-8" \
' TF_SHELL_ALIASES=$(alias)' \
" TF_CMD=$(thefuck $(fc -ln -1 | tail -n 1)) &&" \
" TF_SHELL_ALIASES=$(alias)" \
" thefuck $(fc -ln -1 | tail -n 1)) &&" \
" eval $TF_CMD".format(alias_name)
if settings.alter_history:

View File

@@ -282,5 +282,5 @@ class CorrectedCommand(object):
compatibility_call(self.side_effect, old_cmd, self.script)
# This depends on correct setting of PYTHONIOENCODING by the alias:
logs.debug(u'PYTHONIOENCODING: {}'.format(
os.environ.get('PYTHONIOENCODING', '>-not-set-<')))
os.environ.get('PYTHONIOENCODING', '!!not-set!!')))
print(self.script)

View File

@@ -228,8 +228,8 @@ def cache(*depends_on):
value = fn(*args, **kwargs)
db[key] = {'etag': etag, 'value': value}
return value
except shelve_open_error:
# Caused when going from Python 2 to Python 3 and vice-versa
except (shelve_open_error, ImportError):
# Caused when switching between Python versions
warn("Removing possibly out-dated cache")
os.remove(cache_path)
@@ -269,3 +269,24 @@ def get_installation_info():
def get_alias():
return os.environ.get('TF_ALIAS', 'fuck')
@memoize
def get_valid_history_without_current(command):
def _not_corrected(history, tf_alias):
"""Returns all lines from history except that comes before `fuck`."""
previous = None
for line in history:
if previous is not None and line != tf_alias:
yield previous
previous = line
if history:
yield history[-1]
from thefuck.shells import shell
history = shell.get_history()
tf_alias = get_alias()
executables = get_all_executables()
return [line for line in _not_corrected(history, tf_alias)
if not line.startswith(tf_alias) and not line == command.script
and line.split(' ')[0] in executables]