mirror of
				https://github.com/nvbn/thefuck.git
				synced 2025-10-30 22:54:14 +00:00 
			
		
		
		
	Remove obscure SortedCorrectedCommandsSequence
				
					
				
			This commit is contained in:
		| @@ -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)] | ||||||
|   | |||||||
| @@ -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): | ||||||
|   | |||||||
| @@ -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') | ||||||
|   | |||||||
| @@ -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) | ||||||
|   | |||||||
| @@ -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) |  | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user