Python argparse需要配置文件/ dict中的参数

问题描述 投票:0回答:2

我的脚本有许多配置命名参数。我想从配置文件和/或命令行提供它们。

  • 一些论点是必需的/强制性的
  • 默认情况下,这些是从配置文件中读取的
  • (可选)使用命令行中的值覆盖它们
  • 必须在未提供参数时报告错误(配置文件或命令行)

之前一直在使用set_defaults(),但这在使用add_arguments(..., required=True)时会引发错误。

Python 2.7有更好的工作解决方案吗?

from argparse import ArgumentParser

# this dict will be read from configuration file
config_args = {'my_argument': 'value from configuration file'}

parser = ArgumentParser()
parser.add_argument('--my-argument', dest='my_argument', required=True)

# use values from configuration file by default
parser.set_defaults(**config_args)

# override with command line arguments when provided
args = parser.parse_args({})

此示例将导致错误:

usage: [-h] --required REQUIRED
: error: argument --required is required
python python-2.7 argparse
2个回答
1
投票
In [355]: config_args = {'my_argument': 'value from configuration file'}
In [356]: parser = argparse.ArgumentParser()

add_argument创建并返回一个Action对象。让我们保存对该对象的引用,并查看它:

In [357]: a1 = parser.add_argument('--my-argument', dest='my_argument', required
     ...: =True)
In [358]: a1
Out[358]: _StoreAction(option_strings=['--my-argument'], dest='my_argument', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)

请注意,它的defaultNonestore的默认默认值。 required也将被设置为True

set_defaults改变了a1.default值:

In [359]: parser.set_defaults(**config_args)
In [360]: a1
Out[360]: _StoreAction(option_strings=['--my-argument'], dest='my_argument', nargs=None, const=None, default='value from configuration file', type=None, choices=None, help=None, metavar=None)

set_defaults也可以用来设置一个没有出现在参数中的dest的值(在subparsers的文档中有说明)。

在没有参数的情况下运行,我们会收到错误 - 因为操作是必需的。默认值的存在不会覆盖它。

In [361]: parser.parse_args([])
usage: ipython3 [-h] --my-argument MY_ARGUMENT
ipython3: error: the following arguments are required: --my-argument
An exception has occurred, use %tb to see the full traceback.

SystemExit: 2

如果我们将required属性更改为Falsea1是一个对象,其属性可以在限制范围内更改。包括a1.default)。

In [362]: a1.required
Out[362]: True
In [363]: a1.required=False
In [364]: parser.parse_args([])
Out[364]: Namespace(my_argument='value from configuration file')

您的配置值现在出现。命令行值可以覆盖它。

您还可以在namespace参数中提供默认值:

In [365]: ns = argparse.Namespace(my_argument='foo')
In [366]: parser.parse_args([], namespace=ns)
Out[366]: Namespace(my_argument='foo')

foo值具有优先级,因此不使用操作默认值或set_defaults值。

https://bugs.python.org/issue29670 argparse: does not respect required args pre-populated into namespace

在此错误/问题中,用户希望ns中存在值以覆盖required测试。也就是说,他希望这个ns表现得像在命令行上提供了价值一样。我的结论是改变并不是一件容易的事。目前的parse_args结构不允许我们修改或绕过像required这样的测试。

如果你想要更好的测试,我建议你离开default=None,并在解析后进行自己的测试。

if args.my_argument is None:
    args.my_argument = 'default from config'

1
投票

基于@hpaulj的答案,对代码进行了微调。使用ArgumentParser通知命令行或配置文件提供的缺少参数。

Python27 ArgumentParser不提供用于访问添加的参数操作的公共API。但有私人财产_actions。因此,如果从配置文件中提供,只需循环遍历parser._actions并重置required属性就足以解决我的用例问题。

这是固定代码:

from argparse import ArgumentParser

# this dict will be read from configuration file
config_args = {'my_argument': 'value from configuration file'}

parser = ArgumentParser()
arg = parser.add_argument('--my-argument', required=True)

# use values from configuration file by default
parser.set_defaults(**config_args)

# Reset `required` attribute when provided from config file
for action in parser._actions:
    if action.dest in config_args:
        action.required = False

# override with command line arguments when provided
args = parser.parse_args({})
© www.soinside.com 2019 - 2024. All rights reserved.