Commands with multiple common options going into one argument using custom decorator

You can distill multiple options into a single object quite simply by constructing a decorator like:

Code:

def magic_options(func):
    @click.option('--magic-bar')
    @click.option('--magic-foo')
    def distill_magic(magic_foo, magic_bar, **kwargs):
        kwargs['magic'] = Magic(magic_foo, magic_bar)
        func(**kwargs)

    return distill_magic

Using the decorator

You can then apply the decorator to the command function like:

@click.command('Colored-Magic')
@click.option('--color')
@magic_options
def cli(magic, color):
    ...

It needs to be applied to the bare function. This is because the function returned by click.option has been modified by the click framework and it won't work the way you expected.

Test Code:

import click

@click.command('Colored-Magic')
@click.option('--color')
@magic_options
def cli(magic, color):
    click.echo(str(magic))
    click.echo(color)


class Magic(object):
    def __init__(self, magic_foo, magic_bar):
        self.magic_foo = magic_foo
        self.magic_bar = magic_bar

    def __str__(self):
        return "foo: {}  bar: {}".format(self.magic_foo, self.magic_bar)


if __name__ == "__main__":
    commands = (
        '--magic-foo fooby --magic-bar barbecue',
        '--magic-foo fooby',
        '--magic-bar barbecue',
        '',
        '--help',
    )

    import sys, time

    time.sleep(1)
    print('Click Version: {}'.format(click.__version__))
    print('Python Version: {}'.format(sys.version))
    for cmd in commands:
        try:
            time.sleep(0.1)
            print('-----------')
            print('> ' + cmd)
            time.sleep(0.1)
            cli(cmd.split())

        except BaseException as exc:
            if str(exc) != '0' and \
                    not isinstance(exc, (click.ClickException, SystemExit)):
                raise

Results:

Click Version: 6.7
Python Version: 3.6.3 (v3.6.3:2c5fed8, Oct  3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)]
-----------
> --magic-foo fooby --magic-bar barbecue
foo: fooby  bar: barbecue

-----------
> --magic-foo fooby
foo: fooby  bar: None

-----------
> --magic-bar barbecue
foo: None  bar: barbecue

-----------
> 
foo: None  bar: None

-----------
> --help
Usage: test.py [OPTIONS]

Options:
  --color TEXT
  --magic-bar TEXT
  --magic-foo TEXT
  --help            Show this message and exit.