Skip to content

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:

fast β†’python main.py create Camila
About to execute command: create
Creating user: Camila

python main.py delete Camila
About to execute command: delete
Deleting user: Camila

restart ↻

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:

fast β†’python main.py
Initializing database

python main.py create Camila
Initializing database
Creating user: Camila

restart ↻

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:

fast β†’python main.py
Initializing database

python main.py create Camila
Creating user: Camila

restart ↻

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()
fast β†’python main.py --name Camila --city Berlin
Got extra arg: --name
Got extra arg: Camila
Got extra arg: --city
Got extra arg: Berlin

restart ↻

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.