You can define a Command Group that accepts a -q
flag, then attach all the commands that you would want to be affected by this -q
flag as subcommands of that group. Save the -q
existence/non-existence in the group function, and pass it around to the subcommands as part of the command Context.
https://click.palletsprojects.com/en/8.0.x/commands/#nested-handling-and-contexts
Each time a command is invoked, a new context is created and linked with the parent context. Normally, you can’t see these contexts, but they are there. Contexts are passed to parameter callbacks together with the value automatically.
import click
@click.group()
@click.option("-q", help="Disable all prompts", flag_value=True, default=False)
@click.pass_context
def cli(ctx, q):
# Ensure that ctx.obj exists and is a dict
ctx.ensure_object(dict)
# Apply group-wide feature switches
ctx.obj["q"] = q
print(f"In group: {ctx.obj}")
@cli.command()
@click.option("--name", prompt=True)
def cmd1(name):
click.echo(f"Hello {name}!")
$ python cli.py
Usage: cli.py [OPTIONS] COMMAND [ARGS]...
Options:
-q Disable all prompts
...
Commands:
cmd1
$ python cli.py cmd1
In group: {'q': False}
Name: abc
Hello abc!
$ python cli.py cmd1 --name=xyz
In group: {'q': False}
Hello xyz!
$ python cli.py -q cmd1 --name=xyz
In group: {'q': True}
Hello xyz!
Here, notice that -q
is a flag on the entire CLI, not just for cmd1
, which, as I understand, is what you wanted. It doesn't affect cmd1
yet, but it's there in the context for all commands on that group.
How then do we actually disable the prompt based on what's q
in the context? You can define a custom click.Option
class and override the prompt_for_value(ctx)
method.
import click
DEFAULTS = {
"name": "automatically-set-name",
}
class DynamicPromptOption(click.Option):
def prompt_for_value(self, ctx):
q = ctx.obj.get("q")
print(f"In option {self.name}: q={q}")
if q:
return DEFAULTS[self.name]
return super().prompt_for_value(ctx)
Here, you skip the base implementation call to' prompt_for_value
when -q
was passed (technically, when q
is True
-ish), and just return some automatic default value directly (in my example above, maybe from a global constant or dictionary of values).
Then, make sure to set the cls=<custom class>
parameter on your @click.option
decorator to use your custom Option
class.
@cli.command()
@click.option("--name", prompt=True, cls=DynamicPromptOption)
def cmd1(name):
click.echo(f"Hello {name}!")
$ python cli.py cmd1
In group: {'q': False}
In option name: q=False
Name: abc
Hello abc!
$ python cli.py -q cmd1
In group: {'q': True}
In option name: q=True
Hello automatically-set-name!
The nice thing is that the interactive usage should be unaffected:
$ python cli.py cmd1
In group: {'q': False}
In option name: q=False
Name: abc
Hello abc!
$ python cli.py cmd1 --name=xyz
In group: {'q': False}
Hello xyz!
$ python cli.py cmd1 --name
In group: {'q': False}
Error: Option '--name' requires an argument.