i know i can do this to get the effect of tab completion in python sure.
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: ')
I am now interested in doing tab completion with directories. (/home/user/doc >tab)
How would i go about doing such a task?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
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(' tn;')
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
Method 2
This is enough to enable built in directory tab completion with raw_input():
import readline
readline.parse_and_bind("tab: complete")
Method 3
This version is for python3, uses pathlib, and a minimalistic version that tab completes files/dirs. It is based on some of the above answers, but only works for files/dirs.
#!/usr/bin/python
import pathlib
import readline
def complete_path(text, state):
incomplete_path = pathlib.Path(text)
if incomplete_path.is_dir():
completions = [p.as_posix() for p in incomplete_path.iterdir()]
elif incomplete_path.exists():
completions = [incomplete_path]
else:
exists_parts = pathlib.Path('.')
for part in incomplete_path.parts:
test_next_part = exists_parts / part
if test_next_part.exists():
exists_parts = test_next_part
completions = []
for p in exists_parts.iterdir():
p_str = p.as_posix()
if p_str.startswith(text):
completions.append(p_str)
return completions[state]
# we want to treat '/' as part of a word, so override the delimiters
readline.set_completer_delims(' tn;')
readline.parse_and_bind("tab: complete")
readline.set_completer(complete_path)
print(input('tab complete a filename: '))
Method 4
For path completion
import os
import sys
import readline
import glob
def path_completer(text, state):
"""
This is the tab completer for systems paths.
Only tested on *nix systems
"""
line = readline.get_line_buffer().split()
if '~' in text:
text = os.path.expanduser('~')
return [x for x in glob.glob(text+'*')][state]
if __name__=="__main__":
readline.set_completer_delims('t')
readline.parse_and_bind("tab: complete")
readline.set_completer(path_completer)
ans = input("What file do you want? ")
print(ans)
Note that I’ve refined the code found at https://gist.github.com/iamatypeofwalrus/5637895
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0