如何将几个参数列表传递给@ click.option

时间:2017-12-04 11:05:24

标签: python command-line parameters python-click

我想通过命令行使用这种参数调用python脚本(列表可以是任何大小,例如3):

python test.py --option1 ["o11", "o12", "o13"] --option2 ["o21", "o22", "o23"]

使用点击。从文档中,我们无法在任何地方声明我们可以使用列表作为@click.option

的参数

当我尝试这样做时:

#!/usr/bin/env python
import click

@click.command(context_settings=dict(help_option_names=['-h', '--help']))
@click.option('--option', default=[])
def do_stuff(option):

    return

# do stuff
if __name__ == '__main__':
    do_stuff()

在我的test.py中,通过命令行调用它:

python test.py --option ["some option", "some option 2"]

我收到错误:

  

错误:得到意外的额外参数(某些选项2))

我不能真正使用可变参数,因为每个命令只允许1个可变参数(http://click.pocoo.org/5/arguments/#variadic-arguments

因此,如果有人能指出我正确的方向(最好使用点击),我们将非常感激。

5 个答案:

答案 0 :(得分:11)

如果使用自定义选项类将列表格式化为python列表的字符串文字,则可以强制单击以获取多个列表参数:

自定义类:

import click
import ast

class PythonLiteralOption(click.Option):

    def type_cast_value(self, ctx, value):
        try:
            return ast.literal_eval(value)
        except:
            raise click.BadParameter(value)

这个类将使用Python的Abstract Syntax Tree模块将参数解析为python文字。

自定义类使用:

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

@click.option('--option1', cls=PythonLiteralOption, default=[])

这是如何工作的?

这是有效的,因为click是一个设计良好的OO框架。 @click.option()装饰器通常实例化click.Option对象,但允许使用cls参数覆盖此行为。因此,在我们自己的班级继承click.Option并过度使用所需的方法是一件相对容易的事。

在这种情况下,我们过度click.Option.type_cast_value()然后调用ast.literal_eval()来解析列表。

测试代码:

@click.command(context_settings=dict(help_option_names=['-h', '--help']))
@click.option('--option1', cls=PythonLiteralOption, default=[])
@click.option('--option2', cls=PythonLiteralOption, default=[])
def cli(option1, option2):
    click.echo("Option 1, type: {}  value: {}".format(
        type(option1), option1))
    click.echo("Option 2, type: {}  value: {}".format(
        type(option2), option2))

# do stuff
if __name__ == '__main__':
    import shlex
    cli(shlex.split(
        '''--option1 '["o11", "o12", "o13"]' 
        --option2 '["o21", "o22", "o23"]' '''))

测试结果:

Option 1, type: <type 'list'>  value: ['o11', 'o12', 'o13']
Option 2, type: <type 'list'>  value: ['o21', 'o22', 'o23']

答案 1 :(得分:1)

以下内容可能是更容易的黑客修复程序:

#!/usr/bin/env python
import click
import json

@click.command(context_settings=dict(help_option_names=['-h', '--help']))
@click.option('--option', help='Whatever')
def do_stuff(option):
    try:
        option = json.loads(option)    
    except ValueError:
        pass

# do stuff
if __name__ == '__main__':
    do_stuff()

这可以帮助您将'option'用作liststr

答案 2 :(得分:0)

@Murphy的“简单技巧”几乎对我有用,但关键是option将是string,除非您单引号,否则我必须这样做以重新构成列表:

#!/usr/bin/env python
import click
import json

@click.command(context_settings=dict(help_option_names=['-h', '--help']))
@click.option('--option', help='Whatever')
def do_stuff(option):
    try:
        option = json.loads(option)
        # option = str(option)  # this also works
    except ValueError:
        pass

    option = option[1:-1]  # trim '[' and ']'

    options = option.split(',')

    for i, value in enumerate(options):
        # catch integers
        try:
            int(value)
        except ValueError:
            options[i] = value
        else:
            options[i] = int(value)

    # Here use options as you need

# do stuff
if __name__ == '__main__':
    do_stuff()

您可能会发现其他类型的

要使用它,请将列表用引号引起来:

python test.py --option "[o11, o12, o13]"

或者您可以通过不留空格来避免引号:

python test.py --option [o11,o12,o13]

答案 3 :(得分:0)

如果您不坚持传递类似列表的内容,而只是想传递多个可变参数,则可以使用created-using: SDK选项。

click documentation

multiple
@click.command()
@click.option('--message', '-m', multiple=True)
def commit(message):
    click.echo('\n'.join(message))

答案 4 :(得分:0)

在处理Kubernetes争论时,Stephen Rauch的回答给了我一些问题。这是因为字符串格式设置很麻烦,而且经常会出现新行,因此在数组之前和之后都添加了单引号或空格。因此,我制作了自己的解析器来改善此行为。 该代码应具有自我解释性。
请注意,此代码在变量本身中不支持单引号'"

自定义类别:

class ConvertStrToList(click.Option):
    def type_cast_value(self, ctx, value) -> List:
        try:
            value = str(value)
            assert value.count('[') == 1 and value.count(']') == 1
            list_as_str = value.replace('"', "'").split('[')[1].split(']')[0]
            list_of_items = [item.strip().strip("'") for item in list_as_str.split(',')]
            return list_of_items
        except Exception:
            raise click.BadParameter(value)

自定义班级用法:

@click.option('--option1', cls=PythonLiteralOption, default=[])