1
0
mirror of https://github.com/nvbn/thefuck.git synced 2025-02-20 20:09:07 +00: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 thefuck import corrector, conf
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):
@ -111,3 +112,13 @@ def test_get_corrected_commands(mocker):
mocker.patch('thefuck.corrector.get_rules', return_value=rules)
assert [cmd.script for cmd in get_corrected_commands(command)] \
== ['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
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):
def test_equality(self):

View File

@ -1,10 +1,9 @@
# -*- encoding: utf-8 -*-
from mock import Mock
import pytest
from itertools import islice
from thefuck import ui
from thefuck.types import CorrectedCommand, SortedCorrectedCommandsSequence
from thefuck.types import CorrectedCommand
@pytest.fixture
@ -41,7 +40,7 @@ def test_read_actions(patch_getch):
def test_command_selector():
selector = ui.CommandSelector([1, 2, 3])
selector = ui.CommandSelector(iter([1, 2, 3]))
assert selector.value == 1
selector.next()
assert selector.value == 2
@ -57,51 +56,49 @@ def test_command_selector():
class TestSelectCommand(object):
@pytest.fixture
def commands_with_side_effect(self):
return SortedCorrectedCommandsSequence(
iter([CorrectedCommand('ls', lambda *_: None, 100),
CorrectedCommand('cd', lambda *_: None, 100)]))
return [CorrectedCommand('ls', lambda *_: None, 100),
CorrectedCommand('cd', lambda *_: None, 100)]
@pytest.fixture
def commands(self):
return SortedCorrectedCommandsSequence(
iter([CorrectedCommand('ls', None, 100),
CorrectedCommand('cd', None, 100)]))
return [CorrectedCommand('ls', None, 100),
CorrectedCommand('cd', None, 100)]
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')
def test_without_confirmation(self, capsys, commands, settings):
settings.require_confirmation = False
assert ui.select_command(commands) == commands[0]
assert ui.select_command(iter(commands)) == commands[0]
assert capsys.readouterr() == ('', 'ls\n')
def test_without_confirmation_with_side_effects(
self, capsys, commands_with_side_effect, settings):
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]
assert capsys.readouterr() == ('', 'ls (+side effect)\n')
def test_with_confirmation(self, capsys, patch_getch, commands):
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')
def test_with_confirmation_abort(self, capsys, patch_getch, commands):
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')
def test_with_confirmation_with_side_effct(self, capsys, patch_getch,
commands_with_side_effect):
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]
assert capsys.readouterr() == ('', u'\x1b[1K\rls (+side effect) [enter/↑/↓/ctrl+c]\n')
def test_with_confirmation_select_second(self, capsys, patch_getch, commands):
patch_getch(['\x1b', '[', 'B', '\n'])
assert ui.select_command(commands) == commands[1]
assert ui.select_command(iter(commands)) == commands[1]
assert capsys.readouterr() == (
'', 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 pathlib import Path
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 . import logs
@ -76,10 +76,33 @@ def make_corrected_commands(command, rule):
side_effect=rule.side_effect,
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):
corrected_commands = (
corrected for rule in get_rules()
if is_rule_match(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 traceback import format_stack
Command = namedtuple('Command', ('script', 'stdout', 'stderr'))
@ -36,62 +35,3 @@ class Settings(dict):
def __setattr__(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):
"""Helper for selecting rule from rules list."""
def __init__(self, commands):
self._commands = commands
self._commands_gen = commands
self._commands = [next(self._commands_gen)]
self._realised = False
self._index = 0
def _realise(self):
if not self._realised:
self._commands += list(self._commands_gen)
self._realised = True
def next(self):
self._realise()
self._index = (self._index + 1) % len(self._commands)
def previous(self):
self._realise()
self._index = (self._index - 1) % len(self._commands)
@property
@ -73,11 +84,12 @@ def select_command(corrected_commands):
- selected command.
"""
if not corrected_commands:
try:
selector = CommandSelector(corrected_commands)
except StopIteration:
logs.failed('No fucks given')
return
selector = CommandSelector(corrected_commands)
if not settings.require_confirmation:
logs.show_corrected_command(selector.value)
return selector.value