哪个是允许在Python的命令行覆盖配置选项的最佳方法?

and*_*s-h 68 python command-line configuration-files

我有一个Python应用程序,需要很多(~30)配置参数.到目前为止,我使用OptionParser类在应用程序本身中定义默认值,可以在调用应用程序时在命令行中更改单个参数.

现在我想使用'正确'的配置文件,例如来自ConfigParser类.同时,用户仍应能够在命令行中更改单个参数.

我想知道是否有任何方法可以将这两个步骤结合起来,例如使用optparse(或更新的argparse)来处理命令行选项,但是在ConfigParse语法中从配置文件中读取默认值.

任何想法如何轻松地做到这一点?我真的不想手动调用ConfigParse,然后手动将所有optinos的所有默认值设置为适当的值...

Von*_*Von 73

我刚刚发现你可以这样做argparse.ArgumentParser.parse_known_args().首先使用parse_known_args()从命令行解析配置文件,然后使用ConfigParser读取它并设置默认值,然后使用解析其余选项parse_args().这将允许您具有默认值,使用配置文件覆盖它,然后使用命令行选项覆盖它.例如:

默认情况下没有用户输入:

$ ./argparse-partial.py
Option is "default"
Run Code Online (Sandbox Code Playgroud)

默认来自配置文件:

$ cat argparse-partial.config 
[Defaults]
option=Hello world!
$ ./argparse-partial.py -c argparse-partial.config 
Option is "Hello world!"
Run Code Online (Sandbox Code Playgroud)

默认来自配置文件,由命令行覆盖:

$ ./argparse-partial.py -c argparse-partial.config --option override
Option is "override"
Run Code Online (Sandbox Code Playgroud)

argprase-partial.py如下.处理-h正确的帮助有点复杂.

import argparse
import ConfigParser
import sys

def main(argv=None):
    # Do argv default this way, as doing it in the functional
    # declaration sets it at compile time.
    if argv is None:
        argv = sys.argv

    # Parse any conf_file specification
    # We make this parser with add_help=False so that
    # it doesn't parse -h and print help.
    conf_parser = argparse.ArgumentParser(
        description=__doc__, # printed with -h/--help
        # Don't mess with format of description
        formatter_class=argparse.RawDescriptionHelpFormatter,
        # Turn off help, so we print all options in response to -h
        add_help=False
        )
    conf_parser.add_argument("-c", "--conf_file",
                        help="Specify config file", metavar="FILE")
    args, remaining_argv = conf_parser.parse_known_args()

    defaults = { "option":"default" }

    if args.conf_file:
        config = ConfigParser.SafeConfigParser()
        config.read([args.conf_file])
        defaults.update(dict(config.items("Defaults")))

    # Parse rest of arguments
    # Don't suppress add_help here so it will handle -h
    parser = argparse.ArgumentParser(
        # Inherit options from config_parser
        parents=[conf_parser]
        )
    parser.set_defaults(**defaults)
    parser.add_argument("--option")
    args = parser.parse_args(remaining_argv)
    print "Option is \"{}\"".format(args.option)
    return(0)

if __name__ == "__main__":
    sys.exit(main())
Run Code Online (Sandbox Code Playgroud)

  • '耻骨域'让我发笑.我只是一个愚蠢的孩子. (16认同)
  • 我上面已经被要求重复使用上面的代码,因此我将它放入了公共领域. (14认同)
  • 啊!这是非常酷的代码,但是 SafeConfigParser 插入由命令行覆盖的属性*不起作用*。例如,如果您将以下行添加到 argparse-partial.config `another=%(option)s you are残忍`,那么 `another` 总是会解析为 `Hello world you are残忍`,即使 `option` 被覆盖为其他内容在命令行中..argghh-解析器! (2认同)
  • 请注意,set_defaults 仅在参数名称不包含破折号或下划线时才有效。因此,可以选择 --myVar 而不是 --my-var (不幸的是,这相当难看)。要为配置文件启用区分大小写,请在解析文件之前使用 config.optionxform = str,这样 myVar 就不会转换为 myvar。 (2认同)

use*_*965 15

查看ConfigArgParse - 它是一个新的PyPI包(开源),作为argparse的替代品,增加了对配置文件和环境变量的支持.

  • 刚尝试过,机智很棒:)感谢您指出这一点. (2认同)
  • 谢谢 - 看起来不错!该网页还将 ConfigArgParse 与其他选项进行比较,包括 argparse、ConfArgParse、appsettings、argparse_cnfig、yconf、hieropt 和 configurati (2认同)

xub*_*tix 9

我正在使用ConfigParser和带子命令的argparse来处理这些任务.以下代码中的重要一行是:

subp.set_defaults(**dict(conffile.items(subn)))
Run Code Online (Sandbox Code Playgroud)

这会将子命令的默认值(从argparse)设置为配置文件部分中的值.

下面是一个更完整的例子:

####### content of example.cfg:
# [sub1]
# verbosity=10
# gggg=3.5
# [sub2]
# host=localhost

import ConfigParser
import argparse

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser_sub1 = subparsers.add_parser('sub1')
parser_sub1.add_argument('-V','--verbosity', type=int, dest='verbosity')
parser_sub1.add_argument('-G', type=float, dest='gggg')

parser_sub2 = subparsers.add_parser('sub2')
parser_sub2.add_argument('-H','--host', dest='host')

conffile = ConfigParser.SafeConfigParser()
conffile.read('example.cfg')

for subp, subn in ((parser_sub1, "sub1"), (parser_sub2, "sub2")):
    subp.set_defaults(**dict(conffile.items(subn)))

print parser.parse_args(['sub1',])
# Namespace(gggg=3.5, verbosity=10)
print parser.parse_args(['sub1', '-V', '20'])
# Namespace(gggg=3.5, verbosity=20)
print parser.parse_args(['sub1', '-V', '20', '-G','42'])
# Namespace(gggg=42.0, verbosity=20)
print parser.parse_args(['sub2', '-H', 'www.example.com'])
# Namespace(host='www.example.com')
print parser.parse_args(['sub2',])
# Namespace(host='localhost')
Run Code Online (Sandbox Code Playgroud)


Bla*_*rad 5

我不能说这是最好的方法,但我有一个我制作的 OptionParser 类就是这样做的 - 就像 optparse.OptionParser 一样,默认值来自配置文件部分。你可以拥有它...

class OptionParser(optparse.OptionParser):
    def __init__(self, **kwargs):
        import sys
        import os
        config_file = kwargs.pop('config_file',
                                 os.path.splitext(os.path.basename(sys.argv[0]))[0] + '.config')
        self.config_section = kwargs.pop('config_section', 'OPTIONS')

        self.configParser = ConfigParser()
        self.configParser.read(config_file)

        optparse.OptionParser.__init__(self, **kwargs)

    def add_option(self, *args, **kwargs):
        option = optparse.OptionParser.add_option(self, *args, **kwargs)
        name = option.get_opt_string()
        if name.startswith('--'):
            name = name[2:]
            if self.configParser.has_option(self.config_section, name):
                self.set_default(name, self.configParser.get(self.config_section, name))
Run Code Online (Sandbox Code Playgroud)

随意浏览源代码。测试位于同级目录中。