首页 > 解决方案 > 如何在没有 Context.invoke 或 Context.forward 的情况下“自调用”python-click CLI 命令?

问题描述

click库不鼓励调用click.forwardclick.forward函数来“自调用”CLI 命令。引用文档

有时,从另一个命令调用一个命令可能会很有趣。这是 Click 通常不鼓励使用的模式,但仍有可能。为此,您可以使用 Context.invoke() 或 Context.forward() 方法。

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

例如,假设我们想our_cli get user-funds在执行假设的our_cli buy some_item. 不使用Context.invokeorContext.forward怎么办?


PS:这不是关于使用invokeandforward函数的问题。这已经在这里讨论过:链接链接

标签: pythonpython-3.xcommand-line-interfacepython-click

解决方案


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

例子

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

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()

重构

我们也可以直接调用python代码,而不是调用Context.invoke获取资金。
我们可以通过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}")

闭幕致辞

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


推荐阅读