Using the Context
When you create a Typer application it uses Click underneath. And every Click application has a special object called a "Context" that is normally hidden.
But you can access the context by declaring a function parameter of type typer.Context
.
You might have read it in CLI Option Callback and Context.
The same way, in commands or in the main Typer
callback you can access the context by declaring a function parameter of type typer.Context
.
Getting the contextΒΆ
For example, let's say that you want to execute some logic in a Typer
callback depending on the subcommand that is being called.
You can get the name of the subcommand from the context:
import typer
app = typer.Typer()
@app.command()
def create(username: str):
print(f"Creating user: {username}")
@app.command()
def delete(username: str):
print(f"Deleting user: {username}")
@app.callback()
def main(ctx: typer.Context):
"""
Manage users in the awesome CLI app.
"""
print(f"About to execute command: {ctx.invoked_subcommand}")
if __name__ == "__main__":
app()
Check it:
Executable callbackΒΆ
By default, the callback is only executed right before executing a command.
And if no command is provided, the help message is shown.
But we could make it run even without a subcommand with invoke_without_command=True
:
import typer
app = typer.Typer()
@app.command()
def create(username: str):
print(f"Creating user: {username}")
@app.command()
def delete(username: str):
print(f"Deleting user: {username}")
@app.callback(invoke_without_command=True)
def main():
"""
Manage users in the awesome CLI app.
"""
print("Initializing database")
if __name__ == "__main__":
app()
Check it:
Exclusive executable callbackΒΆ
We might not want the callback to be executed if there's already other command that will be executed.
For that, we can get the typer.Context
and check if there's an invoked command in ctx.invoked_subcommand
.
If it's None
, it means that we are not calling a subcommand but the main program (the callback) directly:
import typer
app = typer.Typer()
@app.command()
def create(username: str):
print(f"Creating user: {username}")
@app.command()
def delete(username: str):
print(f"Deleting user: {username}")
@app.callback(invoke_without_command=True)
def main(ctx: typer.Context):
"""
Manage users in the awesome CLI app.
"""
if ctx.invoked_subcommand is None:
print("Initializing database")
if __name__ == "__main__":
app()
Check it:
Configuring the contextΒΆ
You can pass configurations for the context when creating a command or callback.
To read more about the available configurations check the docs for Click's Context
.
For example, you could keep additional CLI parameters not declared in your CLI program with ignore_unknown_options
and allow_extra_args
.
Then you can access those extra raw CLI parameters as a list
of str
in ctx.args
:
import typer
app = typer.Typer()
@app.command(
context_settings={"allow_extra_args": True, "ignore_unknown_options": True}
)
def main(ctx: typer.Context):
for extra_arg in ctx.args:
print(f"Got extra arg: {extra_arg}")
if __name__ == "__main__":
app()
Tip
Notice that it saves all the extra CLI parameters as a raw list
of str
, including the CLI option names and values, everything together.