通过argparse python从cfg文件动态设置默认值

时间:2016-02-02 00:28:00

标签: python python-2.7 argparse

我正在尝试使用argparse模块。我正在处理2个配置文件:
1.default cfg文件2.用户提供的文件。

如果在运行时没有提供文件,请从默认文件中读取。如果用户提供文件,请使用提供的文件的内容。

我的问题是:我想为每个default提供parser.add_agument字段,因此如果用户未提供该字段,则它将始终具有一些默认值。可以从文件中的任一个(即默认或用户给定文件)读取这些值。我需要检查提供的文件,然后from default/user-given-file import *

我的代码:
get_args.py

class GetArgs:
   def get_args(self):
       parser = argparse.ArgumentParser(description='foo')
       #
       parser.add_argument(
        '-c', '--config', type=str, help='Provide a file', 
                          required=False, default='settings.cfg')
         #now note that below "default=version" will be from either "settings.cfg" or "user given file"

         # here may be i need to import from file ? 
        parser.add_argument(
        '-v','--version', type=str, help='API version', required=False, default=version)
        args = parser.parse_args()

        # Assign args to variables
        config=args.config
        version = args.version
        retutn config,version

读取cfg文件的代码: read_file.py

import ConfigParser
configParser = ConfigParser.RawConfigParser()
configParser.read(file)
try:
    version= configParser.get('Set', 'version')
    ....
    ....
except ConfigParser.NoOptionError:
    print "Option not found in configuration file!"
    sys.exit(1)

类似的东西:

1.python get_args.py                                      #reads "version" from settings.cfg
2.python get_args.py --config /path/to/file/file.cfg      #reads "version" file.cfg   

当我不提供--config时,它完美无缺。

这是否可以动态读取?

1 个答案:

答案 0 :(得分:5)

动态填充解析器

今天的另一个问题得到的答案显示了如何从字典中的值填充解析器:https://stackoverflow.com/a/35132772/901925

Ipython做了类似的事情。它读取配置文件(默认以及用户配置文件),并使用配置值来流行解析器。这为用户提供了几个级别的控制 - 默认配置,配置文件配置,最后是命令行。但是Ipython代码相当复杂。

编辑参数值

另一个有用的观点是,每个参数或Action对象在创建后都可用,并且可以(在限制范围内)进行修改。

arg1 = parser.add_argument('--foo', default='test', ...)
# arg1 is an Action object with the various parameters
print arg1     # a display of some attributes
print arg1.default    # the value of the default as set above
arg1.default = 'different'  # assign a new value

因此可以在解析之前动态设置默认值。

前缀字符文件

另一个有用的工具是https://docs.python.org/3/library/argparse.html#fromfile-prefix-chars

parser = argparse.ArgumentParser(fromfile_prefix_chars='@')

允许我指定以该字符开头的文件的名称。它将读取文件内容并将字符串拼接到sys.argv列表中,就像我键入它一样。

解析后填写默认值

但是,如果要在命令行中指定配置文件,然后使用其内容为解析器设置默认值,事情会变得更复杂。

解析args之后将是一个名称空间对象,其中包含配置文件名以及它解析的所有其他参数。

您可以使用args或字典格式print args查看vars(args)的确切内容。没有关于使用解析器默认值设置哪些值以及由命令行设置的隐藏信息。

默认情况下最干净的解析后测试是

if args.foo is None:
   # this has the default default

如果值为None,则它只能来自默认值,而不是命令行(没有转换为None的字符串)。

所以我可以想象编写一个函数来迭代vars(args)的密钥,测试None,并用config中的相应值替换它。 (你需要帮助吗?)。

如果configargs都被转换为兼容字典,那么您可以使用字典update方法将值从一个转移到另一个。

如果参数未出现在命令行中,则可以使用

default=argparse.SUPPRESS将参数保留在命名空间之外。 https://docs.python.org/3/library/argparse.html#default。这可以与字典更新一起使用,例如

config_dict.update(vars(args))

只会更改config_dict以获取命令行中给出的参数。

两阶段解析器

另一个想法是使用两阶段解析。定义一个具有config_file参数的解析器,而不是(或很少)其他解析器。使用parse_known_args运行它以获取config_file值。读取配置文件并使用它来填充第二阶段解析器(或至少修改其默认值)。现在运行第二阶段解析器以获取完整的命名空间(我只是忽略此args中的任何config_file值。)

ChainMap

https://docs.python.org/3/library/collections.html#chainmap-examples-and-recipes

新的collections类,ChainMap有一个从命名空间,env,默认值等查找链式值的示例。

namespace = parser.parse_args() 
command_line_args = {k:v for k, v in vars(namespace).items() if v} 
combined = ChainMap(command_line_args, os.environ, defaults) 
print(combined['color'])

Setting options from environment variables when using argparse

的其他好主意