单击命令行界面:如果未设置其他可选选项,则使选项为必需 [英] Click Command Line Interfaces: Make options required if other optional option is unset

查看:97
本文介绍了单击命令行界面:如果未设置其他可选选项,则使选项为必需的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当使用Python 点击库编写命令行界面(CLI)时,是否可能定义例如三个选项,仅在第一个(可选)未设置的情况下才需要第二个和第三个?

When writing a command-line interface (CLI) with the Python click library, is it possible to define e.g. three options where the second and third one are only required if the first (optional) one was left unset?

我的用例是一个登录系统,该系统允许我通过authentication token(选项1)或通过username(选项2)和password(选项3)进行身份验证.

My use case is a log-in system which allows me to authenticate either via an authentication token (option 1), or, alternatively, via username (option 2) and password (option 3).

如果给出了令牌,则无需检查是否定义了usernamepassword或提示它们.否则,如果省略了令牌,则必须提供usernamepassword并必须给出.

If the token was given, there is no need to check for username and password being defined or prompting them. Otherwise, if the token was omitted then username and password become required and must be given.

可以使用回调以某种方式完成此操作吗?

Can this be done somehow using callbacks?

我的入门代码当然不能反映预期的模式:

My code to get started which of course does not reflect the intended pattern:

@click.command()
@click.option('--authentication-token', prompt=True, required=True)
@click.option('--username', prompt=True, required=True)
@click.option('--password', hide_input=True, prompt=True, required=True)
def login(authentication_token, username, password):
    print(authentication_token, username, password)

if __name__ == '__main__':
    login()

推荐答案

这可以通过构建从click.Option派生的自定义类来完成,然后在该类中使用click.Option.handle_parse_result()方法,例如:

This can be done by building a custom class derived from click.Option, and in that class over riding the click.Option.handle_parse_result() method like:

import click

class NotRequiredIf(click.Option):
    def __init__(self, *args, **kwargs):
        self.not_required_if = kwargs.pop('not_required_if')
        assert self.not_required_if, "'not_required_if' parameter required"
        kwargs['help'] = (kwargs.get('help', '') +
            ' NOTE: This argument is mutually exclusive with %s' %
            self.not_required_if
        ).strip()
        super(NotRequiredIf, self).__init__(*args, **kwargs)

    def handle_parse_result(self, ctx, opts, args):
        we_are_present = self.name in opts
        other_present = self.not_required_if in opts

        if other_present:
            if we_are_present:
                raise click.UsageError(
                    "Illegal usage: `%s` is mutually exclusive with `%s`" % (
                        self.name, self.not_required_if))
            else:
                self.prompt = None

        return super(NotRequiredIf, self).handle_parse_result(
            ctx, opts, args)

使用自定义类:

要使用自定义类,请将cls参数传递给click.option装饰器,例如:

Using Custom Class:

To use the custom class, pass the cls parameter to click.option decorator like:

@click.option('--username', prompt=True, cls=NotRequiredIf,
              not_required_if='authentication_token')

这是如何工作的?

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

How does this work?

This works because click is a well designed OO framework. The @click.option() decorator usually instantiates a click.Option object but allows this behavior to be overridden with the cls parameter. So it is a relatively easy matter to inherit from click.Option in our own class and over ride the desired methods.

在这种情况下,如果存在authentication-token令牌,我们将越过click.Option.handle_parse_result()并禁用对user/password的需要,并抱怨是否同时存在两个user/passwordauthentication-token.

In this case we over ride click.Option.handle_parse_result() and disable the need to user/password if authentication-token token is present, and complain if both user/password are authentication-token are present.

注意:此答案的灵感来自此答案

Note: This answer was inspired by this answer

@click.command()
@click.option('--authentication-token')
@click.option('--username', prompt=True, cls=NotRequiredIf,
              not_required_if='authentication_token')
@click.option('--password', prompt=True, hide_input=True, cls=NotRequiredIf,
              not_required_if='authentication_token')
def login(authentication_token, username, password):
    click.echo('t:%s  u:%s  p:%s' % (
        authentication_token, username, password))

if __name__ == '__main__':
    login('--username name --password pword'.split())
    login('--help'.split())
    login(''.split())
    login('--username name'.split())
    login('--authentication-token token'.split())

结果:

来自login('--username name --password pword'.split()):

t:None  u:name  p:pword

来自login('--help'.split()):

Usage: test.py [OPTIONS]

Options:
  --authentication-token TEXT
  --username TEXT              NOTE: This argument is mutually exclusive with
                               authentication_token
  --password TEXT              NOTE: This argument is mutually exclusive with
                               authentication_token
  --help                       Show this message and exit.

这篇关于单击命令行界面:如果未设置其他可选选项,则使选项为必需的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆