Tab Completion in Python's Raw_Input()

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



Leave a reply



Submit