单击 - 基于其他选项的提示的动态默认值

Click - Dynamic Defaults for Prompts based on other options

我正在使用 Click to build a CLI interface. Click offers dynamic defaults for prompts, which is great. Also 提供了一些关于如何使用自定义点击实现动态默认值的见解-class 从而在评估默认值时提供更灵活的选项。

我现在想做的是根据另一个提供的点击选项获得动态默认值,例如

python mymodule --param1 something --param2 somethingelse

现在,如果 param2 为空,我想尝试根据提供的 param1 输入获取动态默认值,例如:

@click.command()
@click.option('--param1', prompt=True)
@click.option('--param2', prompt=True, default=lambda: myfunct(param1))
def cmd(param1, param2):
    pass

myfunct(param1:str=None):
    param2 = None
    #get param2 based on param1 input
    return param2

关于什么是完成这项工作的最佳方式有什么想法吗? 是否保证在 param2 之前评估(并提示)param1

扩展 所需的功能可以像这样完成:

自定义 Class:

import click

class OptionPromptNull(click.Option):
    _value_key = '_default_val'

    def __init__(self, *args, **kwargs):
        self.default_option = kwargs.pop('default_option', None)
        super(OptionPromptNull, self).__init__(*args, **kwargs)

    def get_default(self, ctx):
        if not hasattr(self, self._value_key):
            if self.default_option is None:
                default = super(OptionPromptNull, self).get_default(ctx)
            else:
                arg = ctx.params[self.default_option]
                default = self.type_cast_value(ctx, self.default(arg))
            setattr(self, self._value_key, default)
        return getattr(self, self._value_key)

    def prompt_for_value(self, ctx):
        default = self.get_default(ctx)

        # only prompt if the default value is None
        if default is None:
            return super(OptionPromptNull, self).prompt_for_value(ctx)

        return default

使用自定义 Class:

要使用自定义 class,您需要将三个参数传递给 click.option 装饰器,例如:

@click.option('--param3', cls=OptionPromptNull, default_option='param1',
              default=lambda x: get_a_value(x), prompt="Enter Param3")
  • cls需要参考自定义class.

  • default_option 需要指定将哪个选项传递给 default 可调用对象。

  • default 指定用于获取默认值的可调用对象。

这是如何工作的?

之所以有效,是因为 click 是一个设计良好的 OO 框架。 @click.option() 装饰器通常实例化一个 click.Option 对象,但允许使用 cls 参数覆盖此行为。因此,在我们自己的 class 中继承 click.Option 并覆盖所需的方法是一件相对容易的事情。

在这种情况下,我们超越了 click.Option.get_default()click.Option.prompt_for_value() 方法。在 prompt_for_value() 中,我们仅在默认为 None 时提示。然后在 get_default() 中我们调用 default 函数传递所需的(先前输入的)参数。

为了澄清问题的一部分,首先评估选项:按照它们在命令行上传递的顺序,其次:按照它们为未在命令行上传递的选项声明的顺序.

测试代码:

@click.command()
@click.option('--param1', prompt="Enter Param1")
@click.option('--param2', cls=OptionPromptNull,
              default=lambda: get_value_none(), prompt="Enter Param2")
@click.option('--param3', cls=OptionPromptNull, default_option='param1',
              default=lambda x: get_a_value(x), prompt="Enter Param3")
def cli(param1, param2, param3):
    click.echo("param1: '{}'".format(param1))
    click.echo("param2: '{}'".format(param2))
    click.echo("param3: '{}'".format(param3))


def get_value_none():
    return None

def get_a_value(val):
    return val

if __name__ == "__main__":
    commands = (
        r'',
        r'--param3 5',
        '--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)]
-----------
> 
Enter Param1: 3
Enter Param2: 4
param1: '3'
param2: '4'
param3: '3'
-----------
> --param3 5
Enter Param1: 3
Enter Param2: 4
param1: '3'
param2: '4'
param3: '5'
-----------
> --help
Usage: test.py [OPTIONS]

Options:
  --param1 TEXT
  --param2 TEXT
  --param3 TEXT
  --help         Show this message and exit.