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_cloup as typer

app = typer.Typer()


@app.command()
def create(username: str):
    typer.echo(f"Creating user: {username}")


@app.command()
def delete(username: str):
    typer.echo(f"Deleting user: {username}")


@app.callback()
def main(ctx: typer.Context):
    """
    Manage users in the awesome CLI app.
    """
    typer.echo(f"About to execute command: {ctx.invoked_subcommand}")


if __name__ == "__main__":
    app()

Check it:

$ python main.py create Camila

// We get the message from the callback
About to execute command: create
Creating user: Camila

$ python main.py delete Camila

// We get the message from the callback, this time with delete
About to execute command: delete
Deleting user: Camila

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_cloup as typer

app = typer.Typer()


@app.command()
def create(username: str):
    typer.echo(f"Creating user: {username}")


@app.command()
def delete(username: str):
    typer.echo(f"Deleting user: {username}")


@app.callback(invoke_without_command=True)
def main():
    """
    Manage users in the awesome CLI app.
    """
    typer.echo("Initializing database")


if __name__ == "__main__":
    app()

Check it:

$ python main.py

// The callback is executed, we don't get the default help message
Initializing database

// Try with a command
$ python main.py create Camila

// The callback is still executed
Initializing database
Creating user: Camila

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_cloup as typer

app = typer.Typer()


@app.command()
def create(username: str):
    typer.echo(f"Creating user: {username}")


@app.command()
def delete(username: str):
    typer.echo(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:
        typer.echo("Initializing database")


if __name__ == "__main__":
    app()

Check it:

$ python main.py

// The callback is executed
Initializing database

// Check it with a subcommand
$ python main.py create Camila

// This time the callback is not executed
Creating user: Camila

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_cloup as 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:
        typer.echo(f"Got extra arg: {extra_arg}")


if __name__ == "__main__":
    app()
$ python main.py --name Camila --city Berlin

Got extra arg: --name
Got extra arg: Camila
Got extra arg: --city
Got extra arg: Berlin

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.

You can ask questions about Typer. Try:
How can I terminate a program?
How to launch applications?
How to add help to CLI argument?