Thanks to @StephenRauch for the tip of calling the python code directly.
Here is a simplified example that shows how to refactor a Context.invoke call to call python code directly.
Example
Assume that we invoked our CLI's get-user-funds command to get a user's budget before we buy a hypothetical item.
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()
Refactoring
Instead of calling Context.invoke to get the funds, we can also call the python code directly.
We can do that by rewriting buy_item as follows:
@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}")
Closing Remarks
In this example, the refactoring was very simple.
We already had a python function (i.e. get_funds) that we could call directly.
When working with more complex code, it is likely that you have to restructure your code.
In my case, among other things, I had to extract the logic that I wanted to call directly from a @click.command annotated function to a normal python function.
After that, I was able to replace the Context.invoke call with a direct function call.