Cómo hacer un Python, programa de línea de comandos que autocomplete cosas arbitrarias NO interprete

92

Soy consciente de cómo configurar el autocompletado de objetos de Python en el intérprete de Python (en Unix).

  • Google muestra muchos resultados para obtener explicaciones sobre cómo hacer esto.
  • Desafortunadamente, hay tantas referencias que es difícil encontrar lo que necesito hacer, que es un poco diferente.

Necesito saber cómo habilitar, tabulación / finalización automática de elementos arbitrarios en un programa de línea de comandos escrito en Python.

Mi caso de uso específico es un programa de Python de línea de comandos que necesita enviar correos electrónicos. Quiero poder autocompletar direcciones de correo electrónico (tengo las direcciones en el disco) cuando el usuario escribe parte de ellas (y, opcionalmente, presiona la tecla TAB).

No lo necesito para funcionar en Windows o Mac, solo Linux.

Paul D. Eden
fuente
Este blog debería hacer los trucos con la configuración del archivo .pythonrc.
Kris Roofe

Respuestas:

63

Usa los readlineenlaces de Python . Por ejemplo,

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)

Los documentos oficiales del módulo no son mucho más detallados, consulte los documentos de readline para obtener más información.

efímero
fuente
1
Tenga en cuenta que si escribe su línea de comando con el módulo cmd, hay mejores formas de hacerlo.
Florian Bösch
60

Siga la documentación de cmd y estará bien

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

Salida para pestaña -> pestaña -> enviar -> pestaña -> pestaña -> f -> pestaña

(Cmd)
help  send
(Cmd) send
foo@bar.com            here@blubb.com         whatever@wherever.org
(Cmd) send foo@bar.com
(Cmd)
Florian Bösch
fuente
¿Hay alguna forma de controlar cómo readline columniza su salida? Entonces, digamos que me gustaría que se colocara en columnas con dos espacios entre cada elemento.
Fnord
Cuando ejecuto este código, las pestañas simplemente se imprimen en la línea de comando. De hecho, esto es cierto independientemente de si uso cmd o readline recta.
Hack Saw
38

Dado que dice "NO intérprete" en su pregunta, supongo que no quiere respuestas que involucren python readline y cosas por el estilo. ( editar : en retrospectiva, obviamente ese no es el caso. Ho hum. Creo que esta información es interesante de todos modos, así que la dejaré aquí. )

Creo que podrías estar después de esto .

Se trata de agregar finalización a nivel de shell a comandos arbitrarios, extendiendo la finalización de tabulación propia de bash.

En pocas palabras, creará un archivo que contiene una función de shell que generará posibles finalizaciones, lo guardará /etc/bash_completion.d/y lo registrará con el comando complete. Aquí hay un fragmento de la 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

En este caso, la escritura foo --[TAB]le dará los valores de la variable opts, es decir --help, --verbosey --version. Para sus propósitos, esencialmente querrá personalizar los valores que se colocan enopts .

Eche un vistazo al ejemplo en la página vinculada, todo es bastante sencillo.

Owen
fuente
10
En realidad vine aquí por eso
user1767754
¡Gracias, esto es exactamente lo que estaba buscando!
Teekeks
27

Me sorprende que nadie haya mencionado argcomplete, aquí hay un ejemplo de los 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
fuente
Es una publicación antigua, ¿tal vez argcomplete no existía en ese entonces? Gracias por mencionarlo, creo que es exactamente lo que necesita mi proyecto.
FrustratedWithFormsDesigner
¡Muy agradable en combinación con argparse también!
AstroFloyd
13

Aquí hay una versión completa del código que fue muy proporcionada por ephemient here (gracias).

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
fuente
10
# ~/.pythonrc
import rlcompleter, readline
readline.parse_and_bind('tab:complete')

# ~/.bashrc
export PYTHONSTARTUP=~/.pythonrc
user178047
fuente
1
Para mac os, reemplace readline.parse_and_bind('tab:complete') conreadline.parse_and_bind ("bind ^I rl_complete")
Mani
Esto es asombroso. Trabajó para mi. Gracias por compartir.
Ajay Ahuja
@Mani Estuve atrapado en esto durante mucho tiempo. Muchas gracias
AnaS Kayed
5

Puede intentar usar Python Prompt Toolkit , una biblioteca para crear aplicaciones interactivas de línea de comandos en Python.

La biblioteca facilita la adición de la funcionalidad interactiva de autocompletar, lo que permite al usuario usar la Tabclave para recorrer visualmente las opciones disponibles. La biblioteca es multiplataforma (Linux, OS X, FreeBSD, OpenBSD, Windows). Ejemplo:

pgcli - Kit de herramientas de solicitud de Python

(Fuente de la imagen: pcgli )

Flujo
fuente
1

Las respuestas publicadas funcionan bien, pero he abierto una biblioteca de autocompletar que escribí en el trabajo. Lo hemos estado usando durante un tiempo en producción y es rápido, estable y fácil de usar. Incluso tiene un modo de demostración para que pueda probar rápidamente lo que obtendría mientras escribe palabras.

Para instalarlo, simplemente ejecute: pip install fast-autocomplete

Aquí hay un ejemplo:

>>> 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']]

Pago: https://github.com/wearefair/fast-autocomplete para el código fuente.

Y aquí hay una explicación de cómo funciona: http://zepworks.com/posts/you-autocomplete-me/

Se trata de errores ortográficos y, opcionalmente, de ordenar por el peso de la palabra. (digamos que burritoes más importante que book, entonces le da burritoun "recuento" más alto y aparecerá primero antesbook en los resultados.

Words es un diccionario y cada palabra puede tener un contexto. Por ejemplo, el "recuento", cómo mostrar la palabra, algún otro contexto alrededor de la palabra, etc. En este ejemplo, las palabras no tenían ningún contexto.

Seperman
fuente