Library for building powerful interactive command lines in Python
—
Auto-completion system with built-in completers for files, words, and custom data, plus validation framework for input checking. These systems work together to provide intelligent input assistance and error prevention.
Core classes for implementing and managing auto-completion functionality.
class Completion:
def __init__(
self,
text,
start_position=0,
display=None,
display_meta=None,
style="",
selected_style=""
):
"""
Single completion result.
Parameters:
- text: str, completion text to insert
- start_position: int, position where completion starts (negative offset)
- display: str, text to display in completion menu (default: text)
- display_meta: str, additional metadata to show
- style: str, CSS-like style for completion
- selected_style: str, style when completion is selected
"""
class Completer:
"""Abstract base class for completers."""
def get_completions(self, document, complete_event):
"""
Generate completions for document.
Parameters:
- document: Document instance with current text and cursor
- complete_event: CompleteEvent with completion context
Yields:
Completion instances
"""
class CompleteEvent:
def __init__(self, text_inserted=False, completion_requested=False):
"""
Completion event context.
Parameters:
- text_inserted: bool, True if triggered by text insertion
- completion_requested: bool, True if explicitly requested (Tab)
"""
class ThreadedCompleter(Completer):
def __init__(self, completer):
"""
Wrapper for running completer in background thread.
Parameters:
- completer: Completer instance to wrap
"""
class DummyCompleter(Completer):
"""Dummy completer that returns no completions."""
def __init__(self):
"""Create dummy completer for testing."""
class DynamicCompleter(Completer):
def __init__(self, get_completer):
"""
Dynamic completer that changes based on function.
Parameters:
- get_completer: Function returning Completer instance
"""
class ConditionalCompleter(Completer):
def __init__(self, completer, filter):
"""
Completer active only when filter is true.
Parameters:
- completer: Completer instance to wrap
- filter: Filter determining when completer is active
"""
def merge_completers(completers):
"""
Merge multiple completers into one.
Parameters:
- completers: List of Completer instances
Returns:
Combined Completer instance
"""
def get_common_complete_suffix(completions):
"""
Get common suffix of all completions.
Parameters:
- completions: List of Completion instances
Returns:
str: Common suffix or empty string
"""Pre-built completers for common completion scenarios.
class WordCompleter(Completer):
def __init__(
self,
words,
ignore_case=False,
meta_dict=None,
WORD=False,
sentence=False,
match_middle=False,
pattern=None
):
"""
Completer for static word list.
Parameters:
- words: List of words to complete
- ignore_case: bool, case-insensitive matching
- meta_dict: Dict mapping words to metadata
- WORD: bool, match Vi-style WORD boundaries
- sentence: bool, complete sentences instead of words
- match_middle: bool, match anywhere in word
- pattern: compiled regex pattern for word boundaries
"""
class PathCompleter(Completer):
def __init__(
self,
only_directories=False,
get_paths=None,
file_filter=None,
min_input_len=0,
expanduser=False
):
"""
Completer for file and directory paths.
Parameters:
- only_directories: bool, complete only directories
- get_paths: Function returning list of paths to search
- file_filter: Function to filter files/directories
- min_input_len: int, minimum input length before completing
- expanduser: bool, expand ~ to user home directory
"""
class ExecutableCompleter(PathCompleter):
def __init__(self):
"""Completer for executable files in PATH."""
class NestedCompleter(Completer):
def __init__(self, options, ignore_case=True):
"""
Completer for nested command structures.
Parameters:
- options: Dict defining nested completion structure
- ignore_case: bool, case-insensitive matching
Options format:
{
'command1': {
'subcommand1': None,
'subcommand2': {'option1': None, 'option2': None}
},
'command2': WordCompleter(['arg1', 'arg2'])
}
"""
class FuzzyCompleter(Completer):
def __init__(self, completer, WORD=False, pattern=None):
"""
Fuzzy matching wrapper for any completer.
Parameters:
- completer: Completer instance to wrap
- WORD: bool, use Vi-style WORD boundaries
- pattern: compiled regex for word boundaries
"""
class FuzzyWordCompleter(Completer):
def __init__(
self,
words,
meta_dict=None,
WORD=False,
pattern=None
):
"""
Fuzzy word completer with built-in word list.
Parameters:
- words: List of words for completion
- meta_dict: Dict mapping words to metadata
- WORD: bool, use Vi-style WORD boundaries
- pattern: compiled regex for word boundaries
"""
class DeduplicateCompleter(Completer):
def __init__(self, completer):
"""
Remove duplicate completions from wrapped completer.
Parameters:
- completer: Completer instance to wrap
"""Framework for validating user input with custom validation rules.
class Validator:
"""Abstract base class for input validators."""
def validate(self, document):
"""
Validate document content.
Parameters:
- document: Document instance to validate
Raises:
ValidationError: If validation fails
"""
class ValidationError(Exception):
def __init__(self, cursor_position=0, message=""):
"""
Validation error with position and message.
Parameters:
- cursor_position: int, position where error occurred
- message: str, error message to display
"""
@property
def cursor_position(self):
"""int: Position where validation failed."""
@property
def message(self):
"""str: Error message."""
class ThreadedValidator(Validator):
def __init__(self, validator):
"""
Wrapper for running validator in background thread.
Parameters:
- validator: Validator instance to wrap
"""
class ConditionalValidator(Validator):
def __init__(self, validator, filter):
"""
Validator active only when filter is true.
Parameters:
- validator: Validator instance to wrap
- filter: Filter determining when validator is active
"""
class DummyValidator(Validator):
"""Dummy validator that never fails."""
def __init__(self):
"""Create dummy validator for testing."""
class DynamicValidator(Validator):
def __init__(self, get_validator):
"""
Dynamic validator that changes based on function.
Parameters:
- get_validator: Function returning Validator instance
"""Enumeration for controlling how completions are displayed.
class CompleteStyle(Enum):
"""Completion display styles."""
COLUMN = "column"
MULTI_COLUMN = "multi-column"
READLINE_LIKE = "readline-like"System for providing auto-suggestions based on history or other sources.
class AutoSuggest:
"""Abstract base class for auto-suggestion."""
def get_suggestion(self, buffer, document):
"""
Get suggestion for current input.
Parameters:
- buffer: Buffer instance
- document: Document with current text
Returns:
Suggestion instance or None
"""
class AutoSuggestFromHistory(AutoSuggest):
def __init__(self):
"""Auto-suggest based on command history."""
class Suggestion:
def __init__(self, text):
"""
Auto-suggestion text.
Parameters:
- text: str, suggested text to append
"""
@property
def text(self):
"""str: Suggestion text."""from prompt_toolkit import prompt
from prompt_toolkit.completion import WordCompleter
# Simple word completer
words = ['apple', 'banana', 'cherry', 'date', 'elderberry']
completer = WordCompleter(words)
result = prompt('Enter fruit: ', completer=completer)
print(f'You chose: {result}')
# Word completer with metadata
meta_dict = {
'apple': 'Red or green fruit',
'banana': 'Yellow tropical fruit',
'cherry': 'Small red fruit',
'date': 'Sweet brown fruit',
'elderberry': 'Dark purple berry'
}
completer_with_meta = WordCompleter(words, meta_dict=meta_dict)
result = prompt('Enter fruit (with descriptions): ', completer=completer_with_meta)from prompt_toolkit import prompt
from prompt_toolkit.completion import PathCompleter
# File and directory completer
path_completer = PathCompleter()
file_path = prompt('Enter file path: ', completer=path_completer)
# Directory-only completer
dir_completer = PathCompleter(only_directories=True)
directory = prompt('Enter directory: ', completer=dir_completer)
# Executable completer
from prompt_toolkit.completion import ExecutableCompleter
exec_completer = ExecutableCompleter()
command = prompt('Enter command: ', completer=exec_completer)from prompt_toolkit import prompt
from prompt_toolkit.completion import NestedCompleter, WordCompleter
# Define nested command structure
nested_completer = NestedCompleter({
'git': {
'add': None,
'commit': {
'-m': None,
'--message': None,
'--amend': None
},
'push': {
'origin': WordCompleter(['master', 'main', 'develop']),
'upstream': None
},
'pull': {
'origin': WordCompleter(['master', 'main', 'develop'])
},
'checkout': {
'-b': None,
'master': None,
'main': None,
'develop': None
}
},
'docker': {
'run': None,
'build': None,
'ps': None,
'images': None,
'stop': None,
'rm': None
}
})
command = prompt('Enter command: ', completer=nested_completer)
print(f'Command: {command}')from prompt_toolkit import prompt
from prompt_toolkit.completion import FuzzyWordCompleter
# Fuzzy word matching
words = [
'application', 'authentication', 'authorization',
'configuration', 'documentation', 'implementation',
'infrastructure', 'internationalization', 'optimization'
]
fuzzy_completer = FuzzyWordCompleter(words)
# Type partial matches like 'auth' to get 'authentication', 'authorization'
result = prompt('Enter term (fuzzy matching): ', completer=fuzzy_completer)from prompt_toolkit import prompt
from prompt_toolkit.completion import Completer, Completion
from prompt_toolkit.document import Document
class DatabaseTableCompleter(Completer):
def __init__(self, database_connection):
self.db = database_connection
def get_completions(self, document, complete_event):
# Get word before cursor
word = document.get_word_before_cursor()
# Query database for table names
try:
tables = self.db.get_table_names()
for table in tables:
if table.startswith(word.lower()):
yield Completion(
text=table,
start_position=-len(word),
display=table,
display_meta=f'Table: {table}'
)
except Exception:
# Handle database errors gracefully
pass
# Usage (assuming database connection exists)
# db_completer = DatabaseTableCompleter(db_connection)
# query = prompt('Enter table name: ', completer=db_completer)from prompt_toolkit import prompt
from prompt_toolkit.validation import Validator, ValidationError
import re
class EmailValidator(Validator):
def validate(self, document):
text = document.text
if not re.match(r'^[^@]+@[^@]+\.[^@]+$', text):
raise ValidationError(
message='Invalid email format',
cursor_position=len(text)
)
class PasswordValidator(Validator):
def validate(self, document):
text = document.text
if len(text) < 8:
raise ValidationError(
message='Password must be at least 8 characters',
cursor_position=len(text)
)
if not re.search(r'[A-Z]', text):
raise ValidationError(
message='Password must contain uppercase letter',
cursor_position=len(text)
)
if not re.search(r'[0-9]', text):
raise ValidationError(
message='Password must contain a number',
cursor_position=len(text)
)
# Use validators
email = prompt('Email: ', validator=EmailValidator())
password = prompt('Password: ', password=True, validator=PasswordValidator())from prompt_toolkit import prompt
from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.validation import Validator, ValidationError
# Valid countries list
countries = ['USA', 'Canada', 'Mexico', 'Germany', 'France', 'Japan', 'Australia']
class CountryValidator(Validator):
def validate(self, document):
text = document.text
if text and text not in countries:
raise ValidationError(
message=f'"{text}" is not a valid country',
cursor_position=len(text)
)
# Combine completion and validation
country_completer = WordCompleter(countries, ignore_case=True)
country_validator = CountryValidator()
country = prompt(
'Enter country: ',
completer=country_completer,
validator=country_validator
)
print(f'Selected country: {country}')from prompt_toolkit import prompt
from prompt_toolkit.completion import DynamicCompleter, WordCompleter
# Different completion sets
programming_languages = ['python', 'javascript', 'java', 'c++', 'go']
frameworks = ['django', 'flask', 'react', 'vue', 'angular']
# Function to select completer based on context
def get_completer():
# This could be based on application state, user input, etc.
if current_mode == 'languages':
return WordCompleter(programming_languages)
elif current_mode == 'frameworks':
return WordCompleter(frameworks)
else:
return WordCompleter([])
# Use dynamic completer
dynamic_completer = DynamicCompleter(get_completer)
# Set mode and prompt
current_mode = 'languages'
language = prompt('Programming language: ', completer=dynamic_completer)
current_mode = 'frameworks'
framework = prompt('Framework: ', completer=dynamic_completer)from prompt_toolkit import PromptSession
from prompt_toolkit.history import FileHistory
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
from prompt_toolkit.completion import WordCompleter
# Create session with history and auto-suggestions
session = PromptSession(
history=FileHistory('.command_history'),
auto_suggest=AutoSuggestFromHistory(),
completer=WordCompleter(['help', 'exit', 'status', 'config'])
)
print("Type commands. Previous commands will be suggested in gray.")
print("Press Ctrl-C to exit.")
while True:
try:
command = session.prompt('> ')
if command.lower() == 'exit':
break
print(f'Executing: {command}')
except (EOFError, KeyboardInterrupt):
breakfrom prompt_toolkit import prompt
from prompt_toolkit.completion import ThreadedCompleter, Completer, Completion
import time
class SlowCompleter(Completer):
"""Completer that simulates slow operations like network requests."""
def get_completions(self, document, complete_event):
word = document.get_word_before_cursor()
# Simulate slow operation (e.g., API call)
time.sleep(0.5)
# Generate completions
options = ['slow_option_1', 'slow_option_2', 'slow_option_3']
for option in options:
if option.startswith(word):
yield Completion(
text=option,
start_position=-len(word),
display_meta='From slow API'
)
# Wrap in ThreadedCompleter to avoid blocking UI
threaded_completer = ThreadedCompleter(SlowCompleter())
result = prompt('Enter option (completions load in background): ',
completer=threaded_completer)Install with Tessl CLI
npx tessl i tessl/pypi-prompt-toolkit