# pyrl-complete
Python readline completer and command line parser
## What is pyrl-complete?
`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.
## Key Features
- **Custom Grammar**: Define your CLI structure in a simple `.prl` file. The syntax supports:
- Simple command sequences (`get status`)
- Alternatives using `|` (`get (one | two)`)
- Optional groups using `[]` (`command [optional_part]`)
- Options with placeholder arguments (`-d ?`)
- **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.
## Rules Syntax
The grammar for defining your command-line interface is designed to be simple and flexible. Rules are typically defined in a `.prl` file.
#### Statements
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;
```
#### Alternatives (`|`) and Grouping (`()`)
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 ?;
```
#### Optional Groups (`[]`)
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];
```
#### Options and Arguments (`-` and `?`)
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 ?
```
## How it Works
1. **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];
```
2. **Parse**: The library's parser (built with `ply`) reads your rules and generates a list of all valid command paths.
3. **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.
## Usage with a Python CLI
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:
#### 1. Your Rules (`my_cli.prl`)
First, define your command grammar in a `.prl` file.
```
# my_cli.prl
get (status | version);
set user -name ?;
exit;
```
#### 2. Your Python Application (`my_cli.py`)
Next, write the Python code to load these rules and hook them into `readline`.
```python
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!")
```
### How the Example Works
1. **Load and Parse Rules**: We load the rule file, create a `Parser`, and then a `Tree` which holds our completion logic.
2. **Create a Completer Class**: The `PyrlCompleter` class holds the state for our completion. `readline` calls its `complete` method every time the user hits Tab.
* When `state` is `0` (the first Tab press for the current input), we get the full line from `readline.get_line_buffer()` and ask our `completion_tree` for new predictions.
* For subsequent Tab presses (`state > 0`), we simply return the next prediction from the list we already generated.
3. **Setup Readline**: We instantiate our completer and tell `readline` to use it. `readline.parse_and_bind("tab: complete")` is crucial for making the Tab key trigger the completion function.
4. **Main Loop**: A standard `input()` loop lets the user interact with the CLI, and `readline` automatically handles the autocompletion in the background.
## Using the Tester GUI
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:
```bash
python pyrl_complete/apps/tester.py
```
### Workflow
1. **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 `.prl` file from your computer. This will load its content into the editor and switch you to the **Write Rules** tab.
2. **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.
3. **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.
### Interface Overview
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 `.prl` file.
* `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.
## Dependencies
`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 does not require a separate installation.
Raw data
{
"_id": null,
"home_page": "https://github.com/codimoc/pyrl-complete",
"name": "pyrl-complete",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "readline, autocomplete, cli, ply",
"author": "codimoc",
"author_email": "codimoc@prismoid.uk",
"download_url": "https://files.pythonhosted.org/packages/15/12/693d48d685b7b21e79a93140af18c1276d1ac51ff9760da7a0a35abf9efd/pyrl_complete-0.1.1.tar.gz",
"platform": null,
"description": "# pyrl-complete\nPython readline completer and command line parser\n\n## What is pyrl-complete?\n\n`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.\n\nThe 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.\n\n## Key Features\n\n- **Custom Grammar**: Define your CLI structure in a simple `.prl` file. The syntax supports:\n - Simple command sequences (`get status`)\n - Alternatives using `|` (`get (one | two)`)\n - Optional groups using `[]` (`command [optional_part]`)\n - Options with placeholder arguments (`-d ?`)\n- **Completion Engine**: A prefix-tree-based engine that provides:\n - **Suggestions**: A list of all possible valid commands that match the current input.\n - **Predictions**: The most likely next word or token based on the current input.\n- **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.\n\n## Rules Syntax\n\nThe grammar for defining your command-line interface is designed to be simple and flexible. Rules are typically defined in a `.prl` file.\n\n#### Statements\n\nEach complete command path is a statement. Statements can be separated by a **newline** or a **semicolon (`;`)**.\n\n```\n# Separated by newline\nget status\nset user\n\n# Separated by semicolon\nget status; set user;\n```\n\n#### Alternatives (`|`) and Grouping (`()`)\n\nThe 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.\n\n```\n# Creates two valid paths: 'set user name ?' and 'set group name ?'\nset (user | group) name ?;\n```\n\n#### Optional Groups (`[]`)\n\nSquare brackets **`[ ... ]`** define an optional part of a command. The command is valid with or without the tokens inside the brackets.\n\n```\n# Creates three valid paths: 'show config', 'show interfaces', and just 'show'\nshow [config | interfaces];\n```\n\n#### Options and Arguments (`-` and `?`)\n\nTokens 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.\n\n```\n# An option without an argument\nget -h\n\n# An option that requires an argument\nset user -name ?\n```\n\n## How it Works\n\n1. **Define Rules**: You write your command structure in a text file (e.g., `rules.prl`).\n ```\n # Example Rules\n get status;\n set (user | group) name ?;\n show [config | interfaces];\n ```\n2. **Parse**: The library's parser (built with `ply`) reads your rules and generates a list of all valid command paths.\n3. **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.\n\nThis library provides the core components to build a rich autocompletion experience for any Python-based CLI application.\n\n## Usage with a Python CLI\n\nTo 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.\n\nHere's a basic example of how to set it up:\n\n#### 1. Your Rules (`my_cli.prl`)\n\nFirst, define your command grammar in a `.prl` file.\n\n```\n# my_cli.prl\nget (status | version);\nset user -name ?;\nexit;\n```\n\n#### 2. Your Python Application (`my_cli.py`)\n\nNext, write the Python code to load these rules and hook them into `readline`.\n\n```python\nimport readline\nfrom pyrl_complete.parser import Parser\nfrom pyrl_complete.parser.tree import Tree\n\n# --- 1. Load and Parse Rules ---\nwith open(\"my_cli.prl\", \"r\") as f:\n rules_text = f.read()\n\nparser = Parser()\nparser.parse(rules_text)\ncompletion_tree = Tree(parser.paths)\n\n# --- 2. Create a Completer Class ---\nclass PyrlCompleter:\n def __init__(self, tree):\n self.tree = tree\n self.predictions = []\n\n def complete(self, text, state):\n # On the first Tab press, generate new predictions\n if state == 0:\n line_buffer = readline.get_line_buffer()\n self.predictions = self.tree.get_predictions(line_buffer)\n\n # Return the next prediction, or None if there are no more\n return self.predictions[state] if state < len(self.predictions) else None\n\n# --- 3. Setup Readline ---\ncompleter = PyrlCompleter(completion_tree)\nreadline.set_completer(completer.complete)\nreadline.parse_and_bind(\"tab: complete\")\n\n# --- 4. Main Application Loop ---\nprint(\"Welcome to the CLI. Type 'exit' to quit.\")\nwhile True:\n try:\n line = input(\">> \")\n if line.strip() == 'exit':\n break\n print(f\"You entered: {line}\")\n except (EOFError, KeyboardInterrupt):\n break\nprint(\"\\nGoodbye!\")\n```\n\n### How the Example Works\n\n1. **Load and Parse Rules**: We load the rule file, create a `Parser`, and then a `Tree` which holds our completion logic.\n2. **Create a Completer Class**: The `PyrlCompleter` class holds the state for our completion. `readline` calls its `complete` method every time the user hits Tab.\n * When `state` is `0` (the first Tab press for the current input), we get the full line from `readline.get_line_buffer()` and ask our `completion_tree` for new predictions.\n * For subsequent Tab presses (`state > 0`), we simply return the next prediction from the list we already generated.\n3. **Setup Readline**: We instantiate our completer and tell `readline` to use it. `readline.parse_and_bind(\"tab: complete\")` is crucial for making the Tab key trigger the completion function.\n4. **Main Loop**: A standard `input()` loop lets the user interact with the CLI, and `readline` automatically handles the autocompletion in the background.\n\n## Using the Tester GUI\n\nThe 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.\n\nTo run it, execute the `tester.py` script:\n```bash\npython pyrl_complete/apps/tester.py\n```\n\n### Workflow\n\n1. **Write or Load Rules**:\n * Navigate to the **Write Rules** tab to write your grammar from scratch in the text editor.\n * Alternatively, in the **Test Rules** tab, click **Load Rules** to open a `.prl` file from your computer. This will load its content into the editor and switch you to the **Write Rules** tab.\n\n2. **Parse Rules**:\n * In the **Write Rules** tab, click the **Parse Rules** button.\n * This will process the grammar in the editor, build the completion tree, and update the path count (e.g., \"12 paths generated\").\n * The application will automatically switch you to the **Test Rules** tab.\n\n3. **Test Completion**:\n * In the **Test Rules** tab, start typing a command in the **Command line input** field.\n * As you type, the **Predictions** list on the right will update with all possible next tokens.\n * Press the **Tab** key to cycle through the predictions and auto-populate the input field.\n\n### Interface Overview\n\nThe application is organized into two main tabs and a log panel.\n\n* **Write Rules Tab**: This is your editor. It contains a large text area for writing rules and two primary buttons:\n * `Save Rules`: Saves the content of the editor to a `.prl` file.\n * `Parse Rules`: Processes the rules and prepares them for testing.\n\n* **Test Rules Tab**: This is your testing ground.\n * **Rules View (Left)**: A read-only view of the currently parsed rules.\n * **Predictions (Right)**: A list that shows potential completions for the current input.\n * **Command line input**: The field where you type commands and use Tab completion.\n\n* **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.\n\n## Dependencies\n\n`pyrl-complete` has one core external dependency:\n\n- **ply**: Used for the Lex/Yacc-style parsing of the custom grammar rules.\n\nThe included Tester GUI application uses **Tkinter**, which is part of the Python standard library and does not require a separate installation.\n",
"bugtrack_url": null,
"license": "GNU AFFERO GENERAL PUBLIC LICENSE Version 3",
"summary": "manage readline autocomplete for bespoke cli",
"version": "0.1.1",
"project_urls": {
"Homepage": "https://github.com/codimoc/pyrl-complete",
"Repository": "https://github.com/codimoc/pyrl-complete"
},
"split_keywords": [
"readline",
" autocomplete",
" cli",
" ply"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "6797f8d5ba5274cd49155dc236070ce402c562a4945dcccb9f29e6d8f6c10d54",
"md5": "cd8e14881eb50aa071b32eda8ba417a8",
"sha256": "5eec02f9b057a001d087424457c22527f214b2607f69a2cd03072a6697d1caf4"
},
"downloads": -1,
"filename": "pyrl_complete-0.1.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "cd8e14881eb50aa071b32eda8ba417a8",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 26890,
"upload_time": "2025-08-06T19:35:48",
"upload_time_iso_8601": "2025-08-06T19:35:48.398837Z",
"url": "https://files.pythonhosted.org/packages/67/97/f8d5ba5274cd49155dc236070ce402c562a4945dcccb9f29e6d8f6c10d54/pyrl_complete-0.1.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "1512693d48d685b7b21e79a93140af18c1276d1ac51ff9760da7a0a35abf9efd",
"md5": "9783e43d63c851fa9c9fc0b337be0db2",
"sha256": "427cbeeaf0b2d74a886a1de58625f17c51ed257c0a4d1ad09bed3057acf46e97"
},
"downloads": -1,
"filename": "pyrl_complete-0.1.1.tar.gz",
"has_sig": false,
"md5_digest": "9783e43d63c851fa9c9fc0b337be0db2",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 26234,
"upload_time": "2025-08-06T19:35:49",
"upload_time_iso_8601": "2025-08-06T19:35:49.606797Z",
"url": "https://files.pythonhosted.org/packages/15/12/693d48d685b7b21e79a93140af18c1276d1ac51ff9760da7a0a35abf9efd/pyrl_complete-0.1.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-06 19:35:49",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "codimoc",
"github_project": "pyrl-complete",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "pyrl-complete"
}