Subcommands usually need shared setup like config, clients or a database handle, without global variables.
Each invocation creates a context (click.Context) holding the command, the parsed parameters, the parent context (for subcommands) and a free slot, ctx.obj, for your own data. Contexts form a chain from the root group down to the running command.
Get the current context with @click.pass_context, which passes ctx as the first argument. The typical pattern: the group builds shared state and stores it in ctx.obj; subcommands read it.
@click.group()
@click.pass_context
def cli(ctx):
ctx.obj = Config(...)Subcommands then use @click.pass_obj to receive ctx.obj directly, or @click.make_pass_decorator(Config) to find the nearest object of that type in the chain.
Other context features:
ctx.ensure_object(dict)createsctx.objif missing, convenient when the root may be called from tests with or without an object.ctx.invoke(other_command, x=1)andctx.forward(other_command)call another command's callback.ctx.call_on_close(fn)andctx.with_resource(cm)register cleanup that runs when the context ends: close a database connection opened in the group callback, for example.ctx.exit(code)andctx.abort()end the program;ctx.fail("msg")reports a usage error.ctx.metais a dict shared across the whole chain, for extensions.click.get_current_context()retrieves the context from anywhere during execution.
obj= can also be passed to main() or CliRunner.invoke(), which is handy for injecting fakes in tests.
Example
import click
class Config:
def __init__(self, env):
self.env = env
self.log = []
@click.group()
@click.option("--env", default="dev")
@click.pass_context
def cli(ctx, env):
ctx.obj = Config(env)
ctx.call_on_close(lambda: click.echo(f"closing ({len(ctx.obj.log)} actions)"))
@cli.command()
@click.argument("service")
@click.pass_obj
def deploy(cfg, service):
cfg.log.append(service)
click.echo(f"Deploying {service} to {cfg.env}")
cli.main(["--env", "prod", "deploy", "api"], standalone_mode=False)
# Deploying api to prod
# closing (1 actions)Remember
- ctx.obj carries shared state from groups to subcommands
- @pass_context gives the context; @pass_obj gives ctx.obj
- call_on_close and with_resource clean up when the command ends
Watch out for
- Using module-level globals for configuration, which leaks state between test runs.