python readline path completion
So I wanted readline to 'just work' when entering paths in conjunction with raw_input(). Turns out that this is rather strange, undocumented and full of tripwires. This is how i did it:
import os
import sys
import readline
def list_folder(path):
"""
Lists folder contents
"""
if path.startswith(os.path.sep):
# absolute path
basedir = os.path.dirname(path)
contents = os.listdir(basedir)
# add back the parent
contents = [os.path.join(basedir, d) for d in contents]
else:
# relative path
contents = os.listdir(os.curdir)
return contents
def completer(text, state):
"""
Our custom completer function
"""
options = [x for x in list_folder(text) if x.startswith(text)]
return options[state]
readline.set_completer(completer)
if sys.platform == 'darwin':
# Apple uses libedit.
readline.parse_and_bind("bind -e")
readline.parse_and_bind("bind '\t' rl_complete")
else:
# Some tweaks for linux
readline.parse_and_bind('tab: complete')
readline.set_completer_delims(' \t\n`~!@#$%^&*()-=+[{]}\\|;:\'",<>?')
### Try out the stuff:
while True:
last_string = raw_input('? ')
print 'Last input:', last_string
Basically, readline would not work at all under MacOS until you use the little dance above.
Assuming it would work out of the box under linux was wrong as well, since os.path.sep (/
for that matter) is in the list of completer_delims, resulting in the fact that it is not being passed to the completer function. For example, sending it /home
will just make it receive home
. Fortunately, you can override this with readline.set_completer_delims()
.
For even more user-friendlyness, you could add a trailing slash in the list_folder function, like: contents = [d + os.path.sep for d in contents if os.path.isdir(d)]
.