在Python的命令行中,允许configuration选项被覆盖的最佳方法是什么?

我有一个Python应用程序需要相当多的(〜30)configuration参数。 到目前为止,我使用OptionParser类来定义应用程序本身的默认值,并可以在调用应用程序时在命令行中更改各个参数。

现在我想使用“正确的”configuration文件,例如从ConfigParser类。 同时,用户仍应该能够在命令行上更改单个参数。

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

任何想法如何以简单的方式做到这一点? 我真的不喜欢手动调用ConfigParse,然后手动设置所有默认的所有optinos到适当的值…

任何帮助是极大的赞赏 :)

我刚刚发现你可以用argparse.ArgumentParser.parse_known_args()来做到这一点。 首先使用parse_known_args()parsingconfiguration文件,然后使用ConfigParser读取并设置默认值,然后使用parse_args()parsing其余选项。 这将允许你有一个默认值,用configuration文件覆盖它,然后用命令行选项覆盖它。 例如:

默认没有用户input:

 $ ./argparse-partial.py Option is "default" 

默认来自configuration文件:

 $ cat argparse-partial.config [Defaults] option=Hello world! $ ./argparse-partial.py -c argparse-partial.config Option is "Hello world!" 

configuration文件中的缺省值,被命令行覆盖:

 $ ./argparse-partial.py -c argparse-partial.config --option override Option is "override" 

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

我使用ConfigParser和argparse与子命令来处理这样的任务。 下面代码中的重要行是:

 subp.set_defaults(**dict(conffile.items(subn))) 

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

更完整的例子如下:

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

查看ConfigArgParse – 它是一个新的PyPI包( 开放源代码 ),作为argparse的替代品,增加了对configuration文件和环境variables的支持。

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

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

随意浏览源代码 。 testing在兄弟目录中。

尝试这种方式

 # encoding: utf-8 import imp import argparse class LoadConfigAction(argparse._StoreAction): NIL = object() def __init__(self, option_strings, dest, **kwargs): super(self.__class__, self).__init__(option_strings, dest) self.help = "Load configuration from file" def __call__(self, parser, namespace, values, option_string=None): super(LoadConfigAction, self).__call__(parser, namespace, values, option_string) config = imp.load_source('config', values) for key in (set(map(lambda x: x.dest, parser._actions)) & set(dir(config))): setattr(namespace, key, getattr(config, key)) 

用它:

 parser.add_argument("-C", "--config", action=LoadConfigAction) parser.add_argument("-H", "--host", dest="host") 

并创build示例configuration:

 # Example config: /etc/myservice.conf import os host = os.getenv("HOST_NAME", "localhost")