gpt4 book ai didi

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

转载 作者:太空宇宙 更新时间:2023-11-04 04:15:00 27 4
gpt4 key购买 nike

所以这是我的问题:

假设我已经使用 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)
)

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

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

@g.command(cls=CLICommandInvoker)
def c1():
print("C1")

@g.command(cls=CLICommandInvoker)
@click.pass_context
def c2(ctx):
ctx.invoke(c1)
print("C2")

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

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

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

最佳答案

您遇到的问题是,您调用的是 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))

调用代码:

包装器可以像这样直接调用:

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

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

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

关于python - 使用来自另一个命令的自定义类调用 python click 命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55626606/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com