如何在没有 Context.invoke 或 Context.forward 的情况下 'self-invoke' python-单击 CLI 命令?

How to 'self-invoke' python-click CLI commands without Context.invoke or Context.forward?

click 库不鼓励对 'self-invoke' CLI 命令调用 click.forwardclick.forward 函数。引用 docs:

Sometimes, it might be interesting to invoke one command from another command. This is a pattern that is generally discouraged with Click, but possible nonetheless. For this, you can use the Context.invoke() or Context.forward() methods.

但是,他们不提供替代方案。

例如,假设我们想在执行假设 our_cli buy some_item 之前使用我们自己的 CLI 调用 our_cli get user-funds 来验证用户的资金。 如果不使用 Context.invokeContext.forward,该怎么做?


PS:这不是关于使用invokeforward函数的问题。这已经在这里讨论过:link, link.

感谢@StephenRauch 提供直接调用 python 代码的提示。
这是一个简化的示例,展示了如何重构 Context.invoke 调用以直接调用 python 代码。

例子

假设我们在购买假设商品之前调用了 CLI 的 get-user-funds 命令来获取用户的预算。

import click

# E.g., assume the price and funds come from some API
def get_funds(user): return 100  
def get_price(item): return 50   

@click.group()
@click.pass_context
def our_cli(ctx): 
    # To simplify, assume that the CLI already knows the relevant user.
    ctx.obj = {"user": "Cindy"} 


@our_cli.command()
@click.argument("user")
def get_user_funds(user):
    # Normally we would use this command to print the funds 
    # of a user to stdout.
    funds = get_funds(user)
    click.echo(f"{funds=}")
    return funds


@our_cli.command()
@click.argument("item")
@click.pass_context
def buy_item(ctx, item):
    # This is the `invoke` call that we wish to refactor.
    funds = ctx.invoke(get_user_funds)
    if funds >= get_price(item):
        print(f"bought {item}")
    else:
        print("f{funds}")


if __name__ == "__main__":
    our_cli()

重构

除了调用Context.invoke获取资金,我们还可以直接调用python代码。
我们可以通过如下重写 buy_item 来做到这一点:

@our_cli.command()
@click.argument("item")
@click.pass_context
def buy_item(ctx: click.Context, item: str):
    # Now we call python code directly.
    funds = get_funds(ctx.obj["user"])
    # Note that bypass the click.echo(f"{funds=}") call now. This
    #  is probably something we would like in this example.
    if funds >= get_price(item):
        print(f"bought {item}")
    else:
        print("f{funds}")

结束语

在这个例子中,重构非常简单。
我们已经有了可以直接调用的 python 函数(即 get_funds)。
在处理更复杂的代码时,您可能必须重构代码。
在我的例子中,除其他外,我必须将我想直接从 @click.command 注释函数调用的逻辑提取到普通 python 函数。
之后,我能够用直接函数调用替换 Context.invoke 调用。