如果默认或给定值,则 ArgParse 参数错误无

Posted

技术标签:

【中文标题】如果默认或给定值,则 ArgParse 参数错误无【英文标题】:ArgParse argument error if default or given value None 【发布时间】:2020-03-15 01:00:45 【问题描述】:

在为我的程序解析参数时,如果未指定为程序的参数,我还希望能够从环境中获取某些参数。

我目前有:

parser = argparse.ArgumentParser(
    description="Foo Bar Baz")
parser.add_argument('--metadata-api-user', type=str,
                    help='Username for basic authentication access to the metadata API. If not given, will be extracted from env variable '.format("API_USER"),
                    default=os.environ.get("API_USER"))
parser.add_argument('--metadata-api-password', type=str,
                    help='Password for basic authentication access to the metadata API. If not given, will be extracted from env variable '.format("API_PASS"),
                    default=os.environ.get("API_PASS"))
parser.add_argument('--version', type=int, help='Version number', default=1)
parser.add_argument('--location', type=str, help='Location to place thing (default: ./)',default="./")

args = parser.parse_args(args)

这提供了我想要的功能,但是如果没有给出环境变量并且它们没有在命令行中给出,我希望 argparse 引发 ArgumentError。

environ[keyname] 如果只在命令行中指定而不在 env 变量中指定,则在创建参数时会引发 keyerror,这不是很好。

在创建参数时,像 'allow-none'=false 这样的参数会很棒,但如果有人知道另一种解决方案,那就太棒了。

【问题讨论】:

我认为自定义操作可能会对您有所帮助。 docs.python.org/3/library/argparse.html#action @serbia99 我尝试为它实现一个自定义操作,但不幸的是,如果使用默认值,则不会调用该操作,并且不使用默认值的唯一方法是如果一个值被指定,这意味着用户无论如何都必须给出一个值或得到一个错误。无论如何都欢呼! 后解析是要走的路,正如您所注意到的,action.__call__ 仅在用户提供参数时运行。在默认处理中没有任何东西会触发 ArgumentError,尤其是不会触发自定义消息。 【参考方案1】:

default 将简单地返回给定的值,如果用户不提供,但它有一个特殊的情况 None 和字符串。

None 传递给default 将短路并返回None

将字符串传递给default 将返回使用type 构造函数解析的字符串。

我实现了一个名为not_none 的自定义参数化类型,它将尝试使用给定的类型构造函数解析字符串,或者如果字符串为空,则会大声失败。

import argparse
from os import environ
from typing import Callable, TypeVar
T = TypeVar('T')

def not_none(type_constructor: Callable[[str], T] = str):
    """Convert a string to a type T."""
    def parse(s: str) -> T:
        """argparse will call this function if `s` is an empty string, but not when `s` is None."""
        if not s:
            raise ValueError
        else:
            return type_constructor(s)
    return parse

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--foo', help="A foo...", type=not_none(int), default='1')
    parser.add_argument('--bar', help="A bar...", type=not_none(), default=environ.get('BAR', ''))
    # args = parser.parse_args(['--foo', '1'])
    args = parser.parse_args(['--bar', 'BAR'])
    print(args.foo, args.bar)

【讨论】:

【参考方案2】:

很遗憾,我找不到解决方案,所以我不得不为可以从 env 变量中获取的每个参数添加以下内容:

api_user_action = parser.add_argument('--metadata-api-user', type=str,
                    help='Username for basic authentication access to the metadata API. If not given, will be extracted from env variable '.format("API_USER"),
                    default=os.environ.get("API_USER"))

...

if args.metadata_api_user is None:
    raise argparse.ArgumentError(api_user_action,
                                 "API Username was not supplied in either command line or environment variables. \n" + parser.format_help())

这会打印出用法和相应的错误消息(如果两者均未提供),但需要从可以应用的每个参数中重复。

【讨论】:

您可以将actions 收集到一个列表中,并在解析后迭代该列表。那么测试可以写成:if getattr(args,action.dest) is None: ... 您甚至可以将 environ.get 留到这个阶段 - 除非您想在 help 行中显示它。 @hpaulj 这听起来确实是一种更好的方法,谢谢:)【参考方案3】:

为什么在构建参数时不检查环境变量?

if "API_USER" in os.environ:
    parser.add_argument('--metadata-api-user', type=str, default=os.environ.get("API_USER"))
else:
    parser.add_argument('--metadata-api-user', type=str)

这似乎是获得所需结果的最简单方法。

【讨论】:

以上是关于如果默认或给定值,则 ArgParse 参数错误无的主要内容,如果未能解决你的问题,请参考以下文章

ArgParse Python 模块:更改继承参数的默认参数值

打印所有 argparse 参数,包括默认值

argparse详细使用手册

argparse详细使用手册

argparse详细使用手册

如果只给出一个参数,则不同的 argparse 规则