CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-thefuck

Magnificent app which corrects your previous console command

Overview
Eval results
Files

rule-system.mddocs/

Rule System and Correction

Rule loading, command matching, and correction generation. This system processes failed commands through available rules to generate appropriate corrections using a sophisticated pattern-matching and correction-generation pipeline.

Capabilities

Rule Loading and Management

Functions for discovering, loading, and managing correction rules from both built-in and user-defined sources.

def load_rule(rule, settings):
    """
    Imports rule module and returns Rule object.
    
    Parameters:
    - rule (pathlib.Path): Path to rule Python file
    - settings (Settings): Application settings
    
    Returns:
    Rule: Loaded rule object with all metadata
    
    Loads rule module and extracts:
    - match function
    - get_new_command function
    - enabled_by_default (default: True)
    - side_effect (default: None)
    - priority (default: DEFAULT_PRIORITY or custom from settings)
    - requires_output (default: True)
    """

def get_loaded_rules(rules, settings):
    """
    Yields all available rules that are enabled in settings.
    
    Parameters:
    - rules (iterable): Collection of rule file paths
    - settings (Settings): Application settings containing enabled rules
    
    Yields:
    Rule: Each enabled and loaded rule
    """

def get_rules(user_dir, settings):
    """
    Returns all enabled rules from built-in and user directories.
    
    Parameters:
    - user_dir (pathlib.Path): User configuration directory
    - settings (Settings): Application settings
    
    Returns:
    list[Rule]: Sorted list of enabled rules by priority
    
    Searches for rules in:
    - Built-in rules directory (thefuck/rules/*.py)
    - User rules directory (~/.thefuck/rules/*.py)
    """

Command Matching and Processing

Functions for testing rule applicability and generating corrections from matched rules.

def is_rule_match(command, rule, settings):
    """
    Returns True if rule matches the given command.
    
    Parameters:
    - command (Command): Command to test against rule
    - rule (Rule): Rule to test for match
    - settings (Settings): Application settings
    
    Returns:
    bool: True if rule applies to the command
    
    Handles:
    - Script-only commands when rule requires output
    - Exception handling for rule match functions
    - Debug logging of rule matching attempts
    """

def make_corrected_commands(command, rule, settings):
    """
    Generates corrected commands from a matched rule.
    
    Parameters:
    - command (Command): Original failed command
    - rule (Rule): Matched rule to apply
    - settings (Settings): Application settings
    
    Yields:
    CorrectedCommand: Each generated correction with proper priority
    
    Handles:
    - Single correction return values
    - Multiple correction return values (lists)
    - Priority calculation for multiple corrections
    """

def get_corrected_commands(command, user_dir, settings):
    """
    Returns sorted sequence of corrected commands for a given command.
    
    Parameters:
    - command (Command): Original failed command
    - user_dir (pathlib.Path): User configuration directory
    - settings (Settings): Application settings
    
    Returns:
    SortedCorrectedCommandsSequence: Lazy-evaluated, sorted corrections
    
    Process:
    1. Load all enabled rules
    2. Test each rule for matches
    3. Generate corrections from matched rules
    4. Return sorted, deduplicated sequence
    """

Rule Structure

Every correction rule is a Python module that must implement specific functions and can define optional attributes:

Required Functions

def match(command, settings):
    """
    Determines if this rule applies to the given command.
    
    Parameters:
    - command (Command): The failed command to analyze
    - settings (Settings): Application settings
    
    Returns:
    bool: True if this rule can correct the command
    
    This function should analyze command.script, command.stdout, 
    and command.stderr to determine applicability.
    """

def get_new_command(command, settings):
    """
    Generates corrected command(s) for the matched command.
    
    Parameters:
    - command (Command): The failed command to correct
    - settings (Settings): Application settings
    
    Returns:
    str or list[str]: Single correction or list of possible corrections
    
    This function should return the corrected command string(s)
    that should be executed instead of the original.
    """

Optional Attributes

enabled_by_default = True
    """
    bool: Whether this rule is enabled by default.
    Default: True if not specified.
    """

priority = 1000
    """
    int: Rule priority for ordering corrections.
    Lower numbers = higher priority.
    Default: conf.DEFAULT_PRIORITY (1000) if not specified.
    """

requires_output = True
    """
    bool: Whether this rule needs command output (stdout/stderr) to function.
    Default: True if not specified.
    Set to False for rules that only analyze command.script.
    """

def side_effect(old_cmd, new_cmd, settings):
    """
    Optional function executed when the corrected command runs.
    
    Parameters:
    - old_cmd (Command): Original failed command
    - new_cmd (str): Corrected command being executed
    - settings (Settings): Application settings
    
    Returns:
    None
    
    Used for additional actions like updating configuration,
    cleaning up state, or logging corrections.
    """

Built-in Rules Examples

The thefuck package includes 70+ built-in rules covering common command-line tools. Here are some examples:

Git Push Rule Example

# Example: thefuck/rules/git_push.py
import re
from thefuck.utils import for_app

@for_app('git')
def match(command, settings):
    return (command.script.startswith('git push') and
            'set-upstream' in command.stderr)

def get_new_command(command, settings):
    branch = re.search(r'git push (\w+)', command.script)
    if branch:
        return f'git push --set-upstream origin {branch.group(1)}'
    return 'git push --set-upstream origin main'

enabled_by_default = True
priority = 1000

Sudo Rule Example

# Example: thefuck/rules/sudo.py
def match(command, settings):
    return ('permission denied' in command.stderr.lower() or
            'operation not permitted' in command.stderr.lower())

def get_new_command(command, settings):
    return f'sudo {command.script}'

enabled_by_default = True
priority = 100  # High priority

Usage Examples

Basic Rule Processing

from thefuck.corrector import get_corrected_commands
from thefuck.types import Command
from thefuck.main import setup_user_dir
from thefuck.conf import get_settings

# Setup
user_dir = setup_user_dir()
settings = get_settings(user_dir)

# Create a failed command
command = Command(
    script="git push origin main",
    stdout="",
    stderr="fatal: The current branch main has no upstream branch."
)

# Get corrections
corrections = get_corrected_commands(command, user_dir, settings)

# Access corrections
if corrections:
    print(f"First correction: {corrections[0].script}")
    print(f"Total corrections: {len(corrections)}")
    
    for correction in corrections:
        print(f"  {correction.script} (priority: {correction.priority})")

Manual Rule Testing

from thefuck.corrector import load_rule, is_rule_match
from pathlib import Path

# Load a specific rule
rule_path = Path("thefuck/rules/git_push.py")
rule = load_rule(rule_path, settings)

# Test rule matching
command = Command("git push origin main", "", "set-upstream error...")
if is_rule_match(command, rule, settings):
    print(f"Rule {rule.name} matches the command")
    
    # Generate corrections
    corrections = list(make_corrected_commands(command, rule, settings))
    for correction in corrections:
        print(f"Correction: {correction.script}")

Custom Rule Loading

from thefuck.corrector import get_rules

# Get all enabled rules
user_dir = setup_user_dir()
settings = get_settings(user_dir)
rules = get_rules(user_dir, settings)

print(f"Loaded {len(rules)} rules:")
for rule in rules[:5]:  # Show first 5
    print(f"  {rule.name} (priority: {rule.priority})")

Rule Development Patterns

Command Analysis Patterns

# Pattern: Check command name
def match(command, settings):
    return command.script.startswith('docker')

# Pattern: Check error output
def match(command, settings):
    return 'command not found' in command.stderr

# Pattern: Complex regex matching
import re
def match(command, settings):
    return re.search(r'git \w+', command.script) and 'not a git command' in command.stderr

Correction Generation Patterns

# Pattern: Simple replacement
def get_new_command(command, settings):
    return command.script.replace('pussh', 'push')

# Pattern: Multiple corrections
def get_new_command(command, settings):
    return [
        f'sudo {command.script}',
        command.script.replace('apt-get', 'apt')
    ]

# Pattern: Context-aware correction
def get_new_command(command, settings):
    if 'permission denied' in command.stderr:
        return f'sudo {command.script}'
    elif 'not found' in command.stderr:
        return command.script.replace('ll', 'ls -la')

Error Handling

The rule system handles various error conditions:

  • Rule loading errors: Invalid rule modules are logged and skipped
  • Match function exceptions: Caught and logged, rule is skipped for that command
  • Correction generation errors: Caught and logged, rule produces no corrections
  • Priority conflicts: Rules with same priority are ordered consistently
  • Circular dependencies: Prevented through proper rule isolation

Install with Tessl CLI

npx tessl i tessl/pypi-thefuck

docs

configuration.md

core-application.md

data-types.md

index.md

rule-development.md

rule-system.md

shell-integration.md

user-interface.md

utilities.md

tile.json