Como fazer um python, programa de linha de comando preencher automaticamente coisas arbitrárias NÃO interpretador

92

Estou ciente de como configurar o preenchimento automático de objetos Python no interpretador Python (no Unix).

  • O Google mostra muitos acessos para explicações sobre como fazer isso.
  • Infelizmente, há tantas referências a isso que é difícil encontrar o que preciso fazer, o que é um pouco diferente.

Preciso saber como habilitar o preenchimento automático / tab de itens arbitrários em um programa de linha de comando escrito em python.

Meu caso de uso específico é um programa python de linha de comando que precisa enviar e-mails. Quero poder preencher automaticamente os endereços de e-mail (tenho os endereços no disco) quando o usuário digitar parte deles (e, opcionalmente, pressionar a tecla TAB).

Não preciso dele para funcionar em windows ou mac, apenas linux.

Paul D. Eden
fonte
Este blog deve fazer os truques com a configuração do arquivo .pythonrc.
Kris Roofe

Respostas:

63

Use as readlineligações do Python . Por exemplo,

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)

Os documentos oficiais do módulo não são muito mais detalhados, veja os documentos readline para mais informações.

efêmero
fonte
1
Observe que se você escrever sua linha de comando com o módulo cmd, existem maneiras melhores de fazê-lo.
Florian Bösch,
60

Siga a documentação do cmd e você ficará bem

import cmd

addresses = [
    '[email protected]',
    '[email protected]',
    '[email protected]',
]

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()

Saída para guia -> guia -> enviar -> guia -> guia -> f -> guia

(Cmd)
help  send
(Cmd) send
foo@bar.com            here@blubb.com         whatever@wherever.org
(Cmd) send foo@bar.com
(Cmd)
Florian Bösch
fonte
Existe alguma maneira de controlar como readline coluniza sua saída? Então, digamos que eu gostaria que fosse uma coluna com dois espaços entre cada item.
Fnord
Quando executo este código, as guias são simplesmente impressas na linha de comando. Na verdade, isso é verdade, independentemente de eu usar cmd ou readline direto.
Hack Saw
37

Já que você diz "NÃO é intérprete" em sua pergunta, acho que você não quer respostas envolvendo python readline e coisas do gênero. ( editar : em retrospectiva, obviamente não é o caso. Ei, hum. Acho que essas informações são interessantes de qualquer maneira, então vou deixá-las aqui ) .

Eu acho que você pode estar depois disso .

Trata-se de adicionar o preenchimento no nível do shell a comandos arbitrários, estendendo o preenchimento com tab do próprio bash.

Em poucas palavras, você criará um arquivo contendo uma função de shell que irá gerar possíveis completações, salvá-lo /etc/bash_completion.d/e registrá-lo com o comando complete. Aqui está um snippet da página vinculada:

_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

Nesse caso, a digitação foo --[TAB]fornecerá os valores da variável opts, ou seja --help, --verbosee --version. Para seus propósitos, você essencialmente desejará personalizar os valores que são colocados em opts.

Dê uma olhada no exemplo na página vinculada, é tudo muito simples.

Owen
fonte
10
Na verdade, eu vim aqui por causa disso
user1767754
Obrigado, isso é exatamente o que eu estava procurando!
Teekeks
27

Estou surpreso que ninguém tenha mencionado argcomplete, aqui está um exemplo dos documentos:

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'))
qed
fonte
É um post antigo, talvez argcomplete não existisse naquela época? Obrigado por mencionar isso, porém, acho que é exatamente o que meu projeto precisa!
FrustratedWithFormsDesigner,
Muito bom em combinação com argparse também!
AstroFloyd
13

Aqui está uma versão funcional completa do código que foi fornecido pela ephemient aqui (obrigado).

import readline

addrs = ['[email protected]', '[email protected]', '[email protected]']

def completer(text, state):
    options = [x for x in addrs if x.startswith(text)]
    try:
        return options[state]
    except IndexError:
        return None

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

while 1:
    a = raw_input("> ")
    print "You entered", a
Paul D. Eden
fonte
10
# ~/.pythonrc
import rlcompleter, readline
readline.parse_and_bind('tab:complete')

# ~/.bashrc
export PYTHONSTARTUP=~/.pythonrc
user178047
fonte
Para mac os, substitua readline.parse_and_bind('tab:complete') porreadline.parse_and_bind ("bind ^I rl_complete")
Mani
Isso é incrível. Funcionou para mim. Obrigado por compartilhar.
Ajay Ahuja,
5

Você pode tentar usar o Python Prompt Toolkit , uma biblioteca para construir aplicativos de linha de comando interativos em Python.

A biblioteca facilita a adição de funcionalidade de preenchimento automático interativo, permitindo que o usuário use a Tabchave para alternar visualmente pelas opções disponíveis. A biblioteca é multiplataforma (Linux, OS X, FreeBSD, OpenBSD, Windows). Exemplo:

pgcli - Python Prompt Toolkit

(Fonte da imagem: pcgli )

Fluxo
fonte
1

As respostas postadas funcionam bem, mas abri o código-fonte de uma biblioteca de preenchimento automático que escrevi no trabalho. Nós o usamos há algum tempo na produção e é rápido, estável e fácil de usar. Ele ainda tem um modo de demonstração para que você possa testar rapidamente o que obteria ao digitar palavras.

Para instalá-lo, basta executar: pip install fast-autocomplete

Aqui está um exemplo:

>>> 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 para o código-fonte.

E aqui está uma explicação de como funciona: http://zepworks.com/posts/you-autocomplete-me/

Lida com erros de grafia e, opcionalmente, classificação pelo peso da palavra. (digamos que burritoseja mais importante do que book, então você dá burritouma "contagem" maior e ela aparecerá antes booknos resultados.

Palavras são um dicionário e cada palavra pode ter um contexto. Por exemplo, a "contagem", como exibir a palavra, algum outro contexto em torno da palavra, etc. Neste exemplo, as palavras não têm nenhum contexto.

Seperman
fonte