Tab completion in Python's raw_input()
Here is a quick example of how to perform incremental completion of file system paths. I've modified your example, organizing it into a class where methods named complete_[name]
indicate top-level commands.
I've switched the completion function to use the internal readline buffer to determine the state of the overall completion, which makes the state logic a bit simpler. The path completion is in the _complete_path(path)
method, and I've hooked up the extra command to perform path completions on its arguments.
I'm sure the code could be further simplified but it should provide you a decent starting point:
import os
import re
import readline
COMMANDS = ['extra', 'extension', 'stuff', 'errors',
'email', 'foobar', 'foo']
RE_SPACE = re.compile('.*\s+$', re.M)
class Completer(object):
def _listdir(self, root):
"List directory 'root' appending the path separator to subdirs."
res = []
for name in os.listdir(root):
path = os.path.join(root, name)
if os.path.isdir(path):
name += os.sep
res.append(name)
return res
def _complete_path(self, path=None):
"Perform completion of filesystem path."
if not path:
return self._listdir('.')
dirname, rest = os.path.split(path)
tmp = dirname if dirname else '.'
res = [os.path.join(dirname, p)
for p in self._listdir(tmp) if p.startswith(rest)]
# more than one match, or single match which does not exist (typo)
if len(res) > 1 or not os.path.exists(path):
return res
# resolved to a single directory, so return list of files below it
if os.path.isdir(path):
return [os.path.join(path, p) for p in self._listdir(path)]
# exact file match terminates this completion
return [path + ' ']
def complete_extra(self, args):
"Completions for the 'extra' command."
if not args:
return self._complete_path('.')
# treat the last arg as a path and complete it
return self._complete_path(args[-1])
def complete(self, text, state):
"Generic readline completion entry point."
buffer = readline.get_line_buffer()
line = readline.get_line_buffer().split()
# show all commands
if not line:
return [c + ' ' for c in COMMANDS][state]
# account for last argument ending in a space
if RE_SPACE.match(buffer):
line.append('')
# resolve command to the implementation function
cmd = line[0].strip()
if cmd in COMMANDS:
impl = getattr(self, 'complete_%s' % cmd)
args = line[1:]
if args:
return (impl(args) + [None])[state]
return [cmd + ' '][state]
results = [c + ' ' for c in COMMANDS if c.startswith(cmd)] + [None]
return results[state]
comp = Completer()
# we want to treat '/' as part of a word, so override the delimiters
readline.set_completer_delims(' \t\n;')
readline.parse_and_bind("tab: complete")
readline.set_completer(comp.complete)
raw_input('Enter section name: ')
Usage:
% python complete.py
Enter section name: ext<tab>
extension extra
Enter section name: extra foo<tab>
foo.py foo.txt foo/
Enter section name: extra foo/<tab>
foo/bar.txt foo/baz.txt
Enter section name: extra foo/bar.txt
Update It will complete paths from the root if the user types /
:
% python complete.py
Enter section name: extra /Use<tab>
/Users/.localized /Users/Shared/ /Users/user1 /Users/user2
Enter section name: extra /Users/use<tab>
/Users/user1 /Users/user2
Tab Completion in Python Command Line Interface - how to catch Tab events
For that you use the readline
module.
Simplest code I can think:
import readline
COMMANDS = ['extra', 'extension', 'stuff', 'errors',
'email', 'foobar', 'foo']
def complete(text, state):
for cmd in COMMANDS:
if cmd.startswith(text):
if not state:
return cmd
else:
state -= 1
readline.parse_and_bind("tab: complete")
readline.set_completer(complete)
raw_input('Enter section name: ')
Example usage:
Enter section name: <tab>
email errors extension extra foo foobar stuff
Enter section name: e<tab>
email errors extension extra
Enter section name: ext<tab>
extension extra
Besides completion, readline
provides you with:
- Line editing
- Keybinding configuration (emacs and vi modes included)
- History (up arrow to recall previous values)
- History searching, saving and loading
How to make a Python script tab-complete directories in terminal?
Set sensible completion delimiters:
import readline
readline.set_completer_delims(' \t\n=')
readline.parse_and_bind("tab: complete")
option = input("Tab complete a file: ")
By default, readline
will delimit based on any of the following:
>>> import readline
>>> readline.get_completer_delims()
' \t\n`~!@#$%^&*()-=+[{]}\\|;:\'",<>/?'
Since /
is part of this set, anything after a /
will be completed independently of anything before it. This obviously makes no sense when you're trying to complete a file path.
raw_input without leaving a history in readline
You could make a function something like
import readline
def raw_input_no_history():
input = raw_input()
readline.remove_history_item(readline.get_current_history_length()-1)
return input
and call that function instead of raw_input. You may not need the minus 1 dependent on where you call it from.
How to stop autocomplete and have normal tab with GNU readline library in Python
You can use
#allow for arrow keys to be used for raw_input.
readline.parse_and_bind('set editing-mode vi')
#set the tab key to make 4 spaces
readline.parse_and_bind("TAB: ' '")
For some reason, using readline.parse_and_bind("TAB: '\t'")
caused Python to use way too much of the CPU and would just freeze the screen, so I had to switch it to use spaces.
Related Topics
Fix Not Load Dynamic Library for Tensorflow Gpu
Weighted Choice Short and Simple
Using a Python Subprocess Call to Invoke a Python Script
Matplotlib: How to Draw a Rectangle on Image
Error: Pg_Config Executable Not Found When Installing Psycopg2 on Alpine in Docker
Unicodeencodeerror: 'Ascii' Codec Can't Encode Character '\Xe9' - -When Using Urlib.Request Python3
Why Does Indexing Numpy Arrays with Brackets and Commas Differ in Behavior
Where to Put Django Startup Code
Python Generator That Groups Another Iterable into Groups of N
How to Run a Python Script in a Web Page
Matplotlib Figure Facecolor (Background Color)
Panda's Dataframe - Renaming Multiple Identically Named Columns
Install MySQL-Python (Windows)
How to Do N-D Distance and Nearest Neighbor Calculations on Numpy Arrays