Examples

Example code.

Completion Entry Function

The completion entry function is called as function(text, state) for state in 0, 1, 2, … until it returns None. It should return the next possible completion for text. You can run this example with python -m rl.examples.raw_input.

# Complete system commands

import os

from rl import completer


class CommandCompleter:
    # A completion entry function implementing readline's
    # generator protocol

    def __call__(self, text, state):
        if state == 0:
            self.matches = list(self.complete_command(text))
        try:
            return self.matches[state]
        except IndexError:
            return None

    def complete_command(self, text):
        # Return executables matching 'text'
        for dir in os.environ.get('PATH').split(':'):
            if os.path.isdir(dir):
                for name in os.listdir(dir):
                    if name.startswith(text):
                        if os.access(os.path.join(dir, name), os.R_OK|os.X_OK):
                            yield name


def main():
    # Set the completion entry function
    completer.completer = CommandCompleter()

    # Enable TAB completion
    completer.parse_and_bind('TAB: complete')

    command = input('command> ')
    print('You typed:', command)


if __name__ == '__main__':
    main()

Generator Factory

The generator() factory provides a simple way to support this protocol. It is typically used as a decorator but can be passed any callable to create a completion entry function. You can run this example with python -m rl.examples.factory.

# Complete system commands

import os

from rl import completer
from rl import generator
from rl import print_exc


@print_exc
@generator
def complete_command(text):
    # Return executables matching 'text'
    for dir in os.environ.get('PATH').split(':'):
        if os.path.isdir(dir):
            for name in os.listdir(dir):
                if name.startswith(text):
                    if os.access(os.path.join(dir, name), os.R_OK|os.X_OK):
                        yield name


def main():
    # Set the completion entry function
    completer.completer = complete_command

    # Enable TAB completion
    completer.parse_and_bind('TAB: complete')

    command = input('command> ')
    print('You typed:', command)


if __name__ == '__main__':
    main()

Multiple Completions

The completion entry function is often a dispatcher, forwarding calls to more specific completion functions depending on position and format of the completion word. You can run this example with python -m rl.examples.email.

# Complete email addresses

from rl import completer
from rl import completion
from rl import generator
from rl import print_exc

from rl.utils import DEFAULT_DELIMS


def complete_hostname(text):
    # Search /etc/hosts for matching hostnames
    with open('/etc/hosts', 'rt') as f:
        lines = f.readlines()
    for line in lines:
        line = line.split()
        if line and not line[0].startswith('#'):
            for hostname in line[1:]:
                if hostname.startswith(text[1:]):
                    yield '@' + hostname


@print_exc
@generator
def complete_email(text):
    # Dispatch to username or hostname completion
    if text.startswith('@'):
        return complete_hostname(text)
    else:
        completion.append_character = '@'
        return completion.complete_username(text)


def main():
    # Configure word break characters
    completer.word_break_characters = DEFAULT_DELIMS.replace('-', '')

    # Configure special prefixes
    completer.special_prefixes = '@'

    # Set the completion entry function
    completer.completer = complete_email

    # Enable TAB completion
    completer.parse_and_bind('TAB: complete')

    email = input('email> ')
    print('You typed:', email)


if __name__ == '__main__':
   main()

Filename Completion

Filename completion is readline’s party trick. It is also the most complex feature, requiring various parts of readline to be set up. You can run this example with python -m rl.examples.filename.

# Complete filenames

import sys
import unicodedata

from rl import completer
from rl import completion
from rl import generator
from rl import print_exc


@print_exc
def char_is_quoted(text, index):
    # Return True if the character at index is quoted
    return index > 0 and text[index-1] == '\\'


@print_exc
def quote_filename(text, single_match, quote_char):
    # Backslash-quote characters in text
    if quote_char == "'":
        pass
    elif quote_char == '"':
        for c in '\\"$`\n':
            text = text.replace(c, '\\'+c)
    else:
        for c in completer.filename_quote_characters:
            text = text.replace(c, '\\'+c)
    return text


@print_exc
def dequote_filename(text, quote_char):
    # Backslash-dequote characters in text
    if quote_char == "'":
        pass
    elif quote_char == '"':
        for c in '\\"$`\n':
            text = text.replace('\\'+c, c)
    else:
        for c in completer.filename_quote_characters:
            text = text.replace('\\'+c, c)
    return text


@print_exc
def rewrite_filename(text):
    # Normalize decomposed UTF-8 received from HFS Plus
    return unicodedata.normalize('NFC', text)


@print_exc
@generator
def complete_filename(text):
    matches = []
    # Complete usernames
    if text.startswith('~') and '/' not in text:
        matches = completion.complete_username(text)
    # Complete filenames
    if not matches:
        matches = completion.complete_filename(text)
    return matches


def main():
    # Configure quote characters
    completer.quote_characters = '\'"'
    completer.word_break_characters = ' \t\n"\'><;|&=(:'
    completer.filename_quote_characters = '\\ \t\n"\'@><;|&=()#$`?*[!:{'

    # Configure quoting functions
    completer.char_is_quoted_function = char_is_quoted
    completer.filename_quoting_function = quote_filename
    completer.filename_dequoting_function = dequote_filename

    # Configure Unicode converter on Mac OS X
    if sys.platform == "darwin":
        completer.filename_rewrite_hook = rewrite_filename

    # Set the completion entry function
    completer.completer = complete_filename

    # Enable TAB completion
    completer.parse_and_bind('TAB: complete')

    filename = input('file> ')
    print('You typed:', filename)


if __name__ == '__main__':
    main()

Display Matches Hook

The display_matches_hook is called whenever matches need to be displayed.

# Python implementation of the default display_matches_hook

import sys

from rl import completer
from rl import completion
from rl import readline
from rl import print_exc


@print_exc
def display_matches_hook(substitution, matches, longest_match_length):
    num_matches = len(matches)
    if num_matches >= completer.query_items > 0:
        sys.stdout.write('\nDisplay all %d possibilities? (y or n)' % num_matches)
        sys.stdout.flush()
        while True:
            c = readline.read_key()
            if c in 'yY\x20': # SPACEBAR
                break
            if c in 'nN\x7f': # RUBOUT
                sys.stdout.write('\n')
                completion.redisplay(force=True)
                return
    completion.display_match_list(substitution, matches, longest_match_length)
    completion.redisplay(force=True)