Skip to content Skip to sidebar Skip to footer

Invoke Python Click Command With Custom Class From Another Command

So here's my issue: Assume that I have built a CLI using Python Click for which I have created custom classes of groups and commands that wrap invoking to catch exceptions: logger

Solution 1:

The trouble you are running into, is that you are calling click.Context.invoke, which does not use the click.Command.invoke. With a little DRY we can factor out your invoke wrapper and use it like:

Code:

definvoke_with_catch(self, ctx, original_invoke):

    fmt = dict(command=getattr(ctx, 'command', ctx).name)
    try:
        click.echo("Running {command} command".format(**fmt))
        result = original_invoke(self, ctx)
        click.echo("Completed {command} command".format(**fmt))
        return result

    except Exception as exc:
        click.echo(
            'Command {command} failed with exception: {exc}'.format(
                exc=exc, **fmt)
        )

        """ In case command invoked from another command """raise click.ClickException(
            "Failed to invoke {command} command".format(**fmt))

Calling the Code:

The wrapper can be called directly like:

invoke_with_catch(ctx, c1, click.Context.invoke)

or can be used in the inherited class like:

classCLICommandInvoker(click.Command):definvoke(self, ctx):
        return invoke_with_catch(self, ctx, click.Command.invoke)

Test Code:

import click

classCLICommandInvoker(click.Command):
    definvoke(self, ctx):
        return invoke_with_catch(self, ctx, click.Command.invoke)


classCLIGroupInvoker(click.Group):
    definvoke(self, ctx):
        return invoke_with_catch(self, ctx, click.Group.invoke)


@click.group(cls=CLIGroupInvoker)defg():
    pass@g.command(cls=CLICommandInvoker)@click.option("--throw", is_flag=True)defc1(throw):
    click.echo("C1")
    if throw:
        raise Exception('Throwing in C1')

@g.command(cls=CLICommandInvoker)@click.option("--throw", is_flag=True)@click.pass_contextdefc2(ctx, throw):
    invoke_with_catch(ctx, c1, click.Context.invoke)
    click.echo("C2")
    if throw:
        raise Exception('Throwing in C2')


if __name__ == "__main__":
    commands = (
        'c1',
        'c1 --throw',
        'c2',
        'c2 --throw',
        '--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)
            g(cmd.split())

        except BaseException as exc:
            ifstr(exc) != '0'and \
                    notisinstance(exc, (click.ClickException, SystemExit)):
                raise

Results:

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)]
-----------> c1
Running g command
Running c1 command
C1
Completed c1 command
Completed g command
-----------> c1 --throw
Running g command
Running c1 command
C1
Command c1 failed with exception: Throwing in C1
Command g failed with exception: Failed to invoke c1 command
Error: Failed to invoke g command
-----------> c2
Running g command
Running c2 command
Running c1 command
C1
Completed c1 command
C2
Completed c2 command
Completed g command
-----------> c2 --throw
Running g command
Running c2 command
Running c1 command
C1
Completed c1 command
C2
Command c2 failed with exception: Throwing in C2
Command g failed with exception: Failed to invoke c2 command
Error: Failed to invoke g command
-----------> --help
Usage: test.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  c1
  c2
-----------> 
Usage: test.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  c1
  c2

Post a Comment for "Invoke Python Click Command With Custom Class From Another Command"