1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-04-13 06:10:46 +01:00

Remove obscure SortedCorrectedCommandsSequence

This commit is contained in:
nvbn 2015-09-08 12:52:10 +03:00
parent 1fb6dd925b
commit bf80d97062
6 changed files with 65 additions and 113 deletions

View File

@ -3,7 +3,8 @@ from pathlib import PosixPath, Path
from mock import Mock from mock import Mock
from thefuck import corrector, conf from thefuck import corrector, conf
from tests.utils import Rule, Command, CorrectedCommand from tests.utils import Rule, Command, CorrectedCommand
from thefuck.corrector import make_corrected_commands, get_corrected_commands, is_rule_enabled from thefuck.corrector import make_corrected_commands, get_corrected_commands,\
is_rule_enabled, organize_commands
def test_load_rule(mocker): def test_load_rule(mocker):
@ -111,3 +112,13 @@ def test_get_corrected_commands(mocker):
mocker.patch('thefuck.corrector.get_rules', return_value=rules) mocker.patch('thefuck.corrector.get_rules', return_value=rules)
assert [cmd.script for cmd in get_corrected_commands(command)] \ assert [cmd.script for cmd in get_corrected_commands(command)] \
== ['test!', 'test@', 'test;'] == ['test!', 'test@', 'test;']
def test_organize_commands():
"""Ensures that the function removes duplicates and sorts commands."""
commands = [CorrectedCommand('ls'), CorrectedCommand('ls -la', priority=9000),
CorrectedCommand('ls -lh', priority=100),
CorrectedCommand('ls -lh', priority=9999)]
assert list(organize_commands(iter(commands))) \
== [CorrectedCommand('ls'), CorrectedCommand('ls -lh', priority=100),
CorrectedCommand('ls -la', priority=9000)]

View File

@ -1,37 +1,6 @@
from thefuck.types import SortedCorrectedCommandsSequence
from tests.utils import CorrectedCommand from tests.utils import CorrectedCommand
class TestSortedCorrectedCommandsSequence(object):
def test_realises_generator_only_on_demand(self, settings):
should_realise = False
def gen():
yield CorrectedCommand('git commit')
yield CorrectedCommand('git branch', priority=200)
assert should_realise
yield CorrectedCommand('git checkout', priority=100)
commands = SortedCorrectedCommandsSequence(gen())
assert commands[0] == CorrectedCommand('git commit')
should_realise = True
assert commands[1] == CorrectedCommand('git checkout', priority=100)
assert commands[2] == CorrectedCommand('git branch', priority=200)
def test_remove_duplicates(self):
side_effect = lambda *_: None
seq = SortedCorrectedCommandsSequence(
iter([CorrectedCommand('ls', priority=100),
CorrectedCommand('ls', priority=200),
CorrectedCommand('ls', side_effect, 300)]))
assert set(seq) == {CorrectedCommand('ls', priority=100),
CorrectedCommand('ls', side_effect, 300)}
def test_with_blank(self):
seq = SortedCorrectedCommandsSequence(iter([]))
assert list(seq) == []
class TestCorrectedCommand(object): class TestCorrectedCommand(object):
def test_equality(self): def test_equality(self):

View File

@ -1,10 +1,9 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
from mock import Mock
import pytest import pytest
from itertools import islice from itertools import islice
from thefuck import ui from thefuck import ui
from thefuck.types import CorrectedCommand, SortedCorrectedCommandsSequence from thefuck.types import CorrectedCommand
@pytest.fixture @pytest.fixture
@ -41,7 +40,7 @@ def test_read_actions(patch_getch):
def test_command_selector(): def test_command_selector():
selector = ui.CommandSelector([1, 2, 3]) selector = ui.CommandSelector(iter([1, 2, 3]))
assert selector.value == 1 assert selector.value == 1
selector.next() selector.next()
assert selector.value == 2 assert selector.value == 2
@ -57,51 +56,49 @@ def test_command_selector():
class TestSelectCommand(object): class TestSelectCommand(object):
@pytest.fixture @pytest.fixture
def commands_with_side_effect(self): def commands_with_side_effect(self):
return SortedCorrectedCommandsSequence( return [CorrectedCommand('ls', lambda *_: None, 100),
iter([CorrectedCommand('ls', lambda *_: None, 100), CorrectedCommand('cd', lambda *_: None, 100)]
CorrectedCommand('cd', lambda *_: None, 100)]))
@pytest.fixture @pytest.fixture
def commands(self): def commands(self):
return SortedCorrectedCommandsSequence( return [CorrectedCommand('ls', None, 100),
iter([CorrectedCommand('ls', None, 100), CorrectedCommand('cd', None, 100)]
CorrectedCommand('cd', None, 100)]))
def test_without_commands(self, capsys): def test_without_commands(self, capsys):
assert ui.select_command([]) is None assert ui.select_command(iter([])) is None
assert capsys.readouterr() == ('', 'No fucks given\n') assert capsys.readouterr() == ('', 'No fucks given\n')
def test_without_confirmation(self, capsys, commands, settings): def test_without_confirmation(self, capsys, commands, settings):
settings.require_confirmation = False settings.require_confirmation = False
assert ui.select_command(commands) == commands[0] assert ui.select_command(iter(commands)) == commands[0]
assert capsys.readouterr() == ('', 'ls\n') assert capsys.readouterr() == ('', 'ls\n')
def test_without_confirmation_with_side_effects( def test_without_confirmation_with_side_effects(
self, capsys, commands_with_side_effect, settings): self, capsys, commands_with_side_effect, settings):
settings.require_confirmation = False settings.require_confirmation = False
assert ui.select_command(commands_with_side_effect) \ assert ui.select_command(iter(commands_with_side_effect)) \
== commands_with_side_effect[0] == commands_with_side_effect[0]
assert capsys.readouterr() == ('', 'ls (+side effect)\n') assert capsys.readouterr() == ('', 'ls (+side effect)\n')
def test_with_confirmation(self, capsys, patch_getch, commands): def test_with_confirmation(self, capsys, patch_getch, commands):
patch_getch(['\n']) patch_getch(['\n'])
assert ui.select_command(commands) == commands[0] assert ui.select_command(iter(commands)) == commands[0]
assert capsys.readouterr() == ('', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\n') assert capsys.readouterr() == ('', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\n')
def test_with_confirmation_abort(self, capsys, patch_getch, commands): def test_with_confirmation_abort(self, capsys, patch_getch, commands):
patch_getch([KeyboardInterrupt]) patch_getch([KeyboardInterrupt])
assert ui.select_command(commands) is None assert ui.select_command(iter(commands)) is None
assert capsys.readouterr() == ('', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\nAborted\n') assert capsys.readouterr() == ('', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\nAborted\n')
def test_with_confirmation_with_side_effct(self, capsys, patch_getch, def test_with_confirmation_with_side_effct(self, capsys, patch_getch,
commands_with_side_effect): commands_with_side_effect):
patch_getch(['\n']) patch_getch(['\n'])
assert ui.select_command(commands_with_side_effect)\ assert ui.select_command(iter(commands_with_side_effect))\
== commands_with_side_effect[0] == commands_with_side_effect[0]
assert capsys.readouterr() == ('', u'\x1b[1K\rls (+side effect) [enter/↑/↓/ctrl+c]\n') assert capsys.readouterr() == ('', u'\x1b[1K\rls (+side effect) [enter/↑/↓/ctrl+c]\n')
def test_with_confirmation_select_second(self, capsys, patch_getch, commands): def test_with_confirmation_select_second(self, capsys, patch_getch, commands):
patch_getch(['\x1b', '[', 'B', '\n']) patch_getch(['\x1b', '[', 'B', '\n'])
assert ui.select_command(commands) == commands[1] assert ui.select_command(iter(commands)) == commands[1]
assert capsys.readouterr() == ( assert capsys.readouterr() == (
'', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\x1b[1K\rcd [enter/↑/↓/ctrl+c]\n') '', u'\x1b[1K\rls [enter/↑/↓/ctrl+c]\x1b[1K\rcd [enter/↑/↓/ctrl+c]\n')

View File

@ -2,7 +2,7 @@ import sys
from imp import load_source from imp import load_source
from pathlib import Path from pathlib import Path
from .conf import settings, DEFAULT_PRIORITY, ALL_ENABLED from .conf import settings, DEFAULT_PRIORITY, ALL_ENABLED
from .types import Rule, CorrectedCommand, SortedCorrectedCommandsSequence from .types import Rule, CorrectedCommand
from .utils import compatibility_call from .utils import compatibility_call
from . import logs from . import logs
@ -76,10 +76,33 @@ def make_corrected_commands(command, rule):
side_effect=rule.side_effect, side_effect=rule.side_effect,
priority=(n + 1) * rule.priority) priority=(n + 1) * rule.priority)
def organize_commands(corrected_commands):
"""Yields sorted commands without duplicates."""
try:
first_command = next(corrected_commands)
yield first_command
except StopIteration:
return
without_duplicates = {
command for command in sorted(
corrected_commands, key=lambda command: command.priority)
if command != first_command}
sorted_commands = sorted(
without_duplicates,
key=lambda corrected_command: corrected_command.priority)
logs.debug('Corrected commands: '.format(
', '.join(str(cmd) for cmd in [first_command] + sorted_commands)))
for command in sorted_commands:
yield command
def get_corrected_commands(command): def get_corrected_commands(command):
corrected_commands = ( corrected_commands = (
corrected for rule in get_rules() corrected for rule in get_rules()
if is_rule_match(command, rule) if is_rule_match(command, rule)
for corrected in make_corrected_commands(command, rule)) for corrected in make_corrected_commands(command, rule))
return SortedCorrectedCommandsSequence(corrected_commands) return organize_commands(corrected_commands)

View File

@ -1,5 +1,4 @@
from collections import namedtuple from collections import namedtuple
from traceback import format_stack
Command = namedtuple('Command', ('script', 'stdout', 'stderr')) Command = namedtuple('Command', ('script', 'stdout', 'stderr'))
@ -36,62 +35,3 @@ class Settings(dict):
def __setattr__(self, key, value): def __setattr__(self, key, value):
self[key] = value self[key] = value
class SortedCorrectedCommandsSequence(object):
"""List-like collection/wrapper around generator, that:
- immediately gives access to the first commands through [];
- realises generator and sorts commands on first access to other
commands through [], or when len called.
"""
def __init__(self, commands):
self._commands = commands
self._cached = self._realise_first()
self._realised = False
def _realise_first(self):
try:
return [next(self._commands)]
except StopIteration:
return []
def _remove_duplicates(self, corrected_commands):
"""Removes low-priority duplicates."""
commands = {command
for command in sorted(corrected_commands,
key=lambda command: -command.priority)
if command.script != self._cached[0]}
return commands
def _realise(self):
"""Realises generator, removes duplicates and sorts commands."""
from .logs import debug
if self._cached:
commands = self._remove_duplicates(self._commands)
self._cached = [self._cached[0]] + sorted(
commands, key=lambda corrected_command: corrected_command.priority)
self._realised = True
debug('SortedCommandsSequence was realised with: {}, after: {}'.format(
self._cached, '\n'.join(format_stack())))
def __getitem__(self, item):
if item != 0 and not self._realised:
self._realise()
return self._cached[item]
def __bool__(self):
return bool(self._cached)
def __len__(self):
if not self._realised:
self._realise()
return len(self._cached)
def __iter__(self):
if not self._realised:
self._realise()
return iter(self._cached)

View File

@ -50,14 +50,25 @@ def read_actions():
class CommandSelector(object): class CommandSelector(object):
"""Helper for selecting rule from rules list."""
def __init__(self, commands): def __init__(self, commands):
self._commands = commands self._commands_gen = commands
self._commands = [next(self._commands_gen)]
self._realised = False
self._index = 0 self._index = 0
def _realise(self):
if not self._realised:
self._commands += list(self._commands_gen)
self._realised = True
def next(self): def next(self):
self._realise()
self._index = (self._index + 1) % len(self._commands) self._index = (self._index + 1) % len(self._commands)
def previous(self): def previous(self):
self._realise()
self._index = (self._index - 1) % len(self._commands) self._index = (self._index - 1) % len(self._commands)
@property @property
@ -73,11 +84,12 @@ def select_command(corrected_commands):
- selected command. - selected command.
""" """
if not corrected_commands: try:
selector = CommandSelector(corrected_commands)
except StopIteration:
logs.failed('No fucks given') logs.failed('No fucks given')
return return
selector = CommandSelector(corrected_commands)
if not settings.require_confirmation: if not settings.require_confirmation:
logs.show_corrected_command(selector.value) logs.show_corrected_command(selector.value)
return selector.value return selector.value