Python Click: solo ejecuta el subcomando si el comando principal se ejecuta correctamente

Estoy usando Click para crear una CLI de Python y tengo un problema con cómo se manejan las excepciones en Click.

No estoy seguro de la redacción (“subcomando”, “parentcommand”) aquí, pero de mi ejemplo obtendré la idea, espero. Asumamos este código:

@click.group()
@click.option("--something")
def mycli(something):
    try:
        #do something with "something" and set ctx
        ctx.obj = {}
        ctx.obj["somevar"] = some_result
    except:
        print("Something went wrong")
        raise

    #only if everything went fine call mycommand

@click.group()
@click.pass_context
def mygroup(ctx):
    pass

@mygroup.command(name="mycommand")
@click.pass_context
def mycommand(ctx):
    #this only works if somevar is set in ctx so don't call this if setting went wrong in mycli

Cuando se inicia la aplicación se llama así:

if __name__ == "__main__":
    mycli.add_command(mygroup)
    mycli()

Entonces empiezo el programa así:

python myapp --something somevalue mycommand

Comportamiento esperado: primero se llama a mycli y se ejecuta el código. Si se lanza una excepción, el bloque excepto la captura, se imprime un mensaje y se lanza la excepción. Debido a que no tenemos otro bloque try / except, esto dará como resultado la terminación del script. El comando “sub” -command mycommand nunca se llama porque el programa ya terminó cuando se ejecuta el comando “parent”, mycli.

Comportamiento real: la excepción es que el mensaje se imprime, pero aún se llama a mycommand. Luego falla con otro mensaje de excepción porque no se configuró la variable de contexto requerida.

¿Cómo manejaría algo así? Básicamente, solo quiero llamar al subcomando mycommand para que se ejecute si todo en mycli fue bien.

Mejor respuesta
Para manejar la excepción, pero no continuar con los subcomandos, simplemente puede llamar a exit () como:

Código:

import click

@click.group()
@click.option("--something")
@click.pass_context
def mycli(ctx, something):
    ctx.obj = dict(a_var=something)
    try:
        if something != '1':
            raise IndexError('An Error')
    except Exception as exc:
        click.echo('Exception: {}'.format(exc))
        exit()

Código de prueba:

@mycli.group()
@click.pass_context
def mygroup(ctx):
    click.echo('mygroup: {}'.format(ctx.obj['a_var']))
    pass


@mygroup.command()
@click.pass_context
def mycommand(ctx):
    click.echo('mycommand: {}'.format(ctx.obj['a_var']))


if __name__ == "__main__":
    commands = (
        'mygroup mycommand',
        '--something 1 mygroup mycommand',
        '--something 2 mygroup mycommand',
        '--help',
        '--something 1 mygroup --help',
        '--something 1 mygroup mycommand --help',
    )

    import sys, time

    time.sleep(1)
    print('Click Version: {}'.format(click.__version__))
    print('Python Version: {}'.format(sys.version))
    for cmd in commands:
        try:
            time.sleep(0.1)
            print('-----------')
            print('> ' + cmd)
            time.sleep(0.1)
            mycli(cmd.split())

        except BaseException as exc:
            if str(exc) != '0' and \
                    not isinstance(exc, (click.ClickException, SystemExit)):
                raise

Resultados:

Click Version: 6.7
Python Version: 3.6.3 (v3.6.3:2c5fed8, Oct  3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)]
-----------
> mygroup mycommand
Exception: An Error
-----------
> --something 1 mygroup mycommand
mygroup: 1
mycommand: 1
-----------
> --something 2 mygroup mycommand
Exception: An Error
-----------
> --help
Usage: test.py [OPTIONS] COMMAND [ARGS]...

Options:
  --something TEXT
  --help            Show this message and exit.

Commands:
  mygroup
-----------
> --something 1 mygroup --help
Usage: test.py mygroup [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  mycommand
-----------
> --something 1 mygroup mycommand --help
mygroup: 1
Usage: test.py mygroup mycommand [OPTIONS]

Options:
  --help  Show this message and exit.

Por favor indique la dirección original:Python Click: solo ejecuta el subcomando si el comando principal se ejecuta correctamente - Código de registro