使用自定义装饰器将具有多个常用选项的命令放入一个参数

2024-01-04

我想制作一个模块,使构建共享许多选项的单击命令变得非常简单。这些选项将被提取到传递到命令中的单个对象中。作为说明性示例:

from magic import magic_command
import click

@magic_command('Colored')
@click.option('--color')
def cmd(magic, color):
    pass

总指挥部将有许多--magic-...进入的选项magic对象传入cmd。我能够使用以下方法实现这一目标:

def magic_command(name):
    def decorator(func):
        @click.option('--magic-foo')
        @click.option('--magic-bar')
        def wrapper(magic_foo, magic_bar, **kwargs):
            print(f'initializing Magic with {magic_foo} and {magic_bar}')
            magic = Magic(magic_foo, magic_bar)
            func(magic, **kwargs)

        try:
            wrapper.__click_params__.extend(func.__click_params__)
        except AttributeError:
            pass

        return click.command(f'{name}-Magic')(wrapper)
    return decorator

然而,搞乱__click_params__看起来不是特别干净。

这个问题有点类似于this one https://stackoverflow.com/questions/50061342/is-it-possible-to-reuse-python-click-option-decorators-for-multiple-commands,但是这种方法不允许我将许多魔法选项浓缩成一个魔法对象。

为了详细说明,用这种方法我必须做

@magic_command('Colored')
@click.option('--color')
def cmd(magic_foo, magic_bar, color):
    magic = Magic(magic_foo, magic_bar)
    pass

但这意味着自定义代码需要知道有哪些魔法选项以及如何构造魔法。我想这可以使用简化**kwargs但仍然 - 理想情况下我想做好准备magic对象传递给cmd.


您可以通过构造一个装饰器将多个选项提取到一个对象中,例如:

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

使用装饰器

然后,您可以将装饰器应用到命令函数,例如:

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

它需要应用于裸函数。这是因为返回的函数click.option已被点击框架修改,它不会按您预期的方式工作。

测试代码:

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.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用自定义装饰器将具有多个常用选项的命令放入一个参数 的相关文章

随机推荐