In python 3.8 I want to define some click options that are common to multiple commands. I tried the following piece of code:
import click @click.group() @click.option( "-v", "--verbose", count=True, default=0, help="-v for DEBUG", ) @click.option( "--path", help="Main Path.", ) def cli(): pass @click.command("list") @click.option( "--list-option", help="Special option for list command.", ) def my_list_command(verbose, path, list_option): print(verbose, path, list_option) @click.command("find") @click.option( "--find-option", help="Special option for find command.", ) def my_find_command(verbose, path, find_option): print(verbose, path, find_option) cli.add_command(my_list_command) cli.add_command(my_find_command) if __name__ == '__main__': cli()
But when I try to run the command
python script.py list
I get an error
TypeError: cli() got an unexpected keyword argument 'verbose'
What I want, is that the command list
has the following three options: verbose
, path
and list-option
and that the command find
has the following three options: verbose
, path
and find-option
. I do not want to define the options for verbose
and path
twice.
Is there a way to do this?
I also tried to use @click.pass_context
but that does not seem to solev the issue.
Advertisement
Answer
The way you currently defined it it will work, but the --verbose
option belongs to the main command group, so you’d need to call it as python script.py --verbose list
(and my_find_command
and my_list_command
won’t receive it as an argument, only cli
).
To use the same option across multiple commands without repeating yourself too much, you can just assign it to a variable and then use it twice:
verbose_option = click.option( "-v", "--verbose", count=True, default=0, help="-v for DEBUG", ) ... @click.command() @verbose_option def foo(verbose): ... @click.command() @verbose_option def bar(verbose): ...
Unrelated, but while we’re at it: There’s a simpler way of grouping commands, without having to do cli.add_command(my_find_command)
: Just use @cli.command()
instead of @click.command()
:
import click option_verbose = click.option( "-v", "--verbose", count=True, default=0, help="-v for DEBUG", ) @click.group() def cli(): pass @cli.command("list") @option_verbose @click.option( "--list-option", help="Special option for list command.", ) def my_list_command(verbose, list_option): print(verbose, list_option) @cli.command("find") @option_verbose @click.option( "--find-option", help="Special option for find command.", ) def my_find_command(verbose, list_option): print(verbose, list_option) if __name__ == '__main__': cli()
If there’s several options you want to apply, you can define your own decorator that calls all those options on the argument:
def common_options(fn): return click.option( "-v", "--verbose", )( click.option( "-n", "--dry-run", )(fn) )