仅在使用单击时进行选择时才需要和选项

时间:2017-09-27 06:32:40

标签: python command-line-interface python-click

使用click时,我知道如何定义multiple choice option。我也知道如何将选项设置为必需的。但是,如果选项B的值为A,我怎么能指出只需要选项foo

以下是一个例子:

import click

@click.command()
@click.option('--output',
              type=click.Choice(['stdout', 'file']), default='stdout')
@click.option('--filename', type=click.STRING)
def main(output, filename):
    print("output: " + output)
    if output == 'file':
        if filename is None:
            print("filename must be provided!")
        else:
            print("filename: " + str(filename))

if __name__ == "__main__":
    main()

如果output选项为stdout,则不需要filename。但是,如果用户选择outputfile,则必须提供其他选项filename。点击支持这种模式吗?

在函数的开头,我可以添加如下内容:

if output == 'file' and filename is None:
    raise ValueError('When output is "file", a filename must be provided')

但我感兴趣的是,是否有更好/更清洁的解决方案。

2 个答案:

答案 0 :(得分:2)

在本例的特定情况下,我认为更简单的方法是摆脱--output,如果未指定stdout并且{{1} --filename,则假设--filename指定了,然后使用它而不是stdout

但假设这是一个人为的例子,你可以继承click.Option以允许挂钩点击处理:

自定义类:

class OptionRequiredIf(click.Option):

    def full_process_value(self, ctx, value):
        value = super(OptionRequiredIf, self).full_process_value(ctx, value)

        if value is None and ctx.params['output'] == 'file':
            msg = 'Required if --output=file'
            raise click.MissingParameter(ctx=ctx, param=self, message=msg)
        return value

使用自定义类:

要使用自定义类,请将其作为cls参数传递给选项装饰器,如:

@click.option('--filename', type=click.STRING, cls=OptionRequiredIf)

测试代码:

import click

@click.command()
@click.option('--output',
              type=click.Choice(['stdout', 'file']), default='stdout')
@click.option('--filename', type=click.STRING, cls=OptionRequiredIf)
def main(output, filename):
    print("output: " + output)
    if output == 'file':
        if filename is None:
            print("filename must be provided!")
        else:
            print("filename: " + str(filename))


main('--output=file'.split())

结果:

Usage: test.py [OPTIONS]

Error: Missing option "--filename".  Required if --output=file

答案 1 :(得分:1)

我扩展了斯蒂芬的答案,并使其更通用:

class OptionRequiredIf(click.Option):
    """
    Option is required if the context has `option` set to `value`
    """

    def __init__(self, *a, **k):
        try:
            option = k.pop('option')
            value  = k.pop('value')
        except KeyError:
            raise(KeyError("OptionRequiredIf needs the option and value "
                           "keywords arguments"))

        click.Option.__init__(self, *a, **k)
        self._option = option
        self._value = value

    def full_process_value(self, ctx, value):
        value = super(OptionRequiredIf, self).full_process_value(ctx, value)
        if value is None and ctx.params[self._option] == self._value:
            msg = 'Required if --{}={}'.format(self._option, self._value)
            raise click.MissingParameter(ctx=ctx, param=self, message=msg)
        return value

用法示例:

@click.option('--email', type=click.STRING,
              help='Settings for sending emails.',
              option='output', value='email', cls=OptionRequiredIf)

我受到https://perldoc.perl.org/perlintro.html

的启发