How to Make a Python, Command-Line Program Autocomplete Arbitrary Things Not Interpreter

How to make a python, command-line program autocomplete arbitrary things NOT interpreter

Use Python's readline bindings. For example,

import readline

def completer(text, state):
options = [i for i in commands if i.startswith(text)]
if state < len(options):
return options[state]
else:
return None

readline.parse_and_bind("tab: complete")
readline.set_completer(completer)

The official module docs aren't much more detailed, see the readline docs for more info.

Howto do python command-line autocompletion but NOT only at the beginning of a string

I'm not sure I understand the problem. You could use readline.clear_history and readline.add_history to set up the completable strings you want, then control-r to search backword in the history (just as if you were at a shell prompt). For example:

#!/usr/bin/env python

import readline

readline.clear_history()
readline.add_history('foo')
readline.add_history('bar')

while 1:
print raw_input('> ')

Alternatively, you could write your own completer version and bind the appropriate key to it. This version uses caching in case your match list is huge:

#!/usr/bin/env python

import readline

values = ['Paul Eden <paul@domain.com>',
'Eden Jones <ejones@domain.com>',
'Somebody Else <somebody@domain.com>']
completions = {}

def completer(text, state):
try:
matches = completions[text]
except KeyError:
matches = [value for value in values
if text.upper() in value.upper()]
completions[text] = matches
try:
return matches[state]
except IndexError:
return None

readline.set_completer(completer)
readline.parse_and_bind('tab: menu-complete')

while 1:
a = raw_input('> ')
print 'said:', a

Change how Python Cmd Module handles autocompletion

It shouldn't need to be overly complicated. Something like the following:

import cmd

completions = [
'Mage Slayer (Alara Reborn)',
'Magefire Wings (Alara Reborn)',
'Sages of the Anima (Alara Reborn)',
'Sanctum Plowbeast (Alara Reborn)',
'Sangrite Backlash (Alara Reborn)',
'Sanity Gnawers (Alara Reborn)',
'Sen Triplets (Alara Reborn)'
]

class mycmd(cmd.Cmd):
def __init__(self):
cmd.Cmd.__init__(self)

def do_quit(self, s):
return True

def do_add(self, s):
pass

def complete_add(self, text, line, begidx, endidx):
mline = line.partition(' ')[2]
offs = len(mline) - len(text)
return [s[offs:] for s in completions if s.startswith(mline)]

if __name__ == '__main__':
mycmd().cmdloop()

How to write code to autocomplete words and sentences?

(I'm aware this isn't exactly what you're asking for, but) If you're happy with the auto-completion/suggestions appearing on TAB (as used in many shells), then you can quickly get up and running using the readline module.

Here's a quick example based on Doug Hellmann's PyMOTW writeup on readline.

import readline

class MyCompleter(object): # Custom completer

def __init__(self, options):
self.options = sorted(options)

def complete(self, text, state):
if state == 0: # on first trigger, build possible matches
if text: # cache matches (entries that start with entered text)
self.matches = [s for s in self.options
if s and s.startswith(text)]
else: # no text entered, all matches possible
self.matches = self.options[:]

# return match indexed by state
try:
return self.matches[state]
except IndexError:
return None

completer = MyCompleter(["hello", "hi", "how are you", "goodbye", "great"])
readline.set_completer(completer.complete)
readline.parse_and_bind('tab: complete')

input = raw_input("Input: ")
print "You entered", input

This results in the following behaviour (<TAB> representing a the tab key being pressed):

Input: <TAB><TAB>
goodbye great hello hi how are you

Input: h<TAB><TAB>
hello hi how are you

Input: ho<TAB>ow are you

In the last line (HOTAB entered), there is only one possible match and the whole sentence "how are you" is auto completed.

Check out the linked articles for more information on readline.


"And better yet would be if it would complete words not only from the beginning ... completion from arbitrary part of the string."

This can be achieved by simply modifying the match criteria in the completer function, ie. from:

self.matches = [s for s in self.options 
if s and s.startswith(text)]

to something like:

self.matches = [s for s in self.options 
if text in s]

This will give you the following behaviour:

Input: <TAB><TAB>
goodbye great hello hi how are you

Input: o<TAB><TAB>
goodbye hello how are you

Updates: using the history buffer (as mentioned in comments)

A simple way to create a pseudo-menu for scrolling/searching is to load the keywords into the history buffer. You will then be able to scroll through the entries using the up/down arrow keys as well as use Ctrl+R to perform a reverse-search.

To try this out, make the following changes:

keywords = ["hello", "hi", "how are you", "goodbye", "great"]
completer = MyCompleter(keywords)
readline.set_completer(completer.complete)
readline.parse_and_bind('tab: complete')
for kw in keywords:
readline.add_history(kw)

input = raw_input("Input: ")
print "You entered", input

When you run the script, try typing Ctrl+r followed by a. That will return the first match that contains "a". Enter Ctrl+r again for the next match. To select an entry, press ENTER.

Also try using the UP/DOWN keys to scroll through the keywords.

How to enable python repl autocomplete and still allow new line tabs

You should just use IPython. It has both tab completion and auto-indenting of for loops or function definitions. For example:

# Ipython prompt
In [1]: def stuff(x):
...: |
# ^ cursor automatically moves to this position

To install it, you can use pip:

pip install ipython

If you don't have pip installed, you can follow the instructions on this page. On python >= 3.4, pip is installed by default.

If you're on windows, this page contains installers for ipython (and many other python libraries that may be difficult to install).


However, if for any reason you can't install ipython, Brandon Invergo had created a python start-up script that adds several features to the python interpreter, among which is auto indentation. He has released it under GPL v3 and published the source here.

I've copied the code that handles the auto-indentation below. I had to add indent = '' at line 11 to make it work on my python 3.4 interpreter.

import readline

def rl_autoindent():
"""Auto-indent upon typing a new line according to the contents of the
previous line. This function will be used as Readline's
pre-input-hook.

"""
hist_len = readline.get_current_history_length()
last_input = readline.get_history_item(hist_len)
indent = ''
try:
last_indent_index = last_input.rindex(" ")
except:
last_indent = 0
else:
last_indent = int(last_indent_index / 4) + 1
if len(last_input.strip()) > 1:
if last_input.count("(") > last_input.count(")"):
indent = ''.join([" " for n in range(last_indent + 2)])
elif last_input.count(")") > last_input.count("("):
indent = ''.join([" " for n in range(last_indent - 1)])
elif last_input.count("[") > last_input.count("]"):
indent = ''.join([" " for n in range(last_indent + 2)])
elif last_input.count("]") > last_input.count("["):
indent = ''.join([" " for n in range(last_indent - 1)])
elif last_input.count("{") > last_input.count("}"):
indent = ''.join([" " for n in range(last_indent + 2)])
elif last_input.count("}") > last_input.count("{"):
indent = ''.join([" " for n in range(last_indent - 1)])
elif last_input[-1] == ":":
indent = ''.join([" " for n in range(last_indent + 1)])
else:
indent = ''.join([" " for n in range(last_indent)])
readline.insert_text(indent)

readline.set_pre_input_hook(rl_autoindent)

PyCharm autocomplete fails so show all options

discord.Client uses __getattr__ to handle attribute lookup for the user attribute dynamically. __getattr__ is fundamentally incompatible with the kind of static analysis PyCharm uses for autocompletion.

I'd probably just live with PyCharm not being able to find this attribute. If you want to do something about it anyway, writing type stubs might help (or they might go out of sync with the implementation and cause more problems), or you could see if discord.py would accept a pull request to use properties instead of __getattr__/__setattr__ for the attribute forwarding they're doing here.



Related Topics



Leave a reply



Submit