单击命令行界面:如果未设置其他可选选项,则使选项为必需 [英] Click Command Line Interfaces: Make options required if other optional option is unset
问题描述
当使用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).
如果给出了令牌,则无需检查是否定义了username
和password
或提示它们.否则,如果省略了令牌,则必须提供username
和password
并必须给出.
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/password
和authentication-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屋!