如何使用 click 来解析参数字符串?

How can I use click to parse a string for arguments?

假设我有一个包含参数和选项的字符串列表,使用 argparse,我可以使用 parse_args 函数将此列表解析为一个对象,如下所示:

import argparse

extra_params = [‘—sum’, ‘7’, ‘-1’, ‘42’]

parser=argparse.ArgumentParser(description=“argparse docs example”)
parser.add_argument(‘integers’, metavar=‘N’, type=int, nargs=‘+’,
                    help=‘an integer for the accumulator’)
parser.add_argument(‘—sum’, dest=‘accumulate’, action=‘store_const’,
                    const=sum, default=max,
                    help=‘sum the integers (default: find the max)’)
parsed_object=parser.parse_args(extra_params)

在这里,argparse 已经解析了提供的字符串迭代。是否可以使用 click 来解析提供的字符串迭代?

我在 API 文档中搜索了 click,似乎在 类 的 *Command 集中有一个 parse_args 函数,但是在文档中看不到有关我如何执行此操作的任何内容。我已经尝试实例化 BaseCommand 以及 Command,但不确定如何在没有正确上下文的情况下让 parse_args 工作。

对于更广泛的上下文,这个问题是由于构建了一个最终用户用作脚手架来启动他们自己的应用程序的启动器应用程序的结果。在这里,启动器使用了一些参数,点击装饰器可以完美地工作。可以按照文档 here 中所示处理未知参数。然后,此启动器调用最终用户提供的可调用这些未解析的参数。 Click 将未解析的参数保留为字符串元组。在这种情况下,最终用户如何能够使用 Click 来解析他们感兴趣的参数?这里有一个片段来说明这个问题:

import click
from typing import Tuple

@click.command(name="TestLauncher", context_settings={
  "ignore_unknown_options": True
})
@click.option('--uri', '-u',
  help="URI for the server")
@click.argument('unprocessed_args', nargs=-1,
  type=click.UNPROCESSED)
def main(uri: str, unprocessed_args: Tuple[str, ...]) -> None:
    print(f"Was passed a URI of {uri}")
    print(f"Additional args are {unprocessed_args}")

    child_function(unprocessed_args)

def child_function(unprocessed_args: Tuple[str, ...]) -> None:
    # How do I get Click to parse the provided args for me?
    pass

if __name__ == "__main__":
    # pylint: disable=no-value-for-parameter, unexpected-keyword-arg
    main()

运行 来自命令行:

python3 so_test.py --uri test.com --prog-arg 10
Was passed a URI of test.com
Additional args are ('--prog-arg', '10')

尝试这样的事情:

import click

@click.command()
@click.option('--count', default=1, help='number of greetings')
@click.option('--test', default='test_was_not_provided', help='test option')
@click.argument('name')
def hello(*args, **kwargs):
    click.echo(f"Hello World! {kwargs['name']} {kwargs['count']}")


if __name__ == '__main__':
    hello()

运行 类似:python main.py haha --test this_is_a_test --count=40

查看评论和我随后的编辑,让我认为简单地将点击装饰器应用于子函数可能会起作用。好像确实是,但我不完全知道为什么。

import click
from typing import Tuple

@click.command(name="TestLauncher", context_settings={
  "ignore_unknown_options": True
})
@click.option('--uri', '-u',
  help="URI for the server")
@click.argument('unprocessed_args', nargs=-1,
  type=click.UNPROCESSED)
def main(uri: str, unprocessed_args: Tuple[str, ...]) -> None:
    print(f"Was passed a URI of {uri}")
    print(f"Additional args are {unprocessed_args}")

    child_function(unprocessed_args)

@click.command()
@click.option('--prog-arg')
def child_function(prog_arg: str) -> None:
    # How do I get Click to parse the provided args for me?
    print(f"Child function passed: {prog_arg}")

if __name__ == "__main__":
    # pylint: disable=no-value-for-parameter, unexpected-keyword-arg
    main()
python3 so_test.py --uri test.com --prog-arg 10
Was passed a URI of test.com
Additional args are ('--prog-arg', '10')
Child function passed: 10

如果调用函数不知道子函数的参数,可以试试这个:

@click.command(name="TestLauncher", context_settings={
    "ignore_unknown_options": True
})
@click.option('--uri', '-u',
              help="URI for the server")
@click.argument('unprocessed_args', nargs=-1,
                type=click.UNPROCESSED)
def main(uri: str, unprocessed_args: Tuple[str, ...]) -> None:
    print(f"Was passed a URI of {uri}")
    print(f"Additional args are {unprocessed_args}")
    unprocessed_args = dict([(unprocessed_args[i].replace('--', '').replace('-', '_'), unprocessed_args[i+1]) for i in range(0, len(unprocessed_args), 2)])
    click.get_current_context().invoke(child_function, **unprocessed_args)

@click.command(context_settings={"ignore_unknown_options": True})
@click.option('-p', '--prog-arg')
def child_function(prog_arg: str, **kwargs) -> None:
    # How do I get Click to parse the provided args for me?
    print(f"Child function passed: {prog_arg}")
    # all remaining unknown options are in **kwargs

if __name__ == "__main__":
    # pylint: disable=no-value-for-parameter, unexpected-keyword-arg
    main()

但是,请注意:

unprocessed_args = dict([(unprocessed_args[i].replace('--', '').replace('-', '_'), unprocessed_args[i+1]) for i in range(0, len(unprocessed_args), 2)])

这里假设每个选项只能有一个值。另一种方法是通过传递如下选项来调用您的脚本,在 = 上拆分字符串并执行您认为必要的任何预格式化。

--prog-arg=<Your-desired-values>