【问题标题】:How can I use click to parse a string for arguments?如何使用 click 解析参数的字符串?
【发布时间】:2020-05-17 02:35:11
【问题描述】:

假设我有一个包含参数和选项的字符串列表,使用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,似乎在 parse_args 类集内有一个 parse_args 函数,但在文档中没有看到关于我如何做到这一点的任何内容。我试过实例化BaseCommandCommand,但不确定如何在没有正确上下文的情况下让parse_args 工作。

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

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')

【问题讨论】:

  • 但是,一个重要的点击点不是构建参数定义的多情 blob,而是使用它们处理的参数注释函数。所以在点击的背景下,我认为你的问题没有任何意义。您要处理的用例是什么?在那个用例中,为什么要使用 click 而不是 argparse,因为您似乎已经对此感到满意了?
  • 您对extra_params 所做的是模拟通常从命令行读取的输入(通过sys.argv[1:])。这最常用于测试(我在 SO 答案中经常使用它)。 parsed_object 或其等价物也可以直接构造。
  • @StephenRauch 用例是我有一个使用许多参数/选项的主程序。点击在这里完美运行。然而,这个主程序是一个可重复使用的启动器,其他人用作他们自己程序的脚手架。这些子进程可以接受未知数量的参数/选项,因此需要一种将未知参数/选项传递给该子进程的方法,完成here,然后让子进程解析这些参数。让我用这个上下文更新这个问题。

标签: python argparse python-click


【解决方案1】:

试试这样的:

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

【讨论】:

    【解决方案2】:

    查看 cmets 和我随后的编辑,让我认为简单地将 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)
    
    @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 的文档以调用其他命令 click.palletsprojects.com/en/7.x/advanced/…
    • @afterburner 感谢您的指点。这里的一个问题是 ctx.invoke 需要解析参数字符串以指定关键字 args,这需要先验地了解子函数的参数(这在此处是不可能的)。然而 ctx.forward 可以解析来自调用者的参数/选项并将它们作为 kwargs 传递给接收者。在上面的示例中,uri 是 child_function 所需的 kwarg,unprocessed_args 也是如此。然后,这让子函数的任务是解析 unprocessed_args,这让我们回到第 1 步,解析一个可迭代的字符串。
    • 另外@afterburner 你有一个示例 sn-p 解决这个问题,调用函数对子函数的参数一无所知?也很想了解答案中引用的方法可能会发生什么意外行为。
    • 我添加了一个快速评论,如果有帮助请告诉我
    【解决方案3】:

    对于不知道子函数参数的调用函数,你可以试试这个:

    @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>
    

    【讨论】:

      猜你喜欢
      • 2012-11-04
      • 2010-12-20
      • 2016-07-30
      • 2019-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-24
      • 2015-01-22
      相关资源
      最近更新 更多