在插值中使用先前配置文件中的值

时间:2017-01-18 13:29:25

标签: python configuration-files string-interpolation

我正在使用一个程序,该程序使用python' configparser.ConfigParser来处理配置文件作为构建事物的指令。它的设置使得可以指定多个命令行文件参数,并且以后文件中的规范将覆盖先前文件中设置的内容。

这意味着我可以在trivial.ini文件中设置基本模型

[admin]
basename = trivial_model
[model basic]
data = data.tsv

并使用更复杂的模型extension.ini

进行扩展
[admin]
basename = trivial_model_extended
[model basic]
model = bsvs

program trivial.ini extension.ini的行为就像它有

一样
[admin]
basename = trivial_model_extended
[model basic]
data = data.tsv
model = bsvs

现在编写一个更加模块化的东西会很好,因为它能够组合多个这样的扩展而不需要过多地关注细节,给每个扩展名提供不同的基本文件名。我想也许这可能有用

[admin]
basename = %(basename)s_extended
[model basic]
model = bsvs

但是对于当前的实现,我得到了configparser.InterpolationDepthError: Recursion limit exceeded in value substitution: option 'basename' in section 'admin' contains an interpolation key which cannot be substituted in 10 steps. Raw value: '%(basename)s_extended'

是否有一种简单,内置或优雅的方式来启用此类增量规范,或者通过更改当前的实现(这似乎归结为

parser.add_argument(
    "config",
    nargs="+")

args = parser.parse_args()
c = configparser.ConfigParser()
for conf in args.config:
    c.read(conf)

)或通过配置文件中的一些聪明的[default]部分或值(或两者都必要)?

2 个答案:

答案 0 :(得分:1)

一些评论:

  • 您无法像上面那样递归basename定义。我的方法是使用除[DEFAULT]之外的其他内容basename部分,例如,trivial.ini可能如下所示:

    [DEFAULT]
    basename_default = default from trivial.ini
    
    [admin]
    basename = trivial_model
    
    [model basic]
    data = data.tsv
    
  • 请注意,[DEFAULT]部分必须全部为大写

  • 接下来,我可能会有一个附加的.ini文件,我称之为more.ini,它看起来像这样:

    [admin]
    basename = %(basename_default)s and more
    
  • 此外,您不需要循环来读取配置文件:只需为read()方法提供一个文件名列表,其中后一个文件将覆盖第一个文件。

把它放在一起:

parser = argparse.ArgumentParser()
parser.add_argument("config", nargs="+")
args = parser.parse_args('trivial.ini extension.ini more.ini'.split())

cfg = ConfigParser.ConfigParser()
cfg.read(args.config)

admin = 'admin'
model_basic = 'model basic'

print('basename:', cfg.get(admin, 'basename'))
print('defaults:', cfg.defaults())

输出:

basename: default from trivial.ini and more
defaults: OrderedDict([('basename_default', 'default from trivial.ini')])

答案 1 :(得分:0)

get - 时间

的评估

您正在寻找的方式的递归插值不可能是开箱即用的,因为插值仅在调用ConfigParser.get(section, option)时起作用,而不是在读取配置文件时起作用。来自BasicInterpolation的文档字符串:

[...]  All reference expansions are done late, on demand. [...]

我们如何解决这个问题?

_read的{​​{1}}方法会调用ConfigParser,但到那时它已经覆盖了self._interpolation.before_read对象的旧内部值,所以即使

ConfigParser

不会自行完成这个技巧

您还必须重载class BasicReadInterpolation (configparser.BasicInterpolation): def before_read(self, parser, section, option, value): L = [] import pdb pdb.set_trace() interpolations = parser[parser.default_section] interpolations.update(parser[section]) self._interpolate_some( parser, option, L, value, section, interpolations, 1) return ''.join(L) ,其中包含大部分解析魔法的方法,ConfigParser._read,其中当前包含对ConfigParser._join_multiline_values的调用。 (对我来说,这是一个愚蠢的地方。)