He probado optcomplete
trabajando con el módulo optparse
. Su ejemplo es un archivo simple para que pueda hacer que funcione. También lo probé usando el módulo argparse
ya que el anterior está en desuso. Pero realmente no entiendo cómo y por quién se llama al progtwig Python en las pulsaciones de tabs. Sospecho que bash
junto con la línea shebang
y el argparse
(o optparse
) están involucrados de alguna manera. He estado tratando de resolver esto (ahora voy a leer el código fuente).
Tengo una estructura de progtwig un poco más compleja, que incluye una envoltura alrededor de la pieza de código que maneja los argumentos. Su instancia argparse.ArgumentParser()
y las llamadas a add_argument()
, que están superclasificadas en otro módulo intermedio para evitar la duplicación de código, y la envoltura alrededor de la que se llama, están dentro de una función.
Quiero entender la forma en que funciona esta terminación de tabs entre bash y python (o, para el caso, cualquier otro interpretador como perl
).
NOTA: Tengo una comprensión justa de la finalización de bash (que aprendí hace un momento), y creo que entiendo la finalización personalizada de bash (solo).
NOTA: He leído otras preguntas de SO similares, y ninguna responde realmente esta Q.
Edición: Aquí está la función de bash.
Ya entendí cómo el módulo de Python conoce las palabras escritas en la línea de comandos al leer los valores de las variables de os.environ
$COMP_WORDS $COMP_CWORD $COMP_LINE $COMP_POINT $COMPREPLY
Estas variables tienen valores solo al presionar la pestaña. Mi pregunta es ¿cómo se activa el módulo de Python?
Para entender lo que está sucediendo aquí, verifiquemos qué hace realmente esa función de bash:
COMPREPLY=( $( \ COMP_LINE=$COMP_LINE COMP_POINT=$COMP_POINT \ COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ OPTPARSE_AUTO_COMPLETE=1 $1 ) )
¿Ves los $1
al final? ¡Eso significa que realmente llama al archivo Python que queremos ejecutar con el conjunto de variables de entorno especiales! Para rastrear lo que está sucediendo, preparemos un pequeño script para interceptar lo que optcomplete.autocomplete
hace:
#!/usr/bin/env python2 import os, sys import optparse, optcomplete from cStringIO import StringIO if __name__ == '__main__': parser = optparse.OptionParser() parser.add_option('-s', '--simple', action='store_true', help="Simple really simple option without argument.") parser.add_option('-o', '--output', action='store', help="Option that requires an argument.") opt = parser.add_option('-p', '--script', action='store', help="Option that takes python scripts args only.") opt.completer = optcomplete.RegexCompleter('.*\.py') # debug env variables sys.stderr.write("\ncalled with args: %s\n" % repr(sys.argv)) for k, v in sorted(os.environ.iteritems()): sys.stderr.write(" %s: %s\n" % (k, v)) # setup capturing the actions of `optcomplete.autocomplete` def fake_exit(i): sys.stderr.write("autocomplete tried to exit with status %d\n" % i) sys.stdout = StringIO() sys.exit = fake_exit # Support completion for the command-line of this script. optcomplete.autocomplete(parser, ['.*\.tar.*']) sys.stderr.write("autocomplete tried to write to STDOUT:\n") sys.stderr.write(sys.stdout.getvalue()) sys.stderr.write("\n") opts, args = parser.parse_args()
Esto nos da lo siguiente cuando intentamos autocompletarlo:
$ ./test.py [tab] called with args: ['./test.py'] ... COMP_CWORD: 1 COMP_LINE: ./test.py COMP_POINT: 10 COMP_WORDS: ./test.py ... OPTPARSE_AUTO_COMPLETE: 1 ... autocomplete tried to exit with status 1 autocomplete tried to write to STDOUT: -o -h -s -p --script --simple --help --output
Así que optcomplete.autocomplete
simplemente lee el entorno, prepara las coincidencias, las escribe en STDOUT y sale. El resultado -o -h -s -p --script --simple --help --output
se coloca en una matriz bash ( COMPREPLY=( ... )
) y se devuelve a bash para presentar las opciones al usuario. No hay magia involucrada 🙂