CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-readline

GNU readline support for Python on platforms without readline

Pending
Overview
Eval results
Files

hooks.mddocs/

Event Hooks

Advanced customization through event hooks that execute at specific points in the readline process, enabling custom display and input handling. These hooks provide fine-grained control over readline's behavior at key moments in the input cycle.

Capabilities

Startup Hook

Function executed just before readline prints the first prompt, allowing for initialization and setup before user interaction begins.

def set_startup_hook(function=None):
    """
    Set or remove the startup_hook function.
    
    Parameters:
    - function (callable, optional): Function called with no arguments just before
                                   readline prints the first prompt. If None, removes hook.
    
    Returns:
    None
    """

Usage Example:

import readline
import os
import time

def startup_initialization():
    """Initialize readline environment at startup"""
    # Load custom history file
    history_file = os.path.expanduser('~/.myapp_history')
    try:
        readline.read_history_file(history_file)
    except FileNotFoundError:
        pass
    
    # Set up custom completion
    readline.parse_and_bind('tab: complete')
    
    # Display welcome message
    print(f"Welcome! Loaded {readline.get_current_history_length()} history entries.")
    print("Type 'help' for available commands.")

# Set startup hook
readline.set_startup_hook(startup_initialization)

# The hook will be called before the first input() call
user_input = input("Command: ")

Pre-Input Hook

Function executed after the first prompt has been printed but before readline starts reading input characters, useful for pre-populating the command line or performing just-in-time setup.

def set_pre_input_hook(function=None):
    """
    Set or remove the pre_input_hook function.
    
    Parameters:
    - function (callable, optional): Function called with no arguments after the first
                                   prompt has been printed and just before readline starts
                                   reading input characters. If None, removes hook.
    
    Returns:
    None
    
    Note: Only available when compiled with HAVE_RL_PRE_INPUT_HOOK
    """

Usage Example:

import readline
import datetime

def pre_input_setup():
    """Set up command line before input begins"""
    # Insert timestamp prefix
    timestamp = datetime.datetime.now().strftime("%H:%M ")
    readline.insert_text(f"[{timestamp}] ")
    
    # Or insert last command for editing
    history_length = readline.get_current_history_length()
    if history_length > 0:
        last_command = readline.get_history_item(history_length)
        if last_command and last_command.startswith("repeat:"):
            # Remove "repeat:" prefix and insert the command
            command = last_command[7:]  # Remove "repeat:" prefix
            readline.insert_text(command)

# Set pre-input hook
readline.set_pre_input_hook(pre_input_setup)

# Hook executes after prompt is shown but before input starts
command = input("CMD> ")

Advanced Pre-Input Patterns

import readline
import os

class SmartPreInput:
    def __init__(self):
        self.context_aware = True
        self.auto_suggest = True
    
    def setup_input(self):
        """Intelligent pre-input setup based on context"""
        if not self.context_aware:
            return
        
        # Check current directory for context clues
        current_dir = os.getcwd()
        files = os.listdir(current_dir)
        
        # Auto-suggest based on directory contents
        if 'Makefile' in files:
            readline.insert_text("make ")
        elif 'package.json' in files:
            readline.insert_text("npm ")
        elif 'requirements.txt' in files:
            readline.insert_text("pip install -r requirements.txt")
        elif 'setup.py' in files:
            readline.insert_text("python setup.py ")
        elif any(f.endswith('.py') for f in files):
            py_files = [f for f in files if f.endswith('.py')]
            if 'main.py' in py_files:
                readline.insert_text("python main.py ")
            else:
                readline.insert_text("python ")

smart_input = SmartPreInput()
readline.set_pre_input_hook(smart_input.setup_input)

Completion Display Hook

Function for customizing how completion matches are displayed to the user, allowing for enhanced completion interfaces and custom formatting.

def set_completion_display_matches_hook(function=None):
    """
    Set or remove the completion display function.
    
    Parameters:
    - function (callable, optional): Function called as function(substitution, matches, 
                                   longest_match_length) when displaying completion matches.
                                   If None, removes hook.
    
    Returns:
    None
    """

Usage Example:

import readline

def custom_completion_display(substitution, matches, longest_match_length):
    """Custom completion display with enhanced formatting"""
    print(f"\n📋 {len(matches)} completions for '{substitution}':")
    
    # Group matches by type or category
    files = []
    dirs = []
    commands = []
    
    for match in matches:
        if match.endswith('/'):
            dirs.append(match)
        elif '.' in match:
            files.append(match)
        else:
            commands.append(match)
    
    # Display grouped results
    if dirs:
        print("  📁 Directories:")
        for d in dirs[:5]:  # Show first 5
            print(f"    {d}")
        if len(dirs) > 5:
            print(f"    ... and {len(dirs) - 5} more directories")
    
    if files:
        print("  📄 Files:")
        for f in files[:5]:
            print(f"    {f}")
        if len(files) > 5:
            print(f"    ... and {len(files) - 5} more files")
    
    if commands:
        print("  ⚡ Commands:")
        for c in commands[:5]:
            print(f"    {c}")
        if len(commands) > 5:
            print(f"    ... and {len(commands) - 5} more commands")
    
    print()  # Extra line for readability

# Set custom display hook
readline.set_completion_display_matches_hook(custom_completion_display)

Advanced Display Hook Example

import readline
import os
import stat

def detailed_completion_display(substitution, matches, longest_match_length):
    """Detailed completion display with file information"""
    print(f"\n🔍 Completions for '{substitution}' ({len(matches)} matches):")
    
    # Sort matches: directories first, then files
    dirs = [m for m in matches if m.endswith('/')]
    files = [m for m in matches if not m.endswith('/')]
    sorted_matches = dirs + files
    
    # Display with details
    for i, match in enumerate(sorted_matches[:10]):  # Show first 10
        if match.endswith('/'):
            icon = "📁"
            details = "directory"
        else:
            icon = "📄"
            try:
                # Get file size and modification time
                stats = os.stat(match)
                size = stats.st_size
                mtime = stats.st_mtime
                
                # Format size
                if size < 1024:
                    size_str = f"{size}B"
                elif size < 1024 * 1024:
                    size_str = f"{size // 1024}KB"
                else:
                    size_str = f"{size // (1024 * 1024)}MB"
                
                details = f"{size_str}"
                
                # Check if executable
                if os.access(match, os.X_OK):
                    icon = "⚡"
                    details += " (executable)"
                    
            except OSError:
                details = "file"
        
        print(f"  {i+1:2d}. {icon} {match:<{longest_match_length + 2}} {details}")
    
    if len(matches) > 10:
        print(f"     ... and {len(matches) - 10} more matches")
    print()

readline.set_completion_display_matches_hook(detailed_completion_display)

Hook Integration Patterns

Complete Interactive Setup

import readline
import atexit
import os

class InteractiveShell:
    def __init__(self):
        self.history_file = os.path.expanduser('~/.myshell_history')
        self.setup_readline()
    
    def setup_readline(self):
        """Complete readline setup with all hooks"""
        # Set all hooks
        readline.set_startup_hook(self.startup_hook)
        readline.set_pre_input_hook(self.pre_input_hook)
        readline.set_completion_display_matches_hook(self.display_hook)
        
        # Configure completion
        readline.set_completer(self.completer)
        readline.parse_and_bind('tab: complete')
        
        # Set up history
        try:
            readline.read_history_file(self.history_file)
        except FileNotFoundError:
            pass
        
        atexit.register(self.cleanup)
    
    def startup_hook(self):
        """Initialization at startup"""
        print("🚀 Interactive shell ready!")
        readline.set_history_length(1000)
    
    def pre_input_hook(self):
        """Pre-input setup"""
        # Could insert context-aware prefixes here
        pass
    
    def display_hook(self, substitution, matches, longest_match_length):
        """Custom completion display"""
        if len(matches) > 1:
            print(f"\n💡 {len(matches)} options for '{substitution}':")
            for i, match in enumerate(matches[:5]):
                print(f"  {i+1}. {match}")
            if len(matches) > 5:
                print(f"  ... and {len(matches) - 5} more")
            print()
    
    def completer(self, text, state):
        """Simple file completion"""
        import glob
        matches = glob.glob(text + '*')
        return matches[state] if state < len(matches) else None
    
    def cleanup(self):
        """Cleanup when exiting"""
        readline.write_history_file(self.history_file)

# Set up interactive shell
shell = InteractiveShell()

Platform Availability

Note: The set_pre_input_hook() function is only available when compiled with HAVE_RL_PRE_INPUT_HOOK. It may not be available on all systems or readline installations.

Install with Tessl CLI

npx tessl i tessl/pypi-readline

docs

completion.md

configuration.md

history.md

hooks.md

index.md

line-editing.md

tile.json