Categorías
Python

Las listas y tuplas en Python

 

Tabla de Contenidos

  • de línea de comandos ExampleBasic UsageUsage w / Opciones (banderas) Uso
  • básico
  • Uso w / Opciones (Banderas)
  • Secciones de experiencia Argumentos
  • Comandos
  • Banderas / Opciones
  • Versión Opción (–version)
  • Mejora de Ayuda (-h / – help)
  • Manejo de errores Opción
  • InvokeHelp DocumentationVersion
  • Ayuda Documentación
  • Versión Opción
  • ConclusionMy Recomendación
  • Mi Recomendación
  • Bono: PackagingEntry Punto BasicsPackaging clic CommandsPackaging argparse CommandsPackaging Docopt Comandos
  • punto de entrada Fundamentos
  • Embalaje Haga clic en Comandos
  • Packaging argparse Comandos
  • Embalaje Uso Docopt Comandos
  • básico
  • Uso w / Opciones (banderas)
  • Ayuda Documentación
  • Versión Opción
  • Mi Recomendación
  • punto de entrada Fundamentos
  • Embalaje Haga clic en Comandos
  • Packaging argparse Comandos
  • Packaging Docopt Comandos

Hace aproximadamente un año empecé un trabajo donde las aplicaciones de línea de comandos edificio fue una ocurrencia común. En ese momento yo había usado argparse un poco y queríamos explorar ¿con qué otras opciones.

me encontré con que las alternativas más populares disponibles eran clic y docopt. Durante mi exploración también encontré que, aparte de cada uno bibliotecas “¿por qué yo utilizan” sección no había mucho disponible para una comparación completa de las tres bibliotecas. Ahora hay-esta entrada del blog!

Si desea, puede dirigirse directamente a la fuente, aunque en realidad no lo hará mucho bien sin las comparaciones y la construcción paso a paso que se presentan en este artículo.

Este artículo utiliza las siguientes versiones de las bibliotecas:

$ python --version
Python 3.4.3
# argparse is a Python core library

$ pip list | grep click
click (5.1)

$ pip list | grep docopt
docopt (0.6.2)

$ pip list | grep invoke
invoke (0.10.1)

(Ignorar invocación por ahora, es una sorpresa especial para más tarde!) Ejemplo

de línea de comandos

La aplicación de línea de comandos que estamos creando tendrá la siguiente interfaz: Uso

python [file].py [command] [options] NAME

básico

$ python [file].py hello Kyle
Hello, Kyle!

$ python [file].py goodbye Kyle
Goodbye, Kyle!

Uso w / Opciones (banderas)

$ python [file].py hello --greeting=Wazzup Kyle
Whazzup, Kyle!

$ python [file].py goodbye --greeting=Later Kyle
Later, Kyle!

$ python [file].py hello --caps Kyle
HELLO, KYLE!

$ python [file].py hello --greeting=Wazzup --caps Kyle
WAZZUP, KYLE!

en este artículo se comparará cada método bibliotecas para la implementación de las siguientes características:

características adicionales:

Como era de esperar argparse , docopt y clic poner en práctica todas estas características (como cualquier biblioteca de línea de comandos completa haría). Este medio de que la implementación real de estas características es lo que vamos a comparar. Cada biblioteca tiene un enfoque muy diferente que se presta a una comparación muy interesante – argparse = estándar, docopt = docstrings, clic = decoradores.

Secciones de experiencia

Comandos

Vamos a empezar con la creación del esqueleto básico (sin argumentos u opciones) para cada biblioteca.

argparse

import argparse

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

hello_parser = subparsers.add_parser('hello')
goodbye_parser = subparsers.add_parser('goodbye')

if __name__ == '__main__':
args = parser.parse_args()

Con esta ahora tenemos dos comandos (hola y adiós) y un mensaje de ayuda incorporado. Tenga en cuenta que el mensaje de ayuda cambios cuando se ejecuta como una opción en el comando hola.

$ python argparse/commands.py --help
usage: commands.py [-h] {hello,goodbye} ...

positional arguments:
{hello,goodbye}

optional arguments:
-h, --help show this help message and exit

$ python argparse/commands.py hello --help
usage: commands.py hello [-h]

optional arguments:
-h, --help show this help message and exit

Docopt

"""Greeter.

Usage:
commands.py hello
commands.py goodbye
commands.py -h | --help

Options:
-h --help Show this screen.
"""
from docopt import docopt

if __name__ == '__main__':
arguments = docopt(__doc__)

Con esto, de nuevo, tiene dos comandos (hola, adiós) y un built-in mensaje de ayuda. Tenga en cuenta que el mensaje de ayuda cambio no cuando se ejecuta como una opción en el comando hola. Además Nos No necesidad de especificar explícitamente la -h commands.py | –help en la sección Opciones para obtener un comando de ayuda. Sin embargo, si no lo hacemos no se mostrarán en el mensaje de ayuda de salida como opciones.

$ python docopt/commands.py --help
Greeter.

Usage:
commands.py hello
commands.py goodbye
commands.py -h | --help

Options:
-h --help Show this screen.

$ python docopt/commands.py hello --help
Greeter.

Usage:
commands.py hello
commands.py goodbye
commands.py -h | --help

Options:
-h --help Show this screen.

clic

import click

@click.group()
def greet():
pass

@greet.command()
def hello(**kwargs):
pass

@greet.command()
def goodbye(**kwargs):
pass

if __name__ == '__main__':
greet()

Con esta ahora tenemos dos comandos (hola, adiós) y un mensaje de ayuda incorporado. Tenga en cuenta que el mensaje de ayuda cambios cuando se ejecuta como una opción en el comando hola.

$ python click/commands.py --help
Usage: commands.py [OPTIONS] COMMAND [ARGS]...

Options:
--help Show this message and exit.

Commands:
goodbye
hello

$ python click/commands.py hello --help
Usage: commands.py hello [OPTIONS]

Options:
--help Show this message and exit.

Incluso en este punto se puede ver que tenemos muy diferentes enfoques para la construcción de una aplicación básica de línea de comandos. A continuación vamos a añadir el argumento NOMBRE , y la lógica de ouput el resultado de cada herramienta.

Argumentos

En esta sección se añade nueva lógica para el mismo código que se muestra en la sección anterior. Vamos a añadir comentarios a las nuevas líneas que indican su propósito. Argumentos (también conocido como argumentos posicionales) son insumos necesarios para una aplicación de línea de comandos. En este caso estamos añadiendo un argumento de nombre requerido para que la herramienta puede saludar a una persona específica.

argparse

Para añadir un argumento a un subcomando utilizamos el método add_argument. Y con el fin de ejecutar la lógica correcta, cuando se llama a un comando, se utiliza el método de set_defaults para establecer una función predeterminada. Finalmente se ejecuta la función por defecto llamando args.func (args) después de analizar los argumentos en tiempo de ejecución.

import argparse

def hello(args):
print('Hello, {0}!'.format(args.name))

def goodbye(args):
print('Goodbye, {0}!'.format(args.name))

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

hello_parser = subparsers.add_parser('hello')
hello_parser.add_argument('name') # add the name argument
hello_parser.set_defaults(func=hello) # set the default function to hello

goodbye_parser = subparsers.add_parser('goodbye')
goodbye_parser.add_argument('name')
goodbye_parser.set_defaults(func=goodbye)

if __name__ == '__main__':
args = parser.parse_args()
args.func(args) # call the default function
$ python argparse/arguments.py hello Kyle
Hello, Kyle!

$ python argparse/arguments.py hello --help
usage: arguments.py hello [-h] name

positional arguments:
name

optional arguments:
-h, --help show this help message and exit

Docopt

Con el fin de añadir una opción, añadir un a la cadena de documentación. La <> se utilizan para designar un argumento posicional. Con el fin de ejecutar la lógica correcta debemos comprobar si el comando (se trata como un argumento) es verdadera en tiempo de ejecución si los argumentos [ ‘hola :, a continuación, llama a la función correcta.

"""Greeter.

Usage:
basic.py hello
basic.py goodbye
basic.py (-h | --help)

Options:
-h --help Show this screen.

"""
from docopt import docopt

def hello(name):
print('Hello, {0}'.format(name))

def goodbye(name):
print('Goodbye, {0}'.format(name))

if __name__ == '__main__':
arguments = docopt(__doc__)

# if an argument called hello was passed, execute the hello logic.
if arguments['hello']:
hello(arguments[''])
elif arguments['goodbye']:
goodbye(arguments[''])
$ python docopt/arguments.py hello Kyle
Hello, Kyle

$ python docopt/arguments.py hello --help
Greeter.

Usage:
basic.py hello
basic.py goodbye
basic.py (-h | --help)

Options:
-h --help Show this screen.

Tenga en cuenta que el mensaje de ayuda no es específico para el subcomando, sino que es toda la cadena de documentación para el programa.

click

Con el fin de añadir un argumento a un comando clic usamos el @ clic decorador .argument. En este caso estamos justo pasando el nombre del argumento, pero hay muchas opciones más algunos de los cuales vamos a utilizar más tarde. Ya que estamos decorando la lógica (función) con el argumento de que no tenemos que hacer nada para definir o realizar una llamada a la lógica correcta.

import click

@click.group()
def greet():
pass

@greet.command()
@click.argument('name') # add the name argument
def hello(**kwargs):
print('Hello, {0}!'.format(kwargs['name']))

@greet.command()
@click.argument('name')
def goodbye(**kwargs):
print('Goodbye, {0}!'.format(kwargs['name']))

if __name__ == '__main__':
greet()
$ python click/arguments.py hello Kyle
Hello, Kyle!

$ python click/arguments.py hello --help
Usage: arguments.py hello [OPTIONS] NAME

Options:
--help Show this message and exit.

Banderas / Opciones

En esta sección vamos a volver a añadir nueva lógica al mismo código que se muestra en la sección anterior. Vamos a añadir comentarios a las nuevas líneas que indican que hay propósito. Las opciones son entradas no se requiere que se pueden dar para alterar la ejecución de una aplicación de línea de comandos. Las banderas son un valor lógico solamente (Verdadero / Falso) subconjunto de opciones. Por ejemplo: –foo = bar pasará bar como el valor para la opción foo y –baz (si define como una bandera) pasará el valor de verdadero es se le da la opción, o FALSE si no.

Para este ejemplo vamos a añadir la opción –greeting = [saludo], y el –caps bandera. La opción de saludo tendrá valores por defecto de hola y adiós y permitir al usuario pasar de un saludo personalizado. Por ejemplo dado –greeting = Wazzup la herramienta responderá con Wazzup, [nombre] !. La bandera será –caps mayúsculas toda la respuesta si se les da. Por ejemplo dado –caps la herramienta responderá con HOLA, [NOMBRE] !.

argparse

import argparse

# since we are now passing in the greeting
# the logic has been consolidated to a single greet function
def greet(args):
output = '{0}, {1}!'.format(args.greeting, args.name)
if args.caps:
output = output.upper()
print(output)

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

hello_parser = subparsers.add_parser('hello')
hello_parser.add_argument('name')
# add greeting option w/ default
hello_parser.add_argument('--greeting', default='Hello')
# add a flag (default=False)
hello_parser.add_argument('--caps', action='store_true')
hello_parser.set_defaults(func=greet)

goodbye_parser = subparsers.add_parser('goodbye')
goodbye_parser.add_argument('name')
goodbye_parser.add_argument('--greeting', default='Goodbye')
goodbye_parser.add_argument('--caps', action='store_true')
goodbye_parser.set_defaults(func=greet)

if __name__ == '__main__':
args = parser.parse_args()
args.func(args)
$ python argparse/options.py hello --greeting=Wazzup Kyle
Wazzup, Kyle!

$ python argparse/options.py hello --caps Kyle
HELLO, KYLE!

$ python argparse/options.py hello --greeting=Wazzup --caps Kyle
WAZZUP, KYLE!

$ python argparse/options.py hello --help
usage: options.py hello [-h] [--greeting GREETING] [--caps] name

positional arguments:
name

optional arguments:
-h, --help show this help message and exit
--greeting GREETING
--caps

Docopt

Una vez que golpeamos el caso de añadir opciones con valores predeterminados, que con un obstáculo en la implementación básica de comandos en docopt . Vamos a continuar sólo para ilustrar el tema.

"""Greeter.

Usage:
basic.py hello [--caps] [--greeting=]
basic.py goodbye [--caps] [--greeting=]
basic.py (-h | --help)

Options:
-h --help Show this screen.
--caps Uppercase the output.
--greeting= Greeting to use [default: Hello].

"""
from docopt import docopt

def greet(args):
output = '{0}, {1}!'.format(args['--greeting'],
args[''])
if args['--caps']:
output = output.upper()
print(output)

if __name__ == '__main__':
arguments = docopt(__doc__)
greet(arguments)

Ahora, ver lo que sucede cuando nos encontramos los siguientes comandos:

$ python docopt/options.py hello Kyle
Hello, Kyle!

$ python docopt/options.py goodbye Kyle
Hello, Kyle!

qué ?! Debido a que sólo se puede establecer un único defecto para la opción –greeting tanto de nuestra Hola y adiós comandos ahora responden con Hello, Kyle !. Para que podamos para que esto funcione tendremos que seguir el ejemplo GIT docopt ofrece. El código refactorizado se muestra a continuación:

"""Greeter.

Usage:
basic.py hello [--caps] [--greeting=]
basic.py goodbye [--caps] [--greeting=]
basic.py (-h | --help)

Options:
-h --help Show this screen.
--caps Uppercase the output.
--greeting= Greeting to use [default: Hello].

Commands:
hello Say hello
goodbye Say goodbye

"""

from docopt import docopt

HELLO = """usage: basic.py hello [options] []

-h --help Show this screen.
--caps Uppercase the output.
--greeting= Greeting to use [default: Hello].
"""

GOODBYE = """usage: basic.py goodbye [options] []

-h --help Show this screen.
--caps Uppercase the output.
--greeting= Greeting to use [default: Goodbye].
"""

def greet(args):
output = '{0}, {1}!'.format(args['--greeting'],
args[''])
if args['--caps']:
output = output.upper()
print(output)

if __name__ == '__main__':
arguments = docopt(__doc__, options_first=True)

if arguments[''] == 'hello':
greet(docopt(HELLO))
elif arguments[''] == 'goodbye':
greet(docopt(GOODBYE))
else:
exit("{0} is not a command.
See 'options.py --help'.".format(arguments['']))

Como se puede ver el hola | adiós subcomandos son ahora hay propias cadenas de documentación vinculados a las variables de hola y adiós. Cuando se ejecuta la herramienta que utiliza un nuevo argumento, comando, para decidir qué va a analizar. Esto no sólo corregir el problema que tuvimos con sólo un defecto, pero ahora tenemos mensajes subcomando específico de ayuda también.

$ python docopt/options.py --help
usage: greet [--help] [...]

options:
-h --help Show this screen.

commands:
hello Say hello
goodbye Say goodbye

$ python docopt/options.py hello --help
usage: basic.py hello [options] []

-h --help Show this screen.
--caps Uppercase the output.
--greeting= Greeting to use [default: Hello].

$ python docopt/options.py hello Kyle
Hello, Kyle!

$ python docopt/options.py goodbye Kyle
Goodbye, Kyle!

Además todas nuestras nuevas opciones / banderas están funcionando tan bien:

$ python docopt/options.py hello --greeting=Wazzup Kyle
Wazzup, Kyle!

$ python docopt/options.py hello --caps Kyle
HELLO, KYLE!

$ python docopt/options.py hello --greeting=Wazzup --caps Kyle
WAZZUP, KYLE!

click

Para agregar las opciones de saludo y gorras que usamos el decorador @ click.option. Una vez más, ya que tenemos un saludo por defecto ahora hemos tirado la lógica a cabo en una sola función (def portavoz oficial (**) kwargs :).

import click

def greeter(**kwargs):
output = '{0}, {1}!'.format(kwargs['greeting'],
kwargs['name'])
if kwargs['caps']:
output = output.upper()
print(output)

@click.group()
def greet():
pass

@greet.command()
@click.argument('name')
# add an option with 'Hello' as the default
@click.option('--greeting', default='Hello')
# add a flag (is_flag=True)
@click.option('--caps', is_flag=True)
# the application logic has been refactored into a single function
def hello(**kwargs):
greeter(**kwargs)

@greet.command()
@click.argument('name')
@click.option('--greeting', default='Goodbye')
@click.option('--caps', is_flag=True)
def goodbye(**kwargs):
greeter(**kwargs)

if __name__ == '__main__':
greet()
$ python click/options.py hello --greeting=Wazzup Kyle
Wazzup, Kyle!

$ python click/options.py hello --greeting=Wazzup --caps Kyle
WAZZUP, KYLE!

$ python click/options.py hello --caps Kyle
HELLO, KYLE!

Versión Opción (–version)

En esta sección vamos a estar mostrando cómo agregar un argumento –version a cada una de nuestras herramientas. Por simplicidad sólo tendremos que codificar el número de versión 1.0.0 a . Tenga en cuenta que en una aplicación de producción, tendrá que tirar de esto desde la aplicación instalada. Una forma de lograr esto es con este sencillo proceso:

>>> import pkg_resources
>>> # Replace click with the name of your tool:
>>> pkg_resources.get_distribution("click").version
>>> '5.1'

Una segunda opción para determinar la versión sería tener una versión de la ebullición cambio de software automatizado el número de versión se define en el archivo cuando se libera una nueva versión. Esto es posible con bumpversion. Sin embargo, este enfoque no es recomendable, ya que es fácil de conseguir fuera de sincronía. En general, es mejor manera de mantener un número de versión en el menor número de lugares posibles.

Desde la implementación de la adición de una opción de versión codificada es bastante simple usaremos … para denotar saltado secciones del código de la última sección.

argparse

Para argparse es necesario volver a utilizar el método add_argument, esta vez con la acción = parámetro ‘versión’ y un valor para la versión aprobada en. Aplicamos este método para el analizador de raíz (en lugar de la hola o aDIÓS subparsers).

...
parser = argparse.ArgumentParser()
parser.add_argument('--version', action='version', version='1.0.0')
...
$ python argparse/version.py --version
1.0.0

docopt

Para añadir –version a docopt que agregarlo como una opción para nuestra cadena de documentación primaria. Además añadimos el parámetro de versión a nuestra primera llamada a docopt (análisis de la cadena de documentación primaria).

"""usage: greet [--help] [...]

options:
-h --help Show this screen.
--version Show the version.

commands:
hello Say hello
goodbye Say goodbye

"""

from docopt import docopt

...

if __name__ == '__main__':
arguments = docopt(__doc__, options_first=True, version='1.0.0')
...
$ python docopt/version.py --version
1.0.0

click

click nos proporciona un cómodo decorador @ click.version_option. Para añadir este decoramos nuestra función greet (función principal @ click.group).

...
@click.group()
@click.version_option(version='1.0.0')
def greet():
...
$ python click/version.py --version
version.py, version 1.0.0

Mejora de Ayuda (-h / – help)

El último paso para completar nuestra aplicación es mejorar la documentación de ayuda para cada una de las herramientas. Vamos a querer asegurarse de que podemos acceder a la ayuda con ambos -H y –help y que cada argumento y la opción tienen algún nivel de descripción.

argparse

por defecto Por argparse nos proporciona tanto -h y –help por lo que no es necesario añadir nada de eso. Sin embargo, nuestro actual documentación de ayuda para los subcomandos se carece de información sobre lo que –caps y –greeting hacer y lo que es el argumento de nombre.

$ python argparse/version.py hello -h
usage: version.py hello [-h] [--greeting GREETING] [--caps] name

positional arguments:
name

optional arguments:
-h, --help show this help message and exit
--greeting GREETING
--caps

Para añadir más información que utilice el parámetro ayuda del método add_argument.

...

hello_parser = subparsers.add_parser('hello')
hello_parser.add_argument('name', help='name of the person to greet')
hello_parser.add_argument('--greeting', default='Hello', help='word to use for the greeting')
hello_parser.add_argument('--caps', action='store_true', help='uppercase the output')
hello_parser.set_defaults(func=greet)

goodbye_parser = subparsers.add_parser('goodbye')
goodbye_parser.add_argument('name', help='name of the person to greet')
goodbye_parser.add_argument('--greeting', default='Hello', help='word to use for the greeting')
goodbye_parser.add_argument('--caps', action='store_true', help='uppercase the output')

...

Ahora cuando nos proporciona el indicador de la ayuda obtenemos un resultado mucho más completo:

$ python argparse/help.py hello -h
usage: help.py hello [-h] [--greeting GREETING] [--caps] name

positional arguments:
name name of the person to greet

optional arguments:
-h, --help show this help message and exit
--greeting GREETING word to use for the greeting
--caps uppercase the output

Docopt

Esta sección es donde brilla docopt . Debido a que escribimos la documentación como la definición de la interfaz de línea de comandos en sí, ya tenemos la documentación de ayuda completado. Además -h y –help ya están previstos.

$ python docopt/help.py hello -h
usage: basic.py hello [options] []

-h --help Show this screen.
--caps Uppercase the output.
--greeting= Greeting to use [default: Hello].

click

Adición de documentación de ayuda a clic es muy similar a argparse . Tenemos que añadir el parámetro ayuda a todos nuestros @ clic .Opción decoradores.

...

@greet.command()
@click.argument('name')
@click.option('--greeting', default='Hello', help='word to use for the greeting')
@click.option('--caps', is_flag=True, help='uppercase the output')
def hello(**kwargs):
greeter(**kwargs)

@greet.command()
@click.argument('name')
@click.option('--greeting', default='Goodbye', help='word to use for the greeting')
@click.option('--caps', is_flag=True, help='uppercase the output')
def goodbye(**kwargs):
greeter(**kwargs)

...

Sin embargo, clic no nos proporcionan -h por defecto. Tenemos que utilizar el parámetro context_settings para anular los help_option_names por defecto.

import click

CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])

...

@click.group(context_settings=CONTEXT_SETTINGS)
@click.version_option(version='1.0.0')
def greet():
pass

Ahora el clic ayuda documentación está completa.

$ python click/help.py hello -h
Usage: help.py hello [OPTIONS] NAME

Options:
--greeting TEXT word to use for the greeting
--caps uppercase the output
-h, --help Show this message and exit.

Tratamiento de errores

gestión de errores es una parte importante de cualquier aplicación. En esta sección se explorará el manejo de errores por defecto de cada aplicación y ejecutar la lógica adicional si es necesario. Vamos a explorar tres casos de error:

argparse

$ python argparse/final.py hello
usage: final.py hello [-h] [--greeting GREETING] [--caps] name
final.py hello: error: the following arguments are required: name

$ python argparse/final.py --badoption hello Kyle
usage: final.py [-h] [--version] {hello,goodbye} ...
final.py: error: unrecognized arguments: --badoption

$ python argparse/final.py hello --caps=notanoption Kyle
usage: final.py hello [-h] [--greeting GREETING] [--caps] name
final.py hello: error: argument --caps: ignored explicit argument 'notanoption'

no es muy emocionante, como argparse se encarga de todos nuestros casos de error fuera de la caja.

Docopt

$ python docopt/final.py hello
Hello, None!

$ python docopt/final.py hello --badoption Kyle
usage: basic.py hello [options] []

Desafortunadamente, tenemos un poco de trabajo para conseguir docopt a un nivel mínimo aceptable de manejo de errores. El método recomendados para la validación en docopt es el módulo de esquema. * Asegúrese de instalar – pip instalar esquema. Además proporcionan un ejemplo muy básico de validación. La siguiente es nuestra aplicación con la validación del esquema:

...
from schema import Schema, SchemaError, Optional
...
schema = Schema({
Optional('hello'): bool,
Optional('goodbye'): bool,
'': str,
Optional('--caps'): bool,
Optional('--help'): bool,
Optional('--greeting'): str
})

def validate(args):
try:
args = schema.validate(args)
return args
except SchemaError as e:
exit(e)

if arguments[''] == 'hello':
greet(validate(docopt(HELLO)))
elif arguments[''] == 'goodbye':
greet(validate(docopt(GOODBYE)))
...

Con esta validación en su lugar ahora obtenemos algunos mensajes de error.

$ python docopt/validation.py hello
None should be instance of

$ python docopt/validation.py hello --greeting Kyle
None should be instance of

$ python docopt/validation.py hello --caps=notanoption Kyle
--caps must not have an argument
usage: basic.py hello [options] []

Mientras que estos mensajes no son muy descriptivos y puede ser difícil de depuración para aplicaciones de mayor tamaño, es mejor que ninguna validación en absoluto. El módulo de esquema proporciona otros mecanismos para añadir mensajes de error más descriptivos pero no cubrirá los que están aquí.

click

$ python click/final.py hello
Usage: final.py hello [OPTIONS] NAME

Error: Missing argument "name".

$ python click/final.py hello --badoption Kyle
Error: no such option: --badoption

$ python click/final.py hello --caps=notanoption Kyle
Error: --caps option does not take a value

Al igual que argparse , clic entrada de error mangos por defecto.

Con esto, hemos completado la construcción de la aplicación de línea de comandos que se propuso construir. Antes de concluir vamos a echar un vistazo a otra opción posible.

invocación

podemos utilizar invocación, una biblioteca simple tarea se ejecuta, para construir la aplicación bienvenida de línea de comandos? ¡Vamos a averiguar!

Para empezar vamos a empezar con la versión más simple de la ventana de bienvenida:

tasks.py

from invoke import task

@task
def hello(name):
print('Hello, {0}!'. format(name))

@task
def goodbye(name):
print('Goodbye, {0}!'.format(name))

Con este archivo muy simple obtenemos un dos tareas y ayuda mínima. Del mismo directorio que tasks.py obtenemos los siguientes resultados:

$ invoke -l
Available tasks:

goodbye
hello

$ invoke hello Kyle
Hello, Kyle!

$ invoke goodbye Kyle
Goodbye, Kyle!

Ahora vamos a añadir en nuestras opciones / banderas – –greeting y –caps. Además, podemos sacar la lógica saludo a su propia función, tal como lo hicimos con las otras herramientas.

from invoke import task

def greet(name, greeting, caps):
output = '{0}, {1}!'.format(greeting, name)
if caps:
output = output.upper()
print(output)

@task
def hello(name, greeting='Hello', caps=False):
greet(name, greeting, caps)

@task
def goodbye(name, greeting='Goodbye', caps=False):
greet(name, greeting, caps)

Ahora tenemos LOS interfaz completa hemos designado en el principio!

$ invoke hello Kyle
Hello, Kyle!

$ invoke hello --greeting=Wazzup Kyle
Wazzup, Kyle!

$ invoke hello --greeting=Wazzup --caps Kyle
WAZZUP, KYLE!

$ invoke hello --caps Kyle
HELLO, KYLE!

Ayuda Documentación

Con el fin de competir con argparse , docopt y clic , también tendrá que ser capaz de añadir documentación de ayuda completa. Por suerte, esto también está disponible en invocar utilizando el parámetro de ayuda de la decoradora @task y la adición de cadenas de documentación a las funciones decoradas. Opción

...

HELP = {
'name': 'name of the person to greet',
'greeting': 'word to use for the greeting',
'caps': 'uppercase the output'
}

@task(help=HELP)
def hello(name, greeting='Hello', caps=False):
"""
Say hello.
"""
greet(name, greeting, caps)

@task(help=HELP)
def goodbye(name, greeting='Goodbye', caps=False):
"""
Say goodbye.
"""
greet(name, greeting, caps)
$ invoke --help hello
Usage: inv[oke] [--core-opts] hello [--options] [other tasks here ...]

Docstring:
Say hello.

Options:
-c, --caps uppercase the output
-g STRING, --greeting=STRING word to use for the greeting
-n STRING, --name=STRING name of the person to greet
-v, --version

Versión

que implementen la opción –version no es tan simple y viene con una advertencia. Los fundamentos son que vamos a añadir version = Falso como una opción para cada una de las tareas que llama a una nueva función print_version si es verdadero. Con el fin de hacer este trabajo no podemos tener ningún argumento de posición sin valores predeterminados o se obtiene:

$ invoke hello --version
'hello' did not receive all required positional arguments!

También tenga en cuenta que estamos llamando –version en nuestros comandos hola y adiós, porque invocarse a sí mismo tiene un comando versión:

$ invoke --version
Invoke 0.10.1

El implementación completa de un comando versión de la siguiente manera:

...

def print_version():
print('1.0.0')
exit(0)

@task(help=HELP)
def hello(name='', greeting='Hello', caps=False, version=False):
"""
Say hello.
"""
if version:
print_version()
greet(name, greeting, caps)

...

Ahora estamos en condiciones de pedir a invocar para la versión de nuestra herramienta:

$ invoke hello --version
1.0.0

Conclusión

a revisión, vamos a echar un vistazo a la versión final de cada una de las herramientas que hemos creado.

argparse

import argparse

def greet(args):
output = '{0}, {1}!'.format(args.greeting, args.name)
if args.caps:
output = output.upper()
print(output)

parser = argparse.ArgumentParser()
parser.add_argument('--version', action='version', version='1.0.0')
subparsers = parser.add_subparsers()

hello_parser = subparsers.add_parser('hello')
hello_parser.add_argument('name', help='name of the person to greet')
hello_parser.add_argument('--greeting', default='Hello', help='word to use for the greeting')
hello_parser.add_argument('--caps', action='store_true', help='uppercase the output')
hello_parser.set_defaults(func=greet)

goodbye_parser = subparsers.add_parser('goodbye')
goodbye_parser.add_argument('name', help='name of the person to greet')
goodbye_parser.add_argument('--greeting', default='Hello', help='word to use for the greeting')
goodbye_parser.add_argument('--caps', action='store_true', help='uppercase the output')
goodbye_parser.set_defaults(func=greet)

if __name__ == '__main__':
args = parser.parse_args()
args.func(args)

Docopt

"""usage: greet [--help] [...]

options:
-h --help Show this screen.
--version Show the version.

commands:
hello Say hello
goodbye Say goodbye

"""

from docopt import docopt
from schema import Schema, SchemaError, Optional

HELLO = """usage: basic.py hello [options] []

-h --help Show this screen.
--caps Uppercase the output.
--greeting= Greeting to use [default: Hello].
"""

GOODBYE = """usage: basic.py goodbye [options] []

-h --help Show this screen.
--caps Uppercase the output.
--greeting= Greeting to use [default: Goodbye].
"""

def greet(args):
output = '{0}, {1}!'.format(args['--greeting'],
args[''])
if args['--caps']:
output = output.upper()
print(output)

if __name__ == '__main__':
arguments = docopt(__doc__, options_first=True, version='1.0.0')

schema = Schema({
Optional('hello'): bool,
Optional('goodbye'): bool,
'': str,
Optional('--caps'): bool,
Optional('--help'): bool,
Optional('--greeting'): str
})

def validate(args):
try:
args = schema.validate(args)
return args
except SchemaError as e:
exit(e)

if arguments[''] == 'hello':
greet(validate(docopt(HELLO)))
elif arguments[''] == 'goodbye':
greet(validate(docopt(GOODBYE)))
else:
exit("{0} is not a command. See 'options.py --help'.".format(arguments['']))

click

import click

CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])

def greeter(**kwargs):
output = '{0}, {1}!'.format(kwargs['greeting'],
kwargs['name'])
if kwargs['caps']:
output = output.upper()
print(output)

@click.group(context_settings=CONTEXT_SETTINGS)
@click.version_option(version='1.0.0')
def greet():
pass

@greet.command()
@click.argument('name')
@click.option('--greeting', default='Hello', help='word to use for the greeting')
@click.option('--caps', is_flag=True, help='uppercase the output')
def hello(**kwargs):
greeter(**kwargs)

@greet.command()
@click.argument('name')
@click.option('--greeting', default='Goodbye', help='word to use for the greeting')
@click.option('--caps', is_flag=True, help='uppercase the output')
def goodbye(**kwargs):
greeter(**kwargs)

if __name__ == '__main__':
greet()

invocación

from invoke import task

def greet(name, greeting, caps):
output = '{0}, {1}!'.format(greeting, name)
if caps:
output = output.upper()
print(output)

HELP = {
'name': 'name of the person to greet',
'greeting': 'word to use for the greeting',
'caps': 'uppercase the output'
}

def print_version():
print('1.0.0')
exit(0)

@task(help=HELP)
def hello(name='', greeting='Hello', caps=False, version=False):
"""
Say hello.
"""
if version:
print_version()
greet(name, greeting, caps)

@task(help=HELP)
def goodbye(name='', greeting='Goodbye', caps=False, version=False):
"""
Say goodbye.
"""
if version:
print_version()
greet(name, greeting, caps)

Mi Recomendación

Ahora, para obtener esta fuera del camino, mi go-a personal de la biblioteca es clic . He estado usando en múltiples interfaces de comando, grandes y complejas para el año pasado. (El crédito va a @kwbeam por presentarme a clic ). Yo prefiero el enfoque decorador y creo que se presta una interfaz muy limpia, componibles. Dicho esto, vamos a evaluar cada opción bastante.

argparse

Arparse es la biblioteca estándar (incluido con Python) para la creación de utilidades de línea de comandos. Por ese solo hecho, podría decirse que es el más utilizado de los instrumentos examinados aquí. argparse también es muy fácil de usar como un montón de magia (trabajo implícita de que sucede detrás de las escenas) se utiliza para construir la interfaz. Por ejemplo, ambos argumentos y opciones se definen utilizando el método add_arguments y argparse se da cuenta de que es lo que detrás de las escenas.

Docopt

Si piensa escribir documentación es grande, docopt es para usted! Además docopt tiene implementaciones para muchos otros idiomas – lo que significa que puede aprender una biblioteca y utilizarlo a través de muchos idiomas. La desventaja de docopt es que está muy estructurada en la forma en que tiene que definir la interfaz de línea de comandos. (Algunos podrían decir que esto es una buena cosa!)

click

ya he dicho que me gusta mucho clic y han estado utilizando en la producción durante más de un año. os animo a leer el muy completo ¿Por click ? documentación. De hecho, de que la documentación es lo que inspiró esta entrada del blog! La aplicación de estilo decorador de clic es muy sencillo de utilizar y puesto que va a decorar la función que desea ejecutar, que hace que sea muy fácil de leer el código y descubrir lo que va a ser ejecutado. Además, clic soporta características avanzadas como las devoluciones de llamada, de anidación de comandos, y mucho más. Haga clic se basa en un tenedor de la biblioteca optparse ahora en desuso.

invocación

invocación me sorprendió en esta comparación. pensé que una biblioteca diseñada para la ejecución de la tarea podría no ser capaz de igualar fácilmente las bibliotecas de línea de comandos completo – pero lo hizo! Una vez dicho esto, yo no recomendaría su uso para este tipo de trabajo a medida que sin duda va a topar con limitaciones para nada más complejo que el ejemplo que aquí se presenta.

Bono: Embalaje

Dado que no todo el mundo es un embalaje allí fuente de Python con setuptools (u otras soluciones), decidimos no hacer de este un componente central del artículo. Además, no queremos para cubrir envasado como un tema completo. Si desea obtener más información sobre el envasado de con setuptools ir aquí o con Conda ir aquí o puede leer mi post anterior del blog en Conda envasado . Lo que vamos a cubrir aquí es cómo utilizar la opción la entry_points para hacer una aplicación de línea de comandos de un comando ejecutable en instalar .

punto de entrada Fundamentos

Un entry_point es esencialmente un mapa para una única función en el código que se le dará un comando en el sistema PATH. Un entry_point tiene la forma – = comando package.module: función

La mejor manera de explicar esto es solo vistazo a nuestra click ejemplo y añadir un punto de entrada.

Packaging Haga clic en Comandos

click hace que el empaquetado simple, ya que por defecto que llamamos una sola función cuando ejecutamos nuestro programa:

if __name__ == '__main__':
greet()

Además del resto de la setup.py (no cubiertos aquí), nos gustaría añadir lo siguiente para crear un entry_point para nuestra clic aplicación.

Suponiendo que el directorio siguiente estructura-

greeter/
├── greet
│ ├── __init__.py
│ └── cli.py <-- the same as our final.py └── setup.py

-Vamos a crear la siguiente entry_point :

entry_points={
'console_scripts': [
'greet=greet.cli:greet', # command=package.module:function
],
},

Cuando un usuario instala el paquete creado con este entry_point , setuptools creará el siguiente script ejecutable (llamado Greet) y colocarlo en el camino del sistema del usuario.

#!/usr/bin/python
if __name__ == '__main__':
import sys
from greet.cli import greet

sys.exit(greet())

vez instalado, el usuario será ahora capaz de ejecutar el siguiente:

$ greet --help
Usage: greet [OPTIONS] COMMAND [ARGS]...

Options:
--version Show the version and exit.
-h, --help Show this message and exit.

Commands:
goodbye
hello

Packaging argparse Comandos

La única cosa que tenemos que hacer de manera diferente a partir clic es retirar todo de la inicialización de la aplicación en una sola función que puede llamar en nuestra entry_point . Este

:

if __name__ == '__main__':
args = parser.parse_args()
args.func(args)

se convierte en:

def greet():
args = parser.parse_args()
args.func(args)

if __name__ == '__main__':
greet()

Ahora podemos usar el mismo patrón para la entry_point definimos para clic .

Embalaje Docopt Comandos

Embalaje docopt comandos requiere el mismo proceso que argparse . Este

:

if __name__ == '__main__':
arguments = docopt(__doc__, options_first=True, version='1.0.0')

if arguments[''] == 'hello':
greet(docopt(HELLO))
elif arguments[''] == 'goodbye':
greet(docopt(GOODBYE))
else:
exit("{0} is not a command. See 'options.py --help'.".format(arguments['']))

se convierte en:

def greet():
arguments = docopt(__doc__, options_first=True, version='1.0.0')

if arguments[''] == 'hello':
greet(docopt(HELLO))
elif arguments[''] == 'goodbye':
greet(docopt(GOODBYE))
else:
exit("{0} is not a command. See 'options.py --help'.".format(arguments['']))

if __name__ == '__main__':
greet()

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *