如何为点击处理的参数列表指定默认值?

How to specify a default value for argument list processed by click?

我有这行代码,预计会获取传递给我的 Python 脚本的所有文件名:

@click.argument("logs", nargs=-1, type=click.File('r'), required=1)

不传文件名时,我想默认为-,即标准输入。所以,如果我尝试:

@click.argument("logs", nargs=-1, type=click.File('r'), required=1, default="-")

点击变得不愉快并抛出此错误:

TypeError: nargs=-1 in combination with a default value is not supported.

有解决办法吗?我尝试设置 nargs=0 但会引发不同的错误:

IndexError: tuple index out of range

以下代码:

import click


@click.command()
@click.option('--logs', '-l', multiple=True, default='-')
def cli(logs):
    click.echo('\n'.join(logs))

将产生以下内容:

$ myhello -l foo.log -l bar.log -l baz.log
foo.log
bar.log
baz.log
$ myhello
-

因此,如果您可以使用重复的参数以不同的方式调用您的程序,那将是您正在寻找的解决方法。

由于 click 已明确声明他们禁用了问题所需的此特定功能,正如 this issue 在他们的项目中所报告的那样,将需要一个解决方法,并且可以作为一部分轻松实现Python 函数的一部分(而不是像其他答案的评论所建议的那样在 bash 代码上堆放更多)。

解决方法是简单地删除所需的参数并使用语句处理丢失的日志(类似于 this commit 所做的引用链接问题):

import sys
import click

@click.command()
@click.argument("logs", nargs=-1, type=click.File('r'))
def main(logs):
    if not logs:
        logs = (sys.stdin,)
    print(logs)
    # do stuff with logs

if __name__ == '__main__': 
    main()

示例执行

$ python fail.py 
(<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>,)
$ python fail.py -
(<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>,)
$ python fail.py foo bar
(<_io.TextIOWrapper name='foo' mode='r' encoding='UTF-8'>, <_io.TextIOWrapper name='bar' mode='r' encoding='UTF-8'>)

要将可能为空的文件列表默认为 stdin,您可以定义一个自定义参数 class,例如:

自定义 Class:

class FilesDefaultToStdin(click.Argument):
    def __init__(self, *args, **kwargs):
        kwargs['nargs'] = -1
        kwargs['type'] = click.File('r')
        super().__init__(*args, **kwargs)

    def full_process_value(self, ctx, value):
        return super().process_value(ctx, value or ('-', ))

将此行为定义为 class 便于重用。

要使用自定义 Class:

@click.command()
@click.argument("logs", cls=FilesDefaultToStdin)
def main(logs):
    ...

这是如何工作的?

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

在这种情况下,我们覆盖 click.Argument.full_process_value()。在我们的 full_process_value() 中,我们寻找一个空的参数列表,如果为空,我们将 - (stdin) 参数添加到列表中。

此外,我们自动分配 nargs=-1type=click.File('r') 参数。