AI Skill Report Card

Implementing Tab Completion

A-83·Aug 14, 2026·Source: Web
14 / 15
Python
import readline import rlcompleter readline.parse_and_bind("tab: complete")

That's it for the standard interactive interpreter case—rlcompleter.Completer binds against the __main__ namespace by default when used this way (as python -m startup does via site.py / PYTHONSTARTUP).

Recommendation
Add a troubleshooting section for common readline binding issues across libedit vs GNU readline (macOS default differs)
13 / 15

Progress:

  • Step 1: Decide the namespace to complete against (global __main__ vs. a custom dict/namespace)
  • Step 2: Instantiate rlcompleter.Completer (optionally with a namespace)
  • Step 3: Register the completer with readline.set_completer
  • Step 4: Bind the Tab key via readline.parse_and_bind
  • Step 5: Test simple identifier completion and dotted attribute completion

Step 1–4: Custom namespace example

Python
import readline import rlcompleter namespace = {"foo": 42, "bar": "hello"} completer = rlcompleter.Completer(namespace) readline.set_completer(completer.complete) readline.parse_and_bind("tab: complete")

Step 5: How completion resolves

  • complete(text, state) is called repeatedly by readline with increasing state (0, 1, 2...) until it returns None.
  • No dot in text → matches keywords, builtins, and names in the namespace.
  • Dot in text (e.g. "foo.up") → evaluates the expression before the last dot using eval() in the namespace, then completes attributes via dir(). This means completion can execute arbitrary code (property getters, __getattr__, etc.).
Recommendation
Include an example showing custom completer chaining with existing completions or history search
16 / 20

Example 1: Default interactive interpreter setup Input: Add tab completion to a script meant to be run with python -i myscript.py Output:

Python
try: import readline except ImportError: pass else: import rlcompleter readline.parse_and_bind("tab: complete")

Example 2: Completion inside a sandboxed REPL Input: Build a mini-REPL that only completes names from a restricted namespace, not __main__ Output:

Python
import readline, rlcompleter sandbox_ns = {"__builtins__": {}, "x": [1, 2, 3]} completer = rlcompleter.Completer(sandbox_ns) readline.set_completer(completer.complete) readline.parse_and_bind("tab: complete") while True: line = input(">>> ") exec(line, sandbox_ns)

Example 3: Inspecting raw completion matches (non-interactive/testing) Input: Programmatically get completions for "li" and "str.up" Output:

Python
c = rlcompleter.Completer({"list": list}) i = 0 matches = [] while True: m = c.complete("li", i) if m is None: break matches.append(m) i += 1 # matches -> ['list(', 'list.'] (exact strings vary by Python version) i, attr_matches = 0, [] while True: m = c.complete("str.up", i) if m is None: break attr_matches.append(m) i += 1 # attr_matches -> ['str.upper(']
Recommendation
Consider a brief note on performance implications of eval-based completion in large namespaces
  • Guard import readline in try/except ImportError — it's not available on Windows without pyreadline3 or similar.
  • Prefer letting rlcompleter default to __main__.__dict__ for general-purpose interactive tools; only pass a custom namespace when you need isolation.
  • Combine with atexit + readline.write_history_file for a complete REPL experience (history + completion).
  • Use readline.set_completer_delims() if you need to tweak which characters break "words" for completion (defaults exclude . handling specially inside rlcompleter itself).
  • Remember completion for dotted names calls eval() — only wire this up against namespaces where arbitrary attribute access/property evaluation is acceptable.
  • Forgetting parse_and_bind: instantiating Completer and calling set_completer alone does nothing until Tab is bound.
  • Assuming safety: rlcompleter evaluates expressions to resolve attributes on tab-press, which can trigger side effects (e.g., @property methods with side effects, __getattr__ doing I/O). Never expose this against untrusted/attacker-controlled namespaces.
  • Platform assumption: assuming readline is always importable; it's absent by default on Windows.
  • Confusing module scope: passing a copy of a namespace expecting live updates — Completer holds a reference to the dict, so mutating the original dict is fine, but replacing it wholesale is not reflected unless you re-instantiate or use a Completer with no namespace and rely on __main__.
  • State parameter misuse: manually calling complete(text, state), forgetting that state must increment from 0 and stop at the first None, not stop at empty string.
0
Grade A-AI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
14/15
Workflow
13/15
Examples
16/20
Completeness
18/20
Format
14/15
Conciseness
14/15