mirror of
				https://github.com/nvbn/thefuck.git
				synced 2025-10-31 15:12:20 +00:00 
			
		
		
		
	Add excluded_search_path_prefixes setting (#1165)
Improves performance in WSL Fix #1036 * Add excluded_search_path_prefixes setting Allows filtering the paths used to search for commands Can be useful to filter out /mnt/ in WSL for performance * Add test for excluded_search_path_prefixes * Apply suggestions from code review Co-authored-by: Pablo Aguiar <scorphus@gmail.com> Co-authored-by: Pablo Aguiar <scorphus@gmail.com>
This commit is contained in:
		| @@ -434,6 +434,7 @@ Several *The Fuck* parameters can be changed in the file `$XDG_CONFIG_HOME/thefu | |||||||
| * `wait_slow_command` – max amount of time in seconds for getting previous command output if it in `slow_commands` list; | * `wait_slow_command` – max amount of time in seconds for getting previous command output if it in `slow_commands` list; | ||||||
| * `slow_commands` – list of slow commands; | * `slow_commands` – list of slow commands; | ||||||
| * `num_close_matches` – maximum number of close matches to suggest, by default `3`. | * `num_close_matches` – maximum number of close matches to suggest, by default `3`. | ||||||
|  | * `excluded_search_path_prefixes` – path prefixes to ignore when searching for commands, by default `[]`. | ||||||
|  |  | ||||||
| An example of `settings.py`: | An example of `settings.py`: | ||||||
|  |  | ||||||
| @@ -466,6 +467,7 @@ rule with lower `priority` will be matched first; | |||||||
| * `THEFUCK_WAIT_SLOW_COMMAND` – max amount of time in seconds for getting previous command output if it in `slow_commands` list; | * `THEFUCK_WAIT_SLOW_COMMAND` – max amount of time in seconds for getting previous command output if it in `slow_commands` list; | ||||||
| * `THEFUCK_SLOW_COMMANDS` – list of slow commands, like `lein:gradle`; | * `THEFUCK_SLOW_COMMANDS` – list of slow commands, like `lein:gradle`; | ||||||
| * `THEFUCK_NUM_CLOSE_MATCHES` – maximum number of close matches to suggest, like `5`. | * `THEFUCK_NUM_CLOSE_MATCHES` – maximum number of close matches to suggest, like `5`. | ||||||
|  | * `THEFUCK_EXCLUDED_SEARCH_PATH_PREFIXES` – path prefixes to ignore when searching for commands, by default `[]`. | ||||||
|  |  | ||||||
| For example: | For example: | ||||||
|  |  | ||||||
|   | |||||||
| @@ -54,7 +54,8 @@ class TestSettingsFromEnv(object): | |||||||
|                            'THEFUCK_PRIORITY': 'bash=10:lisp=wrong:vim=15', |                            'THEFUCK_PRIORITY': 'bash=10:lisp=wrong:vim=15', | ||||||
|                            'THEFUCK_WAIT_SLOW_COMMAND': '999', |                            'THEFUCK_WAIT_SLOW_COMMAND': '999', | ||||||
|                            'THEFUCK_SLOW_COMMANDS': 'lein:react-native:./gradlew', |                            'THEFUCK_SLOW_COMMANDS': 'lein:react-native:./gradlew', | ||||||
|                            'THEFUCK_NUM_CLOSE_MATCHES': '359'}) |                            'THEFUCK_NUM_CLOSE_MATCHES': '359', | ||||||
|  |                            'THEFUCK_EXCLUDED_SEARCH_PATH_PREFIXES': '/media/:/mnt/'}) | ||||||
|         settings.init() |         settings.init() | ||||||
|         assert settings.rules == ['bash', 'lisp'] |         assert settings.rules == ['bash', 'lisp'] | ||||||
|         assert settings.exclude_rules == ['git', 'vim'] |         assert settings.exclude_rules == ['git', 'vim'] | ||||||
| @@ -65,6 +66,7 @@ class TestSettingsFromEnv(object): | |||||||
|         assert settings.wait_slow_command == 999 |         assert settings.wait_slow_command == 999 | ||||||
|         assert settings.slow_commands == ['lein', 'react-native', './gradlew'] |         assert settings.slow_commands == ['lein', 'react-native', './gradlew'] | ||||||
|         assert settings.num_close_matches == 359 |         assert settings.num_close_matches == 359 | ||||||
|  |         assert settings.excluded_search_path_prefixes == ['/media/', '/mnt/'] | ||||||
|  |  | ||||||
|     def test_from_env_with_DEFAULT(self, os_environ, settings): |     def test_from_env_with_DEFAULT(self, os_environ, settings): | ||||||
|         os_environ.update({'THEFUCK_RULES': 'DEFAULT_RULES:bash:lisp'}) |         os_environ.update({'THEFUCK_RULES': 'DEFAULT_RULES:bash:lisp'}) | ||||||
|   | |||||||
| @@ -94,6 +94,20 @@ def test_get_all_executables_pathsep(path, pathsep): | |||||||
|         Path_mock.assert_has_calls([call(p) for p in path.split(pathsep)], True) |         Path_mock.assert_has_calls([call(p) for p in path.split(pathsep)], True) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @pytest.mark.usefixtures('no_memoize', 'os_environ_pathsep') | ||||||
|  | @pytest.mark.parametrize('path, pathsep, excluded', [ | ||||||
|  |     ('/foo:/bar:/baz:/foo/bar:/mnt/foo', ':', '/mnt/foo'), | ||||||
|  |     (r'C:\\foo;C:\\bar;C:\\baz;C:\\foo\\bar;Z:\\foo', ';', r'Z:\\foo')]) | ||||||
|  | def test_get_all_executables_exclude_paths(path, pathsep, excluded, settings): | ||||||
|  |     settings.init() | ||||||
|  |     settings.excluded_search_path_prefixes = [excluded] | ||||||
|  |     with patch('thefuck.utils.Path') as Path_mock: | ||||||
|  |         get_all_executables() | ||||||
|  |         path_list = path.split(pathsep) | ||||||
|  |         assert call(path_list[-1]) not in Path_mock.mock_calls | ||||||
|  |         assert all(call(p) in Path_mock.mock_calls for p in path_list[:-1]) | ||||||
|  |  | ||||||
|  |  | ||||||
| @pytest.mark.parametrize('args, result', [ | @pytest.mark.parametrize('args, result', [ | ||||||
|     (('apt-get instol vim', 'instol', 'install'), 'apt-get install vim'), |     (('apt-get instol vim', 'instol', 'install'), 'apt-get install vim'), | ||||||
|     (('git brnch', 'brnch', 'branch'), 'git branch')]) |     (('git brnch', 'brnch', 'branch'), 'git branch')]) | ||||||
|   | |||||||
| @@ -101,7 +101,7 @@ class Settings(dict): | |||||||
|         elif attr in ('require_confirmation', 'no_colors', 'debug', |         elif attr in ('require_confirmation', 'no_colors', 'debug', | ||||||
|                       'alter_history', 'instant_mode'): |                       'alter_history', 'instant_mode'): | ||||||
|             return val.lower() == 'true' |             return val.lower() == 'true' | ||||||
|         elif attr == 'slow_commands': |         elif attr in ('slow_commands', 'excluded_search_path_prefixes'): | ||||||
|             return val.split(':') |             return val.split(':') | ||||||
|         else: |         else: | ||||||
|             return val |             return val | ||||||
|   | |||||||
| @@ -43,7 +43,8 @@ DEFAULT_SETTINGS = {'rules': DEFAULT_RULES, | |||||||
|                     'repeat': False, |                     'repeat': False, | ||||||
|                     'instant_mode': False, |                     'instant_mode': False, | ||||||
|                     'num_close_matches': 3, |                     'num_close_matches': 3, | ||||||
|                     'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'}} |                     'env': {'LC_ALL': 'C', 'LANG': 'C', 'GIT_TRACE': '1'}, | ||||||
|  |                     'excluded_search_path_prefixes': []} | ||||||
|  |  | ||||||
| ENV_TO_ATTR = {'THEFUCK_RULES': 'rules', | ENV_TO_ATTR = {'THEFUCK_RULES': 'rules', | ||||||
|                'THEFUCK_EXCLUDE_RULES': 'exclude_rules', |                'THEFUCK_EXCLUDE_RULES': 'exclude_rules', | ||||||
| @@ -58,7 +59,8 @@ ENV_TO_ATTR = {'THEFUCK_RULES': 'rules', | |||||||
|                'THEFUCK_SLOW_COMMANDS': 'slow_commands', |                'THEFUCK_SLOW_COMMANDS': 'slow_commands', | ||||||
|                'THEFUCK_REPEAT': 'repeat', |                'THEFUCK_REPEAT': 'repeat', | ||||||
|                'THEFUCK_INSTANT_MODE': 'instant_mode', |                'THEFUCK_INSTANT_MODE': 'instant_mode', | ||||||
|                'THEFUCK_NUM_CLOSE_MATCHES': 'num_close_matches'} |                'THEFUCK_NUM_CLOSE_MATCHES': 'num_close_matches', | ||||||
|  |                'THEFUCK_EXCLUDED_SEARCH_PATH_PREFIXES': 'excluded_search_path_prefixes'} | ||||||
|  |  | ||||||
| SETTINGS_HEADER = u"""# The Fuck settings file | SETTINGS_HEADER = u"""# The Fuck settings file | ||||||
| # | # | ||||||
|   | |||||||
| @@ -104,6 +104,10 @@ def get_close_matches(word, possibilities, n=None, cutoff=0.6): | |||||||
|     return difflib_get_close_matches(word, possibilities, n, cutoff) |     return difflib_get_close_matches(word, possibilities, n, cutoff) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def include_path_in_search(path): | ||||||
|  |     return not any(path.startswith(x) for x in settings.excluded_search_path_prefixes) | ||||||
|  |  | ||||||
|  |  | ||||||
| @memoize | @memoize | ||||||
| def get_all_executables(): | def get_all_executables(): | ||||||
|     from thefuck.shells import shell |     from thefuck.shells import shell | ||||||
| @@ -119,6 +123,7 @@ def get_all_executables(): | |||||||
|  |  | ||||||
|     bins = [exe.name.decode('utf8') if six.PY2 else exe.name |     bins = [exe.name.decode('utf8') if six.PY2 else exe.name | ||||||
|             for path in os.environ.get('PATH', '').split(os.pathsep) |             for path in os.environ.get('PATH', '').split(os.pathsep) | ||||||
|  |             if include_path_in_search(path) | ||||||
|             for exe in _safe(lambda: list(Path(path).iterdir()), []) |             for exe in _safe(lambda: list(Path(path).iterdir()), []) | ||||||
|             if not _safe(exe.is_dir, True) |             if not _safe(exe.is_dir, True) | ||||||
|             and exe.name not in tf_entry_points] |             and exe.name not in tf_entry_points] | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user