'How to add common options to sub commands which can go *after* the name of the sub command

Using the CLI library click I have an application script app.py with the two sub commands read and write:

@click.group()
@click.pass_context
def cli(ctx):
    pass

@cli.command()
@click.pass_context
def read(ctx):
    print("read")

@cli.command()
@click.pass_context
def write(ctx):
    print("write")

I want to declare a common option --format. I know I can add it as a option to the command group via

@click.group()
@click.option('--format', default='json')
@click.pass_context
def cli(ctx, format):
    ctx.obj['format'] = format

But then I cannot give the option after the command, which in my use case is a lot more natural. I want to be able to issue in the shell:

app.py read --format XXX 

but with the outlined set-up I get the message Error: no such option: --format. The script only accepts the option before the command.

So my question is: How can I add a common option to both sub commands so that it works as if the option were given to each sub command?



Solution 1:[1]

Yes you can add common options to sub commands which can go after the name of the sub command.
You can have options on parent command as well as on sub commands.

Check out below code snippet

import click
from functools import wraps

@click.group()
def cli():
    pass

def common_options(f):
    @wraps(f)
    @click.option('--option1', '-op1', help='Option 1 help text', type=click.FLOAT)
    @click.option('--option2', '-op2', help='Option 2 help text', type=click.FLOAT)
    def wrapper(*args, **kwargs):
        return f(*args, **kwargs)

    return wrapper

@cli.group(invoke_without_command=True)
@common_options
@click.pass_context
def parent(ctx, option1, option2):
    ctx.ensure_object(dict)
    if ctx.invoked_subcommand is None:
         click.secho('Parent group is invoked. Perform specific tasks to do!', fg='bright_green')

@parent.command()
@click.option('--sub_option1', '-sop1', help='Sub option 1 help text', type=click.FLOAT)
@common_options
def sub_command1(option1, option2, sub_option1):
    click.secho('Perform sub command 1 operations', fg='bright_green')

@parent.command()
@click.option('--sub_option2', '-sop2', help='Sub option 2 help text', type=click.FLOAT)
@common_options
def sub_command2(option1, option2, sub_option2):
    click.secho('Perform sub command 2 operations', fg='bright_green')

if __name__ == "__main__":
    cli()

Usage

parent --help
=> prints parent group help text with options and sub commands

parent --option1 10 --option2 12
=> Parent group is invoked. Perform specific tasks to do!

parent sub_command1 --help
=> prints sub command 1 help text with options on sub commands

parent sub_command1 --option1 15 --option2 7 --sub_option1 5
=> Perform sub command 1 operations

parent sub_command2 --option1 15 --option2 7 --sub_option2 4
=> Perform sub command 2 operations

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nikhilkumar Hiragond