Python 同时使用多个选项,或者根本不使用

Python 同时使用多个选项,或者根本不使用,python,command-line-interface,python-click,Python,Command Line Interface,Python Click,正如标题所说,我想同时使用几个选项,或者根本不使用,但我的方法似乎相对丑陋,我想知道是否有更干净的方法来实现这一点。此外,我还研究了如何在argparse中实现它,但如果可能的话,我想在click中实现它(我试图避免使用nargs=[…]) 到目前为止,我的情况如下: @click.group(在没有命令的情况下调用命令=True,没有参数的情况下调用命令=True) @单击。选项( “-d”, “--组目录”, type=单击.Path(), default=“default”, help=”

正如标题所说,我想同时使用几个选项,或者根本不使用,但我的方法似乎相对丑陋,我想知道是否有更干净的方法来实现这一点。此外,我还研究了如何在
argparse
中实现它,但如果可能的话,我想在
click
中实现它(我试图避免使用
nargs=[…]

到目前为止,我的情况如下:

@click.group(在没有命令的情况下调用命令=True,没有参数的情况下调用命令=True)
@单击。选项(
“-d”,
“--组目录”,
type=单击.Path(),
default=“default”,
help=”查找同时从中运行多个作业的TOML文件的目录;默认为melmetal的配置目录:“~/.melmetal”在Unix系统上,以及“C:\\Users\\user\\.melmetal”在Windows上,
)
@单击.option(“-f”,“--group file”,help=“TOML文件名”)
@单击。选项(
-n“,--group name”,help=“作业组的名称”
)
@单击。选项(
“--无调试”,
是_flag=True,
类型=布尔,
help=“防止将日志记录输出到终端”,
)
@单击。传递上下文
@渔获量
def main(ctx、组目录、组文件、组名称、无调试):
选项=[组文件,组名称]
group_dir=如果没有任何选项,则为False,否则为group_dir
options.append(组目录)
如果没有(选项):
通过
elif非全部(选项):
记录器错误(
着色(“红色”、“对不起;您必须同时使用所有选项。”)
)
出口(1)
其他:
[...]
还有第二个例子:

if any(createStuff):
    if not all(createStuff):
        le(
            colorize("red", 'Sorry; you must use both the "--config-dir" and "--config-file" options at once.')
        )
        exit(1)
elif any(filtered):
    if len(filtered) is not len(drbc):
        le(
            colorize("red", 'Sorry; you must use all of "--device", "--repo-name", "--backup-type", and "--config-dir" at once.')
        )
        exit(1)
else:
    ctx = click.get_current_context()
    click.echo(ctx.get_help())
    exit(0)

当没有给出子命令时,如何让帮助文本显示?据我所知,这应该是自动发生的,但对于我的代码,它会自动转到主函数。我的解决方法的一个示例在第二个示例中,即在
else
语句下。

您可以通过构建从
click.Option
派生的自定义类来强制使用组中的所有选项,并在该类中覆盖
click.Option。处理\u parse\u result()
方法如下:

自定义选项类: 使用自定义类: 要使用自定义类,请将
cls
参数传递给
单击。选项类似于装饰器:

@click.option('--opt1', cls=GroupedOptions, opt_group=1)
此外,使用
opt_group
参数给出选项组编号

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

在本例中,我们跳过
单击.Option.handle\u parse\u result()
,并检查是否指定了组中的其他选项

注:此答案的灵感来自

测试代码: 结果: 你可以用。它添加选项组,并允许对任何参数组定义约束。它包括一个
all\u或\u none
约束,它完全满足您的要求

免责声明:我是Cloup的作者

来自cloup导入命令、选项、选项组
从cloup.constraints导入所有或无
@命令()
@选项组(
“我独特的选择”,
选项('--opt1'),
选项('--opt2'),
选项('--opt3'),
约束=全部或无,
)
def cmd(**kwargs):
印刷品(kwargs)
帮助:

用法:cmd[选项] 我的特殊选项[提供全部或全部]: --opt1文本 --opt2文本 --opt3文本 其他选择: --帮助显示此消息并退出。
错误消息:

用法:cmd[选项] 请尝试“cmd--help”以获取帮助。 错误:必须设置以下所有参数或不设置任何参数: --opt1,--opt2,--opt3 这个错误消息可能不是最好的,所以我可能会在下一个版本中更改它。但在Cloup中,您可以轻松地自己完成,例如:

provide_all_or_none = all_or_none.rephrased(
    error='if you provide one of the following options, you need to provide '
          'all the others in the list:\n{param_list}'
)
然后你会得到:

错误:如果提供以下选项之一,则需要提供列表中的所有其他选项:
--opt1,--opt2,--opt3

你应该询问你的问题,而不是你的解决方案,这将有助于明确你想要实现的目标。比如说,我没有问题;我只是想知道是否有办法使代码更简单,比如在选项装饰器中使用关键字参数,因为目前这有点困难。让选项成为一个选项合适吗?按照您所描述的方式,听起来它们并不是真正独立的命令行参数,但是如果将它们作为独立的命令行参数,是否会使它们更加冗长?虽然我认为这是有道理的,因为他们和以前是同一类人。。。然而,这会使元组中的哪个元素是哪个值变得混乱,这会使它更加冗长,但我想不出当一个参数依赖于另一个参数时,我使用过任何命令行。但是看看你的例子,你真的需要吗?您可以从文件中确定目录,也可以从文件中确定目录,谢谢!我会尽快测试它;我可以在我的程序中使用这个吗?StackOverflow上的所有内容都是。您可以随意使用,但如果您重新发布,您需要属性。再次感谢您!(上次)我想先测试一下;我会这样做,并将答案标记为正确!虽然我已经选择了一个答案,但我一定会检查这一点;看起来很有趣!很高兴听到这个消息!我忘了提到我是Cloup的作者(我会加上它)。请随时给我一个反馈,如果你检查它(特别是如果负面!)会做的!再次感谢你!
@click.command()
@click.option('--opt1', cls=GroupedOptions, opt_group=1)
@click.option('--opt2', cls=GroupedOptions, opt_group=1)
@click.option('--opt3', cls=GroupedOptions, opt_group=1)
@click.option('--opt4', cls=GroupedOptions, opt_group=2)
@click.option('--opt5', cls=GroupedOptions, opt_group=2)
def cli(**kwargs):
    for arg, value in kwargs.items():
        click.echo("{}: {}".format(arg, value))

if __name__ == "__main__":
    commands = (
        '--opt1=x',
        '--opt4=a',
        '--opt4=a --opt5=b',
        '--opt1=x --opt2=y --opt3=z --opt4=a --opt5=b',
        '--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)]
-----------
> --opt1=x
Error: Illegal usage: When using option 'opt1' must also use all of options ('opt2', 'opt3')
-----------
> --opt4=a
Error: Illegal usage: When using option 'opt4' must also use all of options ('opt5',)
-----------
> --opt4=a --opt5=b
opt4: a
opt5: b
opt1: None
opt2: None
opt3: None
-----------
> --opt1=x --opt2=y --opt3=z --opt4=a --opt5=b
opt1: x
opt2: y
opt3: z
opt4: a
opt5: b
-----------
> --help
Usage: test.py [OPTIONS]

Options:
  --opt1 TEXT
  --opt2 TEXT
  --opt3 TEXT
  --opt4 TEXT
  --opt5 TEXT
  --help       Show this message and exit.
-----------
> 
opt1: None
opt2: None
opt3: None
opt4: None
opt5: None
provide_all_or_none = all_or_none.rephrased(
    error='if you provide one of the following options, you need to provide '
          'all the others in the list:\n{param_list}'
)