Python 动态指定选项和参数

Python 动态指定选项和参数,python,command-line-interface,python-click,Python,Command Line Interface,Python Click,我想从数据库加载参数和选项。我允许用户定义自己的选项和参数。用户可以在命令行上调用远程api。它们指定指向端点的URL和参数。下面是来自数据库的数据 [ { "name": "thename1", "short": "a", "long": "ace" "type": "string", "required": false }, { "name": "thename2",

我想从数据库加载参数和选项。我允许用户定义自己的选项和参数。用户可以在命令行上调用远程api。它们指定指向端点的URL和参数。下面是来自数据库的数据

[
    {
        "name": "thename1",
        "short": "a",
        "long": "ace"
        "type": "string",
        "required": false
    },
    {
        "name": "thename2",
        "short": "b",
        "long": "bravo"
        "type": "number",
        "required": true
    },
    {
        "name": "thename3",
        "short": "c",
        "long": "candy"
        "type": "array",
        "required": true
    }
]
这些参数与远程端点所需的参数一致。每个API端点都有不同的参数,因此它们需要是动态的。下面是命令行的外观

command run www.mysite.com/api/get -a testparam --bravo testpara2 -c item1 item2

参数和值将映射到URL中。有没有办法在单击中设置动态参数?

这可以通过构建自定义装饰器来完成,该装饰器在将给定的数据结构转换为单击等价物后多次调用
单击选项

代码: 使用\u db中的
选项\u
装饰器: 要使用新的decorator,请修饰命令,并从db传递选项数据,如:

@options_from_db(run_options)
def command(*args, **kwargs):
    ....
这是怎么回事?
@click.option()
装饰器与所有装饰器一样,是一个函数。在本例中,它对修饰函数进行注释,并返回相同的函数。所以我们可以简单地多次调用它来注释我们修饰过的函数

注意:您的
数组
参数违反了不允许
nargs
<0打开选项的单击要求。但有一种方法可以实现这一点,这个答案就是从那里开始使用

代码来源: 测试代码: 结果:
本节中指定了
run
的位置?还是只有一个命令,它总是被称为run?@stephernauch是的,它只有一个命令,叫做run。我可以添加其他函数,如get、post、patch和put http谓词。但是
run
是目前唯一的一个。这太棒了。我会把结果告诉你的这很有帮助,很好。
@options_from_db(run_options)
def command(*args, **kwargs):
    ....
class OptionEatAll(click.Option):

    def __init__(self, *args, **kwargs):
        self.save_other_options = kwargs.pop('save_other_options', True)
        nargs = kwargs.pop('nargs', -1)
        assert nargs == -1, 'nargs, if set, must be -1 not {}'.format(nargs)
        super(OptionEatAll, self).__init__(*args, **kwargs)
        self._previous_parser_process = None
        self._eat_all_parser = None

    def add_to_parser(self, parser, ctx):

        def parser_process(value, state):
            # method to hook to the parser.process
            done = False
            value = [value]
            if self.save_other_options:
                # grab everything up to the next option
                while state.rargs and not done:
                    for prefix in self._eat_all_parser.prefixes:
                        if state.rargs[0].startswith(prefix):
                            done = True
                    if not done:
                        value.append(state.rargs.pop(0))
            else:
                # grab everything remaining
                value += state.rargs
                state.rargs[:] = []
            value = tuple(value)

            # call the actual process
            self._previous_parser_process(value, state)

        retval = super(OptionEatAll, self).add_to_parser(parser, ctx)
        for name in self.opts:
            our_parser = parser._long_opt.get(
                name) or parser._short_opt.get(name)
            if our_parser:
                self._eat_all_parser = our_parser
                self._previous_parser_process = our_parser.process
                our_parser.process = parser_process
                break
        return retval
run_options = [
    {
        "name": "thename1",
        "short": "a",
        "long": "ace",
        "type": "string",
        "required": False
    }, {
        "name": "thename2",
        "short": "b",
        "long": "bravo",
        "type": "number",
        "required": True
    }, {
        "name": "thename3",
        "short": "c",
        "long": "candy",
        "type": "array",
        "required": True
    }
]

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

@cli.command()
@options_from_db(run_options)
@click.argument('url')
def run(*args, **kwargs):
    click.echo('args: {}'.format(args) )
    click.echo('kwargs: {}'.format(kwargs))


if __name__ == "__main__":
    commands = (
        'run www.mysite.com/api/get -a testparam --bravo 5 -c item1 item2',
        '',
        '--help',
        'run --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
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)]
-----------
> run www.mysite.com/api/get -a testparam --bravo 5 -c item1 item2
args: ()
kwargs: {'thename1': 'testparam', 'thename2': 5.0, 'thename3': ('item1', 'item2'), 'url': 'www.mysite.com/api/get'}
-----------
> 
Usage: test.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  run
-----------
> --help
Usage: test.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  run
-----------
> run --help
Usage: test.py run [OPTIONS] URL

Options:
  -a, --ace TEXT
  -b, --bravo FLOAT  [required]
  -c, --candy TEXT   [required]
  --help             Show this message and exit.