使用另一个命令中的自定义类调用python click命令

2024-09-30 16:33:59 发布

您现在位置:Python中文网/ 问答频道 /正文

我的问题是:

假设我使用Python Click构建了一个CLI,并为其创建了组和命令的自定义类,这些类包装调用以捕获异常:

logger = logging.getLogger()
class CLICommandInvoker(click.Command):
    def invoke(self, ctx):
        command = ctx.command.name
        try:
            logger.info("Running {command} command".format(command=command))
            ret = super(CLICommandInvoker, self).invoke(ctx)
            logger.info("Completed {command} command".format(command=command))
            return ret

        except Exception as exc:
            logger.error(
                'Command {command} failed with exception: {exc}'.format(command=command, exc=exc)
            )

            """ In case command invoked from another command """
            raise Exception("Failed to invoke {command} command".format(command=command))


class CLIGroupInvoker(click.Group):
    def invoke(self, ctx):
        group = ctx.command.name
        try:
            ret = super(CLIGroupInvoker, self).invoke(ctx)
            group_subcommand = ctx.invoked_subcommand
            logger.info(
                'Command "{group}-{subcommand}" completed successfully'.format(group = group, subcommand = group_subcommand)
            )
            return ret

        except Exception:
            group_subcommand = ctx.invoked_subcommand
            logger.error(
                'Command "{group}-{subcommand}" failed'.format(group=group, subcommand=group_subcommand)
            )

例如,我在一个组中有两个命令:

^{pr2}$

因此,代码运行良好,但是c2中上下文的invoke方法没有在我的CLICommandInvoker中运行自定义调用,而是直接转到c1函数。我看不到Running c1 command或自定义invoke中关于c1的日志,只有关于c2的日志。在

那么,我做错什么了?从另一个命令调用命令时,如何让命令调用使用自定义类?还是不可能?在

我知道有一种解决方案可以简单地重构代码以提取实现本身,并简单地让命令“包装”实际的逻辑,但我们假设目前还不可能。在


Tags: 命令selfinfoformatexceptiongrouploggercommand
1条回答
网友
1楼 · 发布于 2024-09-30 16:33:59

您遇到的问题是,您正在调用click.Context.invoke,它不使用click.Command.invoke。使用一个小的DRY我们可以将您的调用包装器分解出来,然后使用它,如下所示:

代码:

def invoke_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))

调用代码:

包装器可以直接调用,如下所示:

^{pr2}$

或者可以在继承类中使用,例如:

class CLICommandInvoker(click.Command):
    def invoke(self, ctx):
        return invoke_with_catch(self, ctx, click.Command.invoke)

测试代码:

import click

class CLICommandInvoker(click.Command):
    def invoke(self, ctx):
        return invoke_with_catch(self, ctx, click.Command.invoke)


class CLIGroupInvoker(click.Group):
    def invoke(self, ctx):
        return invoke_with_catch(self, ctx, click.Group.invoke)


@click.group(cls=CLIGroupInvoker)
def g():
    pass

@g.command(cls=CLICommandInvoker)
@click.option(" throw", is_flag=True)
def c1(throw):
    click.echo("C1")
    if throw:
        raise Exception('Throwing in C1')

@g.command(cls=CLICommandInvoker)
@click.option(" throw", is_flag=True)
@click.pass_context
def c2(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:
            if str(exc) != '0' and \
                    not isinstance(exc, (click.ClickException, SystemExit)):
                raise

结果:

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

相关问题 更多 >