Python readline completer and command line parser
pyrl-complete is a Python library for building powerful, context-aware command-line autocompletion. It allows developers to define the grammar of a command-line interface (CLI) using a simple, human-readable syntax.
The library parses this grammar to understand all possible command structures, including commands, sub-commands, options, and arguments. It then uses this understanding to provide intelligent autocompletion suggestions and predictions as a user types.
- Custom Grammar: Define your CLI structure in a simple
.prlfile. The syntax supports:- Simple command sequences (
get status) - Alternatives using
|(get (one | two)) - Optional groups using
[](command [optional_part]) - Options with placeholder arguments (
-d ?)
- Simple command sequences (
- Completion Engine: A prefix-tree-based engine that provides:
- Suggestions: A list of all possible valid commands that match the current input.
- Predictions: The most likely next word or token based on the current input.
- Tester GUI: A bundled Tkinter application that provides a live development environment. You can write your grammar, parse it, and test the completion behavior in real-time, making development and debugging fast and easy.
The grammar for defining your command-line interface is designed to be simple and flexible. Rules are typically defined in a .prl file.
Each complete command path is a statement. Statements can be separated by a newline or a semicolon (;).
# Separated by newline
get status
set user
# Separated by semicolon
get status; set user;
The pipe character | is used to define a set of alternative tokens. Parentheses ( ... ) are used to group these alternatives, which is necessary when they appear in the middle of a command.
# Creates two valid paths: 'set user name ?' and 'set group name ?'
set (user | group) name ?;
Square brackets [ ... ] define an optional part of a command. The command is valid with or without the tokens inside the brackets.
# Creates three valid paths: 'show config', 'show interfaces', and just 'show'
show [config | interfaces];
Tokens starting with a hyphen - are treated as options. An option can be followed by a ? to indicate that it takes an argument. The ? acts as a placeholder for the completion engine.
# An option without an argument
get -h
# An option that requires an argument
set user -name ?
- Define Rules: You write your command structure in a text file (e.g.,
rules.prl).# Example Rules get status; set (user | group) name ?; show [config | interfaces]; - Parse: The library's parser (built with
ply) reads your rules and generates a list of all valid command paths. - Complete: As a user types a command, the completion engine queries a tree built from these paths to find and suggest the next valid tokens.
This library provides the core components to build a rich autocompletion experience for any Python-based CLI application.
To integrate pyrl-complete into a Python CLI application, you need to connect its completion engine to Python's built-in readline library. readline handles the user input loop and allows you to register a custom completer function.
Here's a basic example of how to set it up:
First, define your command grammar in a .prl file.
# my_cli.prl
get (status | version);
set user -name ?;
exit;
Next, write the Python code to load these rules and hook them into readline.
import readline
from pyrl_complete.parser import Parser
from pyrl_complete.parser.tree import Tree
# --- 1. Load and Parse Rules ---
with open("my_cli.prl", "r") as f:
rules_text = f.read()
parser = Parser()
parser.parse(rules_text)
completion_tree = Tree(parser.paths)
# --- 2. Create a Completer Class ---
class PyrlCompleter:
def __init__(self, tree):
self.tree = tree
self.predictions = []
def complete(self, text, state):
# On the first Tab press, generate new predictions
if state == 0:
line_buffer = readline.get_line_buffer()
self.predictions = self.tree.get_predictions(line_buffer)
# Return the next prediction, or None if there are no more
return self.predictions[state] if state < len(self.predictions) else None
# --- 3. Setup Readline ---
completer = PyrlCompleter(completion_tree)
readline.set_completer(completer.complete)
readline.parse_and_bind("tab: complete")
# --- 4. Main Application Loop ---
print("Welcome to the CLI. Type 'exit' to quit.")
while True:
try:
line = input(">> ")
if line.strip() == 'exit':
break
print(f"You entered: {line}")
except (EOFError, KeyboardInterrupt):
break
print("\nGoodbye!")- Load and Parse Rules: We load the rule file, create a
Parser, and then aTreewhich holds our completion logic. - Create a Completer Class: The
PyrlCompleterclass holds the state for our completion.readlinecalls itscompletemethod every time the user hits Tab.- When
stateis0(the first Tab press for the current input), we get the full line fromreadline.get_line_buffer()and ask ourcompletion_treefor new predictions. - For subsequent Tab presses (
state > 0), we simply return the next prediction from the list we already generated.
- When
- Setup Readline: We instantiate our completer and tell
readlineto use it.readline.parse_and_bind("tab: complete")is crucial for making the Tab key trigger the completion function. - Main Loop: A standard
input()loop lets the user interact with the CLI, andreadlineautomatically handles the autocompletion in the background.
The library includes a graphical tester application built with Tkinter that provides a complete environment for writing, parsing, and testing your completion rules in real-time.
To run it, execute the tester.py script:
python pyrl_complete/apps/tester.py-
Write or Load Rules:
- Navigate to the Write Rules tab to write your grammar from scratch in the text editor.
- Alternatively, in the Test Rules tab, click Load Rules to open a
.prlfile from your computer. This will load its content into the editor and switch you to the Write Rules tab.
-
Parse Rules:
- In the Write Rules tab, click the Parse Rules button.
- This will process the grammar in the editor, build the completion tree, and update the path count (e.g., "12 paths generated").
- The application will automatically switch you to the Test Rules tab.
-
Test Completion:
- In the Test Rules tab, start typing a command in the Command line input field.
- As you type, the Predictions list on the right will update with all possible next tokens.
- Press the Tab key to cycle through the predictions and auto-populate the input field.
The application is organized into two main tabs and a log panel.
-
Write Rules Tab: This is your editor. It contains a large text area for writing rules and two primary buttons:
Save Rules: Saves the content of the editor to a.prlfile.Parse Rules: Processes the rules and prepares them for testing.
-
Test Rules Tab: This is your testing ground.
- Rules View (Left): A read-only view of the currently parsed rules.
- Predictions (Right): A list that shows potential completions for the current input.
- Command line input: The field where you type commands and use Tab completion.
-
Log Activity Panel: Located at the bottom of the window, this panel shows a running log of actions like loading files, parsing rules, and which completion was selected, which is useful for debugging.
pyrl-complete has one core external dependency:
- ply: Used for the Lex/Yacc-style parsing of the custom grammar rules.
The included Tester GUI application uses Tkinter, which is part of the Python standard library and should not require a separate installation. However on some linux systems a separate installation is required via the package mager, for example
sudo apt install python3-tk
for Debian/Ubuntu.