How to make a python, command-line program autocom

2020-01-25 12:39发布

I am aware of how to setup autocompletion of python objects in the python interpreter (on unix).

  • Google shows many hits for explanations on how to do this.
  • Unfortunately, there are so many references to that it is difficult to find what I need to do, which is slightly different.

I need to know how to enable, tab/auto completion of arbitrary items in a command-line program written in python.

My specific use case is a command-line python program that needs to send emails. I want to be able to autocomplete email addresses (I have the addresses on disk) when the user types part of it (and optionally presses the TAB key).

I do not need it to work on windows or mac, just linux.

8条回答
叼着烟拽天下
2楼-- · 2020-01-25 12:41

Follow the cmd documentation and you'll be fine

import cmd

addresses = [
    'here@blubb.com',
    'foo@bar.com',
    'whatever@wherever.org',
]

class MyCmd(cmd.Cmd):
    def do_send(self, line):
        pass

    def complete_send(self, text, line, start_index, end_index):
        if text:
            return [
                address for address in addresses
                if address.startswith(text)
            ]
        else:
            return addresses


if __name__ == '__main__':
    my_cmd = MyCmd()
    my_cmd.cmdloop()

Output for tab -> tab -> send -> tab -> tab -> f -> tab

(Cmd)
help  send
(Cmd) send
foo@bar.com            here@blubb.com         whatever@wherever.org
(Cmd) send foo@bar.com
(Cmd)
查看更多
虎瘦雄心在
3楼-- · 2020-01-25 12:45

I am surprised that nobody has mentioned argcomplete, here is an example from the docs:

from argcomplete.completers import ChoicesCompleter

parser.add_argument("--protocol", choices=('http', 'https', 'ssh', 'rsync', 'wss'))
parser.add_argument("--proto").completer=ChoicesCompleter(('http', 'https', 'ssh', 'rsync', 'wss'))
查看更多
看我几分像从前
4楼-- · 2020-01-25 12:45
# ~/.pythonrc
import rlcompleter, readline
readline.parse_and_bind('tab:complete')

# ~/.bashrc
export PYTHONSTARTUP=~/.pythonrc
查看更多
戒情不戒烟
5楼-- · 2020-01-25 12:57

The posted answers work fine but I have open sourced an autocomplete library that I wrote at work. We have been using it for a while in production and it is fast, stable and easy to use. It even has a demo mode so you can quickly test what you would get as you type words.

To install it, simply run: pip install fast-autocomplete

Here is an example:

>>> from fast_autocomplete import AutoComplete
>>> words = {'book': {}, 'burrito': {}, 'pizza': {}, 'pasta':{}}
>>> autocomplete = AutoComplete(words=words)
>>> autocomplete.search(word='b', max_cost=3, size=3)
[['book'], ['burrito']]
>>> autocomplete.search(word='bu', max_cost=3, size=3)
[['burrito']]
>>> autocomplete.search(word='barrito', max_cost=3, size=3)  # mis-spelling
[['burrito']]

Checkout: https://github.com/wearefair/fast-autocomplete for the source code.

And here is an explanation of how it works: http://zepworks.com/posts/you-autocomplete-me/

It deals with mis-spellings and optionally sorting by the weight of the word. (let's say burrito is more important than book, then you give burrito a higher "count" and it will show up first before book in the results.

Words is a dictionary and each word can have a context. For example the "count", how to display the word, some other context around the word etc. In this example words didn't have any context.

查看更多
可以哭但决不认输i
6楼-- · 2020-01-25 12:59

Since you say "NOT interpreter" in your question, I guess you don't want answers involving python readline and suchlike. (edit: in hindsight, that's obviously not the case. Ho hum. I think this info is interesting anyway, so I'll leave it here.)

I think you might be after this.

It's about adding shell-level completion to arbitrary commands, extending bash's own tab-completion.

In a nutshell, you'll create a file containing a shell-function that will generate possible completions, save it into /etc/bash_completion.d/ and register it with the command complete. Here's a snippet from the linked page:

_foo() 
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts="--help --verbose --version"

    if [[ ${cur} == -* ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
        return 0
    fi
}
complete -F _foo foo

In this case, the typing foo --[TAB] will give you the values in the variable opts, i.e. --help, --verbose and --version. For your purposes, you'll essentially want to customise the values that are put into opts.

Do have a look at the example on the linked page, it's all pretty straightforward.

查看更多
对你真心纯属浪费
7楼-- · 2020-01-25 13:03

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.

查看更多
登录 后发表回答