1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-11-01 23:51:59 +00:00

Compare commits

..

16 Commits
1.48 ... 1.49

Author SHA1 Message Date
nvbn
78769e4fbc Bump to 1.49 2015-07-15 07:49:18 +03:00
nvbn
3e4c043ccc #280: Add debug output 2015-07-15 07:47:54 +03:00
nvbn
934099fe9e #289: Add is a directory pattern to cp_omitting_directory rule 2015-07-15 07:12:07 +03:00
Vladimir Iakovlev
464f86eccf Merge pull request #288 from scorphus/overridden-aliases
fix(fish.get_aliases): do not include overridden aliases
2015-07-15 06:58:32 +03:00
Pablo Santiago Blum de Aguiar
891fbe7ed1 fix(fish.get_aliases): do not include overridden aliases
Fish Shell overrides some shell commands, such as `cd` and `ls` and
therefore some rules fail to match. The following aliases are excluded
by default:

 * cd
 * grep
 * ls
 * man
 * open

To change them, one can use the `TF_OVERRIDDEN_ALIASES` environment
variable such as:

```
set TF_OVERRIDDEN_ALIASES 'cd,grep,ls'
```

Fix #262
2015-07-13 22:53:15 -03:00
nvbn
5abab8bd1e Merge branch 'master' of github.com:nvbn/thefuck 2015-07-10 17:58:53 +03:00
nvbn
7ebc8a38af #N/A Add history rule 2015-07-10 17:58:41 +03:00
nvbn
f40b63f44b #N/A Add ability to disable memoization in tests 2015-07-10 17:06:05 +03:00
nvbn
4b4e7acc0f #N/A Add ability to get shell history 2015-07-10 16:42:21 +03:00
Vladimir Iakovlev
a8587d3871 Merge pull request #285 from mcarton/tmux
Use `get_closest` in the tmux rule
2015-07-10 15:54:14 +03:00
mcarton
370c58e679 Use get_closest in the tmux rule 2015-07-10 09:49:49 +02:00
Vladimir Iakovlev
328e65179e Merge pull request #283 from mcarton/mercurial
Some fixes in REAME.md
2015-07-09 20:13:38 +03:00
Vladimir Iakovlev
63bb4da8e1 Merge pull request #282 from mcarton/sudo
Add systemd's kind of error for the sudo rule
2015-07-09 20:13:17 +03:00
mcarton
0b5a7a8e2d Fix rule name in README 2015-07-09 18:35:33 +02:00
mcarton
5693bd49f7 #281 Add the mercurial rule to README.md 2015-07-09 18:01:44 +02:00
mcarton
12f8d017b9 Add systemd's kind of error for the sudo rule
A complete error would be:

```
% systemctl daemon-reload
==== AUTHENTICATING FOR org.freedesktop.systemd1.reload-daemon ===
Authentication is required to reload the systemd state.
Authenticating as: martin
Password:
```
2015-07-09 17:24:45 +02:00
21 changed files with 277 additions and 58 deletions

View File

@@ -169,12 +169,14 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `go_run` – appends `.go` extension when compiling/running Go programs
* `grep_recursive` – adds `-r` when you trying to grep directory;
* `has_exists_script` – prepends `./` when script/binary exists;
* `history` – tries to replace command with most similar command from history;
* `java` – removes `.java` extension when running Java programs;
* `javac` – appends missing `.java` when compiling Java files;
* `lein_not_task` – fixes wrong `lein` tasks like `lein rpl`;
* `ls_lah` – adds -lah to ls;
* `man` – change manual section;
* `man_no_space` – fixes man commands without spaces, for example `mandiff`;
* `mercurial` – fixes wrong `hg` commands;
* `mkdir_p` – adds `-p` when you trying to create directory without parent;
* `no_command` – fixes wrong console commands, for example `vom/vim`;
* `no_such_file` – creates missing directories with `mv` and `cp` commands;
@@ -184,7 +186,7 @@ using the matched rule and runs it. Rules enabled by default are as follows:
* `python_execute` – appends missing `.py` when executing Python files;
* `quotation_marks` – fixes uneven usage of `'` and `"` when containing args'
* `rm_dir` – adds `-rf` when you trying to remove directory;
* `sed` – adds missing '/' to `sed`'s `s` commands;
* `sed_unterminated_s` – adds missing '/' to `sed`'s `s` commands;
* `sl_ls` – changes `sl` to `ls`;
* `ssh_known_hosts` – removes host from `known_hosts` on warning;
* `sudo` – prepends `sudo` to previous command if it failed because of permissions;
@@ -251,7 +253,8 @@ The Fuck has a few settings parameters which can be changed in `~/.thefuck/setti
* `require_confirmation` – requires confirmation before running new command, by default `False`;
* `wait_command` – max amount of time in seconds for getting previous command output;
* `no_colors` – disable colored output;
* `priority` – dict with rules priorities, rule with lower `priority` will be matched first.
* `priority` – dict with rules priorities, rule with lower `priority` will be matched first;
* `debug` – enabled debug output, by default `False`;
Example of `settings.py`:
@@ -261,6 +264,7 @@ require_confirmation = True
wait_command = 10
no_colors = False
priority = {'sudo': 100, 'no_command': 9999}
debug = False
```
Or via environment variables:
@@ -270,7 +274,8 @@ Or via environment variables:
* `THEFUCK_WAIT_COMMAND` – max amount of time in seconds for getting previous command output;
* `THEFUCK_NO_COLORS` – disable colored output, `true/false`;
* `THEFUCK_PRIORITY` – priority of the rules, like `no_command=9999:apt_get=100`,
rule with lower `priority` will be matched first.
rule with lower `priority` will be matched first;
* `THEFUCK_DEBUG` – enables debug output, `true/false`.
For example:

View File

@@ -11,7 +11,7 @@ elif (3, 0) < sys.version_info < (3, 3):
' ({}.{} detected).'.format(*sys.version_info[:2]))
sys.exit(-1)
VERSION = '1.48'
VERSION = '1.49'
install_requires = ['psutil', 'colorama', 'six']

6
tests/conftest.py Normal file
View File

@@ -0,0 +1,6 @@
import pytest
@pytest.fixture
def no_memoize(monkeypatch):
monkeypatch.setattr('thefuck.utils.memoize.disabled', True)

View File

@@ -1,14 +1,22 @@
from mock import Mock
import pytest
from thefuck.rules.cp_omitting_directory import match, get_new_command
from tests.utils import Command
def test_match():
assert match(Mock(script='cp dir', stderr="cp: omitting directory 'dir'"),
None)
assert not match(Mock(script='some dir',
stderr="cp: omitting directory 'dir'"), None)
assert not match(Mock(script='cp dir', stderr=""), None)
@pytest.mark.parametrize('script, stderr', [
('cp dir', 'cp: dor: is a directory'),
('cp dir', "cp: omitting directory 'dir'")])
def test_match(script, stderr):
assert match(Command(script, stderr=stderr), None)
@pytest.mark.parametrize('script, stderr', [
('some dir', 'cp: dor: is a directory'),
('some dir', "cp: omitting directory 'dir'"),
('cp dir', '')])
def test_not_match(script, stderr):
assert not match(Command(script, stderr=stderr), None)
def test_get_new_command():
assert get_new_command(Mock(script='cp dir'), None) == 'cp -a dir'
assert get_new_command(Command(script='cp dir'), None) == 'cp -a dir'

View File

@@ -0,0 +1,35 @@
import pytest
from thefuck.rules.history import match, get_new_command
from tests.utils import Command
@pytest.fixture
def history(mocker):
return mocker.patch('thefuck.rules.history.get_history',
return_value=['ls cat', 'diff x', 'nocommand x'])
@pytest.fixture
def callables(mocker):
return mocker.patch('thefuck.rules.history.get_all_callables',
return_value=['diff', 'ls'])
@pytest.mark.usefixtures('history', 'callables', 'no_memoize')
@pytest.mark.parametrize('script', ['ls cet', 'daff x'])
def test_match(script):
assert match(Command(script=script), None)
@pytest.mark.usefixtures('history', 'callables', 'no_memoize')
@pytest.mark.parametrize('script', ['apt-get', 'nocommand y'])
def test_not_match(script):
assert not match(Command(script=script), None)
@pytest.mark.usefixtures('history', 'callables', 'no_memoize')
@pytest.mark.parametrize('script, result', [
('ls cet', 'ls cat'),
('daff x', 'diff x')])
def test_get_new_command(script, result):
assert get_new_command(Command(script), None) == result

View File

@@ -1,36 +1,42 @@
from mock import patch, Mock
from thefuck.rules.no_command import match, get_new_command, _get_all_callables
import pytest
from thefuck.rules.no_command import match, get_new_command, get_all_callables
from tests.utils import Command
@patch('thefuck.rules.no_command._safe', return_value=[])
@patch('thefuck.rules.no_command.get_aliases',
return_value=['vim', 'apt-get', 'fsck', 'fuck'])
@pytest.fixture(autouse=True)
def _safe(mocker):
mocker.patch('thefuck.rules.no_command._safe', return_value=[])
@pytest.fixture(autouse=True)
def get_aliases(mocker):
mocker.patch('thefuck.rules.no_command.get_aliases',
return_value=['vim', 'apt-get', 'fsck', 'fuck'])
@pytest.mark.usefixtures('no_memoize')
def test_get_all_callables(*args):
all_callables = _get_all_callables()
all_callables = get_all_callables()
assert 'vim' in all_callables
assert 'fsck' in all_callables
assert 'fuck' not in all_callables
@patch('thefuck.rules.no_command._safe', return_value=[])
@patch('thefuck.rules.no_command.get_aliases',
return_value=['vim', 'apt-get', 'fsck', 'fuck'])
@pytest.mark.usefixtures('no_memoize')
def test_match(*args):
assert match(Mock(stderr='vom: not found', script='vom file.py'), None)
assert match(Mock(stderr='fucck: not found', script='fucck'), None)
assert not match(Mock(stderr='qweqwe: not found', script='qweqwe'), None)
assert not match(Mock(stderr='some text', script='vom file.py'), None)
assert match(Command(stderr='vom: not found', script='vom file.py'), None)
assert match(Command(stderr='fucck: not found', script='fucck'), None)
assert not match(Command(stderr='qweqwe: not found', script='qweqwe'), None)
assert not match(Command(stderr='some text', script='vom file.py'), None)
@patch('thefuck.rules.no_command._safe', return_value=[])
@patch('thefuck.rules.no_command.get_aliases',
return_value=['vim', 'apt-get', 'fsck', 'fuck'])
@pytest.mark.usefixtures('no_memoize')
def test_get_new_command(*args):
assert get_new_command(
Mock(stderr='vom: not found',
script='vom file.py'),
Command(stderr='vom: not found',
script='vom file.py'),
None) == 'vim file.py'
assert get_new_command(
Mock(stderr='fucck: not found',
script='fucck'),
None) == 'fsck'
Command(stderr='fucck: not found',
script='fucck'),
Command) == 'fsck'

View File

@@ -16,4 +16,4 @@ def test_match(tmux_ambiguous):
def test_get_new_command(tmux_ambiguous):
assert get_new_command(Command('tmux list', stderr=tmux_ambiguous), None)\
== 'tmux list-buffers'
== 'tmux list-keys'

View File

@@ -5,3 +5,10 @@ from thefuck import logs
def test_color():
assert logs.color('red', Mock(no_colors=False)) == 'red'
assert logs.color('red', Mock(no_colors=True)) == ''
def test_debug(capsys):
logs.debug('test', Mock(no_colors=True, debug=True))
assert capsys.readouterr() == ('', 'DEBUG: test\n')
logs.debug('test', Mock(no_colors=True, debug=False))
assert capsys.readouterr() == ('', '')

View File

@@ -110,7 +110,7 @@ class TestGetMatchedRule(object):
def test_when_rule_failed(self, capsys):
main.get_matched_rule(
Command('ls'), [Rule('test', Mock(side_effect=OSError('Denied')))],
Mock(no_colors=True))
Mock(no_colors=True, debug=False))
assert capsys.readouterr()[1].split('\n')[0] == '[WARN] Rule test:'
@@ -126,7 +126,7 @@ class TestRunRule(object):
def test_run_rule_with_side_effect(self, capsys):
side_effect = Mock()
settings = Mock()
settings = Mock(debug=False)
command = Command()
main.run_rule(Rule(get_new_command=lambda *_: 'new-command',
side_effect=side_effect),

View File

@@ -12,6 +12,16 @@ def isfile(mocker):
return mocker.patch('os.path.isfile', return_value=True)
@pytest.fixture
@pytest.mark.usefixtures('isfile')
def history_lines(mocker):
def aux(lines):
mock = mocker.patch('io.open')
mock.return_value.__enter__\
.return_value.__iter__.return_value = lines
return aux
class TestGeneric(object):
@pytest.fixture
def shell(self):
@@ -38,6 +48,12 @@ class TestGeneric(object):
assert 'thefuck' in shell.app_alias()
assert 'TF_ALIAS' in shell.app_alias()
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
# We don't know what to do in generic shell with history lines,
# so just ignore them:
assert list(shell.get_history()) == []
@pytest.mark.usefixtures('isfile')
class TestBash(object):
@@ -85,6 +101,10 @@ class TestBash(object):
assert 'thefuck' in shell.app_alias()
assert 'TF_ALIAS' in shell.app_alias()
def test_get_history(self, history_lines, shell):
history_lines(['ls', 'rm'])
assert list(shell.get_history()) == ['ls', 'rm']
@pytest.mark.usefixtures('isfile')
class TestFish(object):
@@ -96,18 +116,34 @@ class TestFish(object):
def Popen(self, mocker):
mock = mocker.patch('thefuck.shells.Popen')
mock.return_value.stdout.read.return_value = (
b'fish_config\nfuck\nfunced\nfuncsave\ngrep\nhistory\nll\nmath')
b'cd\nfish_config\nfuck\nfunced\nfuncsave\ngrep\nhistory\nll\nls\n'
b'man\nmath\npopd\npushd\nruby')
return mock
@pytest.fixture
def environ(self, monkeypatch):
data = {'TF_OVERRIDDEN_ALIASES': 'cd, ls, man, open'}
monkeypatch.setattr('thefuck.shells.os.environ', data)
return data
@pytest.mark.usefixture('environ')
def test_get_overridden_aliases(self, shell, environ):
assert shell._get_overridden_aliases() == ['cd', 'ls', 'man', 'open']
@pytest.mark.parametrize('before, after', [
('cd', 'cd'),
('pwd', 'pwd'),
('fuck', 'fish -ic "fuck"'),
('find', 'find'),
('funced', 'fish -ic "funced"'),
('grep', 'grep'),
('awk', 'awk'),
('math "2 + 2"', r'fish -ic "math \"2 + 2\""'),
('man', 'man'),
('open', 'open'),
('vim', 'vim'),
('ll', 'fish -ic "ll"')]) # Fish has no aliases but functions
('ll', 'fish -ic "ll"'),
('ls', 'ls')]) # Fish has no aliases but functions
def test_from_shell(self, before, after, shell):
assert shell.from_shell(before) == after
@@ -129,10 +165,12 @@ class TestFish(object):
'fuck': 'fuck',
'funced': 'funced',
'funcsave': 'funcsave',
'grep': 'grep',
'history': 'history',
'll': 'll',
'math': 'math'}
'math': 'math',
'popd': 'popd',
'pushd': 'pushd',
'ruby': 'ruby'}
def test_app_alias(self, shell):
assert 'function fuck' in shell.app_alias()
@@ -187,3 +225,7 @@ class TestZsh(object):
assert 'alias fuck' in shell.app_alias()
assert 'thefuck' in shell.app_alias()
assert 'TF_ALIAS' in shell.app_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

@@ -34,6 +34,15 @@ def test_memoize():
fn.assert_called_once_with()
@pytest.mark.usefixtures('no_memoize')
def test_no_memoize():
fn = Mock(__name__='fn')
memoized = memoize(fn)
memoized()
memoized()
assert fn.call_count == 2
class TestGetClosest(object):
def test_when_can_match(self):

View File

@@ -29,13 +29,15 @@ DEFAULT_SETTINGS = {'rules': DEFAULT_RULES,
'wait_command': 3,
'require_confirmation': False,
'no_colors': False,
'debug': False,
'priority': {}}
ENV_TO_ATTR = {'THEFUCK_RULES': 'rules',
'THEFUCK_WAIT_COMMAND': 'wait_command',
'THEFUCK_REQUIRE_CONFIRMATION': 'require_confirmation',
'THEFUCK_NO_COLORS': 'no_colors',
'THEFUCK_PRIORITY': 'priority'}
'THEFUCK_PRIORITY': 'priority',
'THEFUCK_DEBUG': 'debug'}
SETTINGS_HEADER = u"""# ~/.thefuck/settings.py: The Fuck settings file
@@ -87,7 +89,7 @@ def _val_from_env(env, attr):
return dict(_priority_from_env(val))
elif attr == 'wait_command':
return int(val)
elif attr in ('require_confirmation', 'no_colors'):
elif attr in ('require_confirmation', 'no_colors', 'debug'):
return val.lower() == 'true'
else:
return val

View File

@@ -1,3 +1,4 @@
from pprint import pformat
import sys
from traceback import format_exception
import colorama
@@ -52,3 +53,12 @@ def failed(msg, settings):
msg=msg,
red=color(colorama.Fore.RED, settings),
reset=color(colorama.Style.RESET_ALL, settings)))
def debug(msg, settings):
if settings.debug:
sys.stderr.write(u'{blue}{bold}DEBUG:{reset} {msg}\n'.format(
msg=msg,
reset=color(colorama.Style.RESET_ALL, settings),
blue=color(colorama.Fore.BLUE, settings),
bold=color(colorama.Style.BRIGHT, settings)))

View File

@@ -1,6 +1,7 @@
from imp import load_source
from pathlib import Path
from os.path import expanduser
from pprint import pformat
from subprocess import Popen, PIPE
import os
import sys
@@ -79,6 +80,7 @@ def get_command(settings, args):
return
script = shells.from_shell(script)
logs.debug('Call: {}'.format(script), settings)
result = Popen(script, shell=True, stdout=PIPE, stderr=PIPE,
env=dict(os.environ, LANG='C'))
if wait_output(settings, result):
@@ -90,6 +92,7 @@ def get_matched_rule(command, rules, settings):
"""Returns first matched rule for command."""
for rule in rules:
try:
logs.debug(u'Trying rule: {}'.format(rule.name), settings)
if rule.match(command, settings):
return rule
except Exception:
@@ -125,12 +128,21 @@ def main():
colorama.init()
user_dir = setup_user_dir()
settings = conf.get_settings(user_dir)
logs.debug('Run with settings: {}'.format(pformat(settings)), settings)
command = get_command(settings, sys.argv)
if command:
logs.debug('Received stdout: {}'.format(command.stdout), settings)
logs.debug('Received stderr: {}'.format(command.stderr), settings)
rules = get_rules(user_dir, settings)
logs.debug(
'Loaded rules: {}'.format(', '.join(rule.name for rule in rules)),
settings)
matched_rule = get_matched_rule(command, rules, settings)
if matched_rule:
logs.debug('Matched rule: {}'.format(matched_rule.name), settings)
run_rule(matched_rule, command, settings)
return

View File

@@ -4,8 +4,9 @@ from thefuck.utils import sudo_support
@sudo_support
def match(command, settings):
stderr = command.stderr.lower()
return command.script.startswith('cp ') \
and 'cp: omitting directory' in command.stderr.lower()
and ('omitting directory' in stderr or 'is a directory' in stderr)
@sudo_support

24
thefuck/rules/history.py Normal file
View File

@@ -0,0 +1,24 @@
from difflib import get_close_matches
from thefuck.shells import get_history
from thefuck.utils import get_closest, memoize
from thefuck.rules.no_command import get_all_callables
@memoize
def _history_of_exists_without_current(command):
callables = get_all_callables()
return [line for line in get_history()
if line != command.script
and line.split(' ')[0] in callables]
def match(command, settings):
return len(get_close_matches(command.script,
_history_of_exists_without_current(command)))
def get_new_command(command, settings):
return get_closest(command.script,
_history_of_exists_without_current(command))
priority = 9999

View File

@@ -1,7 +1,7 @@
from difflib import get_close_matches
import os
from pathlib import Path
from thefuck.utils import sudo_support
from thefuck.utils import sudo_support, memoize
from thefuck.shells import thefuck_alias, get_aliases
@@ -12,7 +12,8 @@ def _safe(fn, fallback):
return fallback
def _get_all_callables():
@memoize
def get_all_callables():
tf_alias = thefuck_alias()
return [exe.name
for path in os.environ.get('PATH', '').split(':')
@@ -25,14 +26,14 @@ def _get_all_callables():
def match(command, settings):
return 'not found' in command.stderr and \
bool(get_close_matches(command.script.split(' ')[0],
_get_all_callables()))
get_all_callables()))
@sudo_support
def get_new_command(command, settings):
old_command = command.script.split(' ')[0]
new_command = get_close_matches(old_command,
_get_all_callables())[0]
get_all_callables())[0]
return ' '.join([new_command] + command.script.split(' ')[1:])

View File

@@ -13,7 +13,8 @@ patterns = ['permission denied',
'must be root',
'need to be root',
'need root',
'only root can do that']
'only root can do that',
'authentication is required']
def match(command, settings):

View File

@@ -1,3 +1,4 @@
from thefuck.utils import get_closest
import re
@@ -8,7 +9,12 @@ def match(command, settings):
def get_new_command(command, settings):
cmd = re.match(r"ambiguous command: (.*), could be: ([^, \n]*)",
cmd = re.match(r"ambiguous command: (.*), could be: (.*)",
command.stderr)
return command.script.replace(cmd.group(1), cmd.group(2))
old_cmd = cmd.group(1)
suggestions = [cmd.strip() for cmd in cmd.group(2).split(',')]
new_cmd = get_closest(old_cmd, suggestions)
return command.script.replace(old_cmd, new_cmd)

View File

@@ -7,7 +7,9 @@ from collections import defaultdict
from subprocess import Popen, PIPE
from time import time
import os
import io
from psutil import Process
import six
from .utils import DEVNULL, memoize
@@ -48,6 +50,26 @@ class Generic(object):
with open(history_file_name, 'a') as history:
history.write(self._get_history_line(command_script))
def _script_from_history(self, line):
"""Returns prepared history line.
Should return a blank line if history line is corrupted or empty.
"""
return ''
def get_history(self):
"""Returns list of history entries."""
history_file_name = self._get_history_file_name()
if os.path.isfile(history_file_name):
with io.open(history_file_name, 'r',
encoding='utf-8', errors='ignore') as history:
for line in history:
prepared = self._script_from_history(line)\
.strip()
if prepared:
yield prepared
def and_(self, *commands):
return u' && '.join(commands)
@@ -63,7 +85,6 @@ class Bash(Generic):
value = value[1:-1]
return name, value
@memoize
def get_aliases(self):
proc = Popen('bash -ic alias', stdout=PIPE, stderr=DEVNULL,
shell=True)
@@ -79,12 +100,24 @@ class Bash(Generic):
def _get_history_line(self, command_script):
return u'{}\n'.format(command_script)
def _script_from_history(self, line):
print(line)
return line
class Fish(Generic):
def _get_overridden_aliases(self):
overridden_aliases = os.environ.get('TF_OVERRIDDEN_ALIASES', '').strip()
if overridden_aliases:
return [alias.strip() for alias in overridden_aliases.split(',')]
else:
return ['cd', 'grep', 'ls', 'man', 'open']
def app_alias(self):
return ("function fuck -d 'Correct your previous console command'\n"
return ("set TF_ALIAS fuck\n"
"function fuck -d 'Correct your previous console command'\n"
" set -l exit_code $status\n"
" set -l TF_ALIAS fuck\n"
" set -l eval_script"
" (mktemp 2>/dev/null ; or mktemp -t 'thefuck')\n"
" set -l fucked_up_commandd $history[1]\n"
@@ -96,12 +129,12 @@ class Fish(Generic):
" end\n"
"end")
@memoize
def get_aliases(self):
overridden = self._get_overridden_aliases()
proc = Popen('fish -ic functions', stdout=PIPE, stderr=DEVNULL,
shell=True)
functions = proc.stdout.read().decode('utf-8').strip().split('\n')
return {function: function for function in functions}
return {func: func for func in functions if func not in overridden}
def _expand_aliases(self, command_script):
aliases = self.get_aliases()
@@ -137,7 +170,6 @@ class Zsh(Generic):
value = value[1:-1]
return name, value
@memoize
def get_aliases(self):
proc = Popen('zsh -ic alias', stdout=PIPE, stderr=DEVNULL,
shell=True)
@@ -153,6 +185,12 @@ class Zsh(Generic):
def _get_history_line(self, command_script):
return u': {}:0;{}\n'.format(int(time()), command_script)
def _script_from_history(self, line):
if ';' in line:
return line.split(';', 1)[1]
else:
return ''
class Tcsh(Generic):
def app_alias(self):
@@ -162,7 +200,6 @@ class Tcsh(Generic):
name, value = alias.split("\t", 1)
return name, value
@memoize
def get_aliases(self):
proc = Popen('tcsh -ic alias', stdout=PIPE, stderr=DEVNULL,
shell=True)
@@ -219,5 +256,11 @@ def and_(*commands):
return _get_shell().and_(*commands)
@memoize
def get_aliases():
return list(_get_shell().get_aliases().keys())
@memoize
def get_history():
return list(_get_shell().get_history())

View File

@@ -80,12 +80,13 @@ def memoize(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
key = pickle.dumps((args, kwargs))
if key not in memo:
if key not in memo or memoize.disabled:
memo[key] = fn(*args, **kwargs)
return memo[key]
return wrapper
memoize.disabled = False
def get_closest(word, possibilities, n=3, cutoff=0.6):