0
# Utilities and Helpers
1
2
Common utility functions, decorators, and helper tools used throughout the application for caching, string matching, command manipulation, and application detection. These utilities provide essential functionality that supports the core correction system.
3
4
## Capabilities
5
6
### Caching and Memoization
7
8
Functions for improving performance through result caching and memoization.
9
10
```python { .api }
11
def memoize(fn):
12
"""
13
Decorator for function result caching.
14
15
Parameters:
16
- fn (callable): Function to memoize
17
18
Returns:
19
callable: Memoized version of the function
20
21
Caches function results based on arguments to avoid repeated computation.
22
Cache can be disabled by setting memoize.disabled = True.
23
24
Example:
25
@memoize
26
def expensive_function(arg1, arg2):
27
# Expensive computation
28
return result
29
"""
30
31
def cache(*depends_on):
32
"""
33
File-based caching decorator with dependency tracking.
34
35
Parameters:
36
- *depends_on (str): Files to monitor for cache invalidation
37
38
Returns:
39
callable: Decorator function that caches results
40
41
Caches function result in temporary file. Cache will be expired when
42
modification date of files from depends_on will be changed. Function
43
wrapped in cache should be arguments agnostic.
44
45
Example:
46
@cache('~/.thefuck/settings.py')
47
def get_expensive_settings():
48
# Expensive computation
49
return result
50
"""
51
```
52
53
### System and Application Detection
54
55
Functions for detecting system capabilities and applications.
56
57
```python { .api }
58
def which(program):
59
"""
60
Returns program path or None.
61
62
Parameters:
63
- program (str): Program name to locate
64
65
Returns:
66
str or None: Full path to program if found, None otherwise
67
68
Cross-platform implementation of 'which' command functionality.
69
Searches PATH for executable programs.
70
"""
71
72
def is_app(command, *app_names):
73
"""
74
Returns True if command is call to one of passed app names.
75
76
Parameters:
77
- command (Command): Command object to check
78
- *app_names (str): Application names to check against
79
80
Returns:
81
bool: True if command matches any of the app names
82
83
Checks if the command script starts with any of the specified
84
application names, handling both bare commands and commands with arguments.
85
86
Example:
87
cmd = Command("git status", "", "")
88
if is_app(cmd, 'git'):
89
print("This is a git command")
90
"""
91
92
def for_app(*app_names):
93
"""
94
Decorator that specifies matching script is for one of app names.
95
96
Parameters:
97
- *app_names (str): Application names to match against
98
99
Returns:
100
callable: Decorator function
101
102
Uses is_app internally to check if the command matches any of the
103
specified application names. Returns False if no match.
104
105
Example:
106
@for_app('docker', 'podman')
107
def container_correction(command, settings):
108
# Container-specific correction logic
109
return corrected_command
110
"""
111
```
112
113
### String Matching and Fuzzy Search
114
115
Functions for intelligent string matching and similarity detection.
116
117
```python { .api }
118
def get_closest(word, possibilities, n=3, cutoff=0.6, fallback_to_first=True):
119
"""
120
Returns closest match or just first from possibilities.
121
122
Parameters:
123
- word (str): Word to find matches for
124
- possibilities (list): List of possible matches
125
- n (int): Maximum number of matches to consider (default: 3)
126
- cutoff (float): Minimum similarity score (0.0-1.0, default: 0.6)
127
- fallback_to_first (bool): Return first possibility if no close match
128
129
Returns:
130
str or None: Best match or first possibility if fallback enabled
131
132
Uses difflib.get_close_matches for intelligent fuzzy matching.
133
Useful for correcting typos in command names and arguments.
134
"""
135
136
def get_all_executables():
137
"""
138
Returns list of all available executables and shell aliases.
139
140
Returns:
141
list: List of executable names and shell aliases
142
143
Searches PATH directories for executable files and includes shell aliases.
144
Excludes the thefuck alias itself to avoid self-reference. Results are
145
memoized for performance.
146
147
Used for intelligent command suggestions and corrections.
148
"""
149
```
150
151
### Command Manipulation
152
153
Functions for analyzing and modifying command strings.
154
155
```python { .api }
156
def replace_argument(script, from_, to):
157
"""
158
Replaces command line argument.
159
160
Parameters:
161
- script (str): Original command string
162
- from_ (str): Argument to replace
163
- to (str): Replacement argument
164
165
Returns:
166
str: Command with argument replaced
167
168
First tries to replace at the end of the command, then in the middle
169
with space boundaries to avoid partial replacements.
170
Example: replace_argument('git pul origin', 'pul', 'pull')
171
"""
172
173
def replace_command(command, broken, matched):
174
"""
175
Helper for *_no_command rules.
176
177
Parameters:
178
- command (Command): Command object containing the script
179
- broken (str): Broken command name to replace
180
- matched (list): List of possible correct command names
181
182
Returns:
183
list: List of corrected command strings
184
185
Uses fuzzy matching to find close matches and generates multiple
186
correction suggestions by replacing the broken command with matches.
187
"""
188
189
def eager(fn, *args, **kwargs):
190
"""
191
Decorator that converts generator to list.
192
193
Parameters:
194
- fn (callable): Function that returns a generator
195
- *args: Arguments to pass to function
196
- **kwargs: Keyword arguments to pass to function
197
198
Returns:
199
list: List containing all generator results
200
201
Forces immediate evaluation of generator functions.
202
"""
203
204
def get_all_matched_commands(stderr, separator='Did you mean'):
205
"""
206
Extracts command suggestions from error output.
207
208
Parameters:
209
- stderr (str): Error output containing suggestions
210
- separator (str): Text that indicates start of suggestions
211
212
Returns:
213
list: List of suggested commands
214
215
Parses command line tool error messages to extract suggested
216
corrections. Commonly used with tools that provide "did you mean"
217
style suggestions.
218
"""
219
220
def wrap_settings(params):
221
"""
222
Adds default values to settings if it not presented.
223
224
Parameters:
225
- params (dict): Default parameter values to add to settings
226
227
Returns:
228
callable: Decorator function
229
230
Decorator that ensures rule functions have access to default
231
settings values. Commonly used in rules that need specific
232
application paths or configuration values.
233
234
Example:
235
@wrap_settings({'apt': '/usr/bin/apt'})
236
def match(command, settings):
237
print(settings.apt)
238
"""
239
```
240
241
242
### Platform and Environment
243
244
Utility constants and functions for platform compatibility.
245
246
```python { .api }
247
DEVNULL = open(os.devnull, 'w')
248
"""
249
File object for null output redirection.
250
Cross-platform equivalent of /dev/null.
251
"""
252
253
def quote(text):
254
"""
255
Shell-safe quoting function.
256
257
Parameters:
258
- text (str): Text to quote for shell safety
259
260
Returns:
261
str: Properly quoted text
262
263
Platform-specific implementation:
264
- Python 2: Uses pipes.quote
265
- Python 3: Uses shlex.quote
266
"""
267
```
268
269
## Usage Examples
270
271
### Memoization for Performance
272
273
```python
274
from thefuck.utils import memoize
275
276
@memoize
277
def expensive_git_operation(repo_path):
278
"""Expensive operation that benefits from caching."""
279
# Simulate expensive operation
280
import subprocess
281
result = subprocess.run(['git', 'log', '--oneline'],
282
cwd=repo_path, capture_output=True, text=True)
283
return result.stdout.split('\n')
284
285
# First call: executes operation
286
commits1 = expensive_git_operation('/path/to/repo')
287
288
# Second call: returns cached result
289
commits2 = expensive_git_operation('/path/to/repo') # Much faster
290
291
# Disable caching temporarily
292
from thefuck.utils import memoize
293
memoize.disabled = True
294
commits3 = expensive_git_operation('/path/to/repo') # Executes again
295
memoize.disabled = False
296
```
297
298
### Application Detection
299
300
```python
301
from thefuck.utils import which, is_app, for_app
302
303
# Check if programs are available
304
git_path = which('git')
305
if git_path:
306
print(f"Git found at: {git_path}")
307
308
docker_path = which('docker')
309
if docker_path:
310
print(f"Docker found at: {docker_path}")
311
312
# Use decorators for app-specific functions
313
@for_app('git')
314
def handle_git_command(command, settings):
315
print(f"Handling git command: {command.script}")
316
317
@is_app('npm')
318
def handle_npm_command(command, settings):
319
print(f"Handling npm command: {command.script}")
320
```
321
322
### Fuzzy String Matching
323
324
```python
325
from thefuck.utils import get_closest, get_all_matched
326
327
# Find closest match for typos
328
git_commands = ['push', 'pull', 'commit', 'checkout', 'branch', 'merge']
329
closest = get_closest('pul', git_commands)
330
print(f"Did you mean: {closest}") # "pull"
331
332
# Get all possible matches
333
typo = 'comit'
334
all_matches = get_all_matched(typo, git_commands, cutoff=0.4)
335
print(f"Possible matches: {all_matches}") # ['commit']
336
337
# Directory name correction
338
dirs = ['Documents', 'Downloads', 'Desktop', 'Pictures']
339
corrected = get_closest('Documnets', dirs)
340
print(f"Corrected directory: {corrected}") # "Documents"
341
```
342
343
### Command Manipulation
344
345
```python
346
from thefuck.utils import replace_argument, replace_command, add_argument
347
348
# Fix command typos
349
original = "git pul origin main"
350
fixed = replace_argument(original, 'pul', 'pull')
351
print(f"Fixed: {fixed}") # "git pull origin main"
352
353
# Fix command name typos
354
typo_cmd = "gti status"
355
fixed_cmd = replace_command(typo_cmd, 'gti', 'git')
356
print(f"Fixed command: {fixed_cmd}") # "git status"
357
358
# Add missing arguments
359
incomplete = "docker run image"
360
complete = add_argument(incomplete, '--rm')
361
print(f"Complete: {complete}") # "docker run --rm image"
362
```
363
364
### History Integration
365
366
```python
367
from thefuck.utils import get_valid_history_without_current
368
from thefuck.types import Command
369
370
# Get relevant history
371
current_cmd = Command("git push origin main", "", "error output")
372
history = get_valid_history_without_current(current_cmd)
373
374
print("Recent commands (excluding current):")
375
for cmd in history[:5]: # Show last 5
376
print(f" {cmd}")
377
```
378
379
### Safe Shell Quoting
380
381
```python
382
from thefuck.utils import quote
383
384
# Safely quote arguments for shell execution
385
filename = "file with spaces.txt"
386
safe_filename = quote(filename)
387
command = f"cat {safe_filename}"
388
print(f"Safe command: {command}") # cat 'file with spaces.txt'
389
390
# Handle special characters
391
special_arg = "arg with $pecial char&"
392
safe_arg = quote(special_arg)
393
print(f"Safe argument: {safe_arg}")
394
```
395
396
### Caching with Persistence
397
398
```python
399
from thefuck.utils import cache
400
import requests
401
402
@cache
403
def fetch_api_data(url):
404
"""Expensive API call that benefits from persistent caching."""
405
response = requests.get(url)
406
return response.json()
407
408
# First call: makes HTTP request
409
data1 = fetch_api_data('https://api.example.com/data')
410
411
# Second call (even in different session): uses cached data
412
data2 = fetch_api_data('https://api.example.com/data') # No HTTP request
413
```
414
415
## Performance Considerations
416
417
### Memoization Guidelines
418
- Use for CPU-intensive pure functions
419
- Avoid for functions with side effects
420
- Consider memory usage for large result sets
421
- Disable during testing if needed
422
423
### Caching Best Practices
424
- Use persistent cache for cross-session data
425
- Consider cache invalidation strategies
426
- Monitor cache size and cleanup as needed
427
- Use appropriate cache keys
428
429
### String Matching Optimization
430
- Adjust cutoff values for accuracy vs. performance
431
- Pre-filter possibilities when working with large lists
432
- Consider caching fuzzy match results for repeated queries
433
434
These utilities form the foundation that enables thefuck's intelligent command correction and provide the performance optimizations necessary for responsive user experience.