1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-01-18 20:11:17 +00:00

#128 #69 add support of shell specific actions, add alias expansion for bash and zsh

This commit is contained in:
nvbn 2015-05-03 12:46:01 +02:00
parent 938f1df035
commit fcc2a1a40a
4 changed files with 141 additions and 2 deletions

View File

@ -50,6 +50,11 @@ class TestGetCommand(object):
monkeypatch.setattr('thefuck.main.os.environ', {})
monkeypatch.setattr('thefuck.main.wait_output', lambda *_: True)
@pytest.fixture(autouse=True)
def generic_shell(self, monkeypatch):
monkeypatch.setattr('thefuck.shells.from_shell', lambda x: x)
monkeypatch.setattr('thefuck.shells.to_shell', lambda x: x)
def test_get_command_calls(self, Popen):
assert main.get_command(Mock(), Mock(),
['thefuck', 'apt-get', 'search', 'vim']) \

53
tests/test_shells.py Normal file
View File

@ -0,0 +1,53 @@
import pytest
from mock import Mock
from thefuck import shells
class TestGeneric(object):
def test_from_shell(self):
assert shells.Generic().from_shell('pwd') == 'pwd'
def test_to_shell(self):
assert shells.Bash().to_shell('pwd') == 'pwd'
class TestBash(object):
@pytest.fixture(autouse=True)
def Popen(self, monkeypatch):
mock = Mock()
monkeypatch.setattr('thefuck.shells.Popen', mock)
mock.return_value.stdout.read.return_value = (
b'alias l=\'ls -CF\'\n'
b'alias la=\'ls -A\'\n'
b'alias ll=\'ls -alF\'')
return mock
@pytest.mark.parametrize('before, after', [
('pwd', 'pwd'),
('ll', 'ls -alF')])
def test_from_shell(self, before, after):
assert shells.Bash().from_shell(before) == after
def test_to_shell(self):
assert shells.Bash().to_shell('pwd') == 'pwd'
class TestZsh(object):
@pytest.fixture(autouse=True)
def Popen(self, monkeypatch):
mock = Mock()
monkeypatch.setattr('thefuck.shells.Popen', mock)
mock.return_value.stdout.read.return_value = (
b'l=\'ls -CF\'\n'
b'la=\'ls -A\'\n'
b'll=\'ls -alF\'')
return mock
@pytest.mark.parametrize('before, after', [
('pwd', 'pwd'),
('ll', 'ls -alF')])
def test_from_shell(self, before, after):
assert shells.Zsh().from_shell(before) == after
def test_to_shell(self):
assert shells.Zsh().to_shell('pwd') == 'pwd'

View File

@ -7,7 +7,7 @@ import sys
from psutil import Process, TimeoutExpired
import colorama
from .history import History
from . import logs, conf, types
from . import logs, conf, types, shells
def setup_user_dir():
@ -73,6 +73,7 @@ def get_command(settings, history, args):
if not script:
return
script = shells.from_shell(script)
history.update(last_command=script,
last_fixed_command=None)
result = Popen(script, shell=True, stdout=PIPE, stderr=PIPE,
@ -109,7 +110,7 @@ def confirm(new_command, side_effect, settings):
def run_rule(rule, command, history, settings):
"""Runs command from rule for passed command."""
new_command = rule.get_new_command(command, settings)
new_command = shells.to_shell(rule.get_new_command(command, settings))
if confirm(new_command, rule.side_effect, settings):
if rule.side_effect:
rule.side_effect(command, settings)

80
thefuck/shells.py Normal file
View File

@ -0,0 +1,80 @@
"""Module with shell specific actions, each shell class should
implement `from_shell` and `to_shell` methods.
"""
from collections import defaultdict
from subprocess import Popen, PIPE
import os
from psutil import Process
FNULL = open(os.devnull, 'w')
class Generic(object):
def _get_aliases(self):
return {}
def _expand_aliases(self, command_script):
aliases = self._get_aliases()
binary = command_script.split(' ')[0]
if binary in aliases:
return command_script.replace(binary, aliases[binary], 1)
else:
return command_script
def from_shell(self, command_script):
"""Prepares command before running in app."""
return self._expand_aliases(command_script)
def to_shell(self, command_script):
"""Prepares command for running in shell."""
return command_script
class Bash(Generic):
def _parse_alias(self, alias):
name, value = alias.replace('alias ', '', 1).split('=', 1)
if value[0] == value[-1] == '"' or value[0] == value[-1] == "'":
value = value[1:-1]
return name, value
def _get_aliases(self):
proc = Popen('bash -ic alias', stdout=PIPE, stderr=FNULL, shell=True)
return dict(
self._parse_alias(alias)
for alias in proc.stdout.read().decode('utf-8').split('\n')
if alias)
class Zsh(Generic):
def _parse_alias(self, alias):
name, value = alias.split('=', 1)
if value[0] == value[-1] == '"' or value[0] == value[-1] == "'":
value = value[1:-1]
return name, value
def _get_aliases(self):
proc = Popen('zsh -ic alias', stdout=PIPE, stderr=FNULL, shell=True)
return dict(
self._parse_alias(alias)
for alias in proc.stdout.read().decode('utf-8').split('\n')
if alias)
shells = defaultdict(lambda: Generic(), {
'bash': Bash(),
'zsh': Zsh()})
def _get_shell():
shell = Process(os.getpid()).parent().cmdline()[0]
return shells[shell]
def from_shell(command):
return _get_shell().from_shell(command)
def to_shell(command):
return _get_shell().to_shell(command)