AI Skill Report Card

Implementing Readline Features

A-85·Aug 14, 2026·Source: Web
15 / 15
Python
import readline import atexit import os histfile = os.path.join(os.path.expanduser("~"), ".myapp_history") try: readline.read_history_file(histfile) readline.set_history_length(1000) except FileNotFoundError: pass atexit.register(readline.write_history_file, histfile) def completer(text, state): options = [cmd for cmd in COMMANDS if cmd.startswith(text)] if state < len(options): return options[state] return None readline.set_completer(completer) readline.parse_and_bind("tab: complete") while True: line = input("prompt> ")

Note: readline is Unix-only (GNU readline or libedit). On Windows, use pyreadline3 as a drop-in substitute, guarded by a try/except import.

Recommendation
Add a 'bad outcome' example showing broken completion due to wrong delims or missing None return, not just correct usage
14 / 15

Progress:

  • Step 1: Import readline with platform guard
  • Step 2: Set up history file (load on start, save on exit)
  • Step 3: Configure history length limit
  • Step 4: Implement completer function (stateful, index-based)
  • Step 5: Bind completer with parse_and_bind
  • Step 6: Set completer delimiters if needed (e.g., for path/attribute completion)
  • Step 7: Test on target platforms (libedit vs GNU readline have different bind syntax)

Step 1: Platform-safe import

Python
try: import readline except ImportError: readline = None # Windows fallback or degrade gracefully

Step 2-3: History management

Python
import readline, atexit, os histfile = os.path.expanduser("~/.app_history") try: readline.read_history_file(histfile) except FileNotFoundError: open(histfile, 'wb').close() readline.set_history_length(1000) atexit.register(readline.write_history_file, histfile)

Use readline.append_history_file(n, filename) for multi-session append instead of full overwrite when concurrent sessions are possible.

Step 4-5: Completion

The completer is called repeatedly with increasing state until it returns None:

Python
def make_completer(options): def completer(text, state): matches = [o for o in options if o.startswith(text)] try: return matches[state] except IndexError: return None return completer readline.set_completer(make_completer(["start", "stop", "status"])) readline.parse_and_bind("tab: complete")

For attribute/path-like completion, adjust delimiters:

Python
readline.set_completer_delims(' \t\n`~!@#$%^&*()-=+[{]}\\|;:\'",<>/?')

Step 6: Detect libedit vs GNU readline (macOS defaults to libedit)

Python
if readline.__doc__ and 'libedit' in readline.__doc__: readline.parse_and_bind("bind ^I rl_complete") else: readline.parse_and_bind("tab: complete")
Recommendation
Include a brief troubleshooting section mapping symptoms (e.g., 'Tab does nothing') to causes
17 / 20

Example 1: Filesystem path completion

Input: User types ls /usr/lo<TAB> Output: Completer inspects text = "/usr/lo", globs matching paths, returns /usr/local/ on first Tab press.

Python
import glob def path_completer(text, state): matches = glob.glob(text + '*') matches = [m + '/' if os.path.isdir(m) else m for m in matches] return matches[state] if state < len(matches) else None readline.set_completer(path_completer) readline.set_completer_delims(' \t\n') readline.parse_and_bind("tab: complete")

Example 2: Persistent cross-session history

Input: App run 1 enters deploy prod, exits; app run 2 starts. Output: Pressing Up-arrow in run 2 immediately recalls deploy prod because read_history_file loaded it at startup.

Example 3: Custom key binding

Input: Bind Ctrl-L to clear screen instead of default behavior. Output:

Python
readline.parse_and_bind(r'"\C-l": clear-screen')
Recommendation
Consider showing pyreadline3 setup snippet for Windows to complete the platform story
  • Always wrap read_history_file in try/except FileNotFoundError — file won't exist on first run.
  • Cap history with set_history_length() to avoid unbounded growth.
  • Register write_history_file via atexit, not manually at every exit point.
  • Use readline.set_pre_input_hook() sparingly — it's for pre-filling input, not general logic.
  • When completions are expensive (e.g., DB lookups), cache results per text prefix within a single completion cycle since state=0,1,2... calls happen in quick succession.
  • Strip readline's own history entries for sensitive input (passwords) — use input()'s underlying getpass instead, since readline will persist everything to history.
  • Check readline.__doc__ for 'libedit' to branch bind syntax on macOS.
  • Assuming readline exists on Windows — it doesn't ship in stdlib there; always guard the import.
  • Forgetting the completer must return None to terminate, not raise or return empty string — infinite state loops otherwise.
  • Not resetting completer_delims when completing things like file paths — default delims split on /, breaking full-path suggestions.
  • Writing history file on every input line — expensive; let atexit handle it once, or use append_history_file periodically instead.
  • Logging passwords into history — never route sensitive prompts through a readline-enabled input() without disabling history for that call.
  • Ignoring libedit differences on macOSparse_and_bind("tab: complete") silently fails to bind Tab under libedit; needs bind ^I rl_complete.
0
Grade A-AI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
15/15
Workflow
14/15
Examples
17/20
Completeness
18/20
Format
14/15
Conciseness
14/15