如何在没有 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.forward
和 click.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.invoke
或 Context.forward
,该怎么做?
PS:这不是关于使用invoke
和forward
函数的问题。这已经在这里讨论过: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
调用。
click
库不鼓励对 'self-invoke' CLI 命令调用 click.forward
和 click.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.invoke
或 Context.forward
,该怎么做?
PS:这不是关于使用invoke
和forward
函数的问题。这已经在这里讨论过: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
调用。