在 ConfigParser 中如何只获取部分局部选项?

In ConfigParser how to get only section-local options?

考虑以下名为 foo.py 的 MWE:

#!/usr/bin/env python3
from configparser import ConfigParser

if __name__ == "__main__":
    cfg = ConfigParser()
    with open("foo.ini") as ini:
        cfg.read_file(ini)
    for section in cfg.sections():
        print("[%s]" % (section))
        for option, value in cfg.items(section):
            print("%s = %s" % (option, value))

... 以及随附的 foo.ini:

[DEFAULT]
basedir = /foo/bar
sourcedir = %(basedir)s/baz

[task:1]
sourcedir = /usr
workdir = %(sourcedir)s/src

[task:2]
hdrdir = %(sourcedir)s/include

脚本将在 运行 时产生以下内容:

[task:1]
basedir = /foo/bar
sourcedir = /usr
workdir = /usr/src
[task:2]
basedir = /foo/bar
sourcedir = /foo/bar/baz
hdrdir = /foo/bar/baz/include

期望的结果是专门迭代(插值)选项(通过ConfigParser.items()给定部分的本地

raw=True 传递给 ConfigParser.items() 不会影响项目列表,只是值 不是 被插值(无论如何不是期望的结果)。

现在我可以在初始脚本的这个修改版本中做类似的事情:

#!/usr/bin/env python3
from configparser import ConfigParser

if __name__ == "__main__":
    cfg = ConfigParser()
    with open("foo.ini") as ini:
        cfg.read_file(ini)
    print("; sections: %s" % (cfg.sections()))
    print("; defaults: %s" % (cfg.defaults()))
    for section in cfg.sections():
        print("[%s]" % (section))
        print("; options = %s" % (cfg.options(section)))
        for option, value in cfg.items(section):
            if option in cfg.defaults():
                # can't use cfg.defaults()[option] ... that's the raw value
                if cfg[cfg.default_section][option] == value:
                    continue
            print("%s = %s" % (option, value))

... 产生:

; sections: ['task:1', 'task:2']
; defaults: OrderedDict([('basedir', '/foo/bar'), ('sourcedir', '%(basedir)s/baz')])
[task:1]
; options = ['sourcedir', 'workdir', 'basedir']
sourcedir = /usr
workdir = /usr/src
[task:2]
; options = ['hdrdir', 'basedir', 'sourcedir']
hdrdir = /foo/bar/baz/include

乍一看它可以工作,但会过滤掉与全局同名选项具有相同值的部分局部选项。

现在这看起来可能只是一个表面问题,但在我的例子中,仍然存在语义差异,选项是否存在于本地部分内部,或者它的值是否从默认部分继承(考虑多个文件, 多个继承选项 ...)。重点是这是一种天真的方法,对我不起作用。

所以问题是: 我怎样才能 .ini 文件的一部分中的选项,没有所有这些全局继承选项? 有没有办法在不深入 configparser 模块的内部并篡改非 public 方法和东西的情况下做到这一点?

检查 code of ConfigParser 我没有看到 public API 可以这样做。事实上,ConfigParser 中的任何方法都使用默认值扩展了给定的部分。

我看到的唯一直接的方法是更改​​配置中的默认部分 ConfigParser constructor 中的default_section 参数):

if __name__ == "__main__":
    cfg = ConfigParser(default_section=None)
# (...)

然而,副作用是自动 "default" 机制将不再适用于此实例化 ConfigParser。但是你仍然可以自己做回退机制:

cfg = ConfigParser(default_section=None)
cfg.get(mysection, myoption, fallback=cfg.get('DEFAULT', myoption))

此外,值的插值在 DEFAULT 部分不起作用。但是你可以使用 ExtendedInterpolation.

完整示例:

# test.py
from configparser import ConfigParser, DEFAULTSECT, ExtendedInterpolation

if __name__ == '__main__':
    cfg = ConfigParser(default_section=None, interpolation=ExtendedInterpolation())

    with open('test.ini') as ini:
        cfg.read_file(ini)

    for section in cfg.sections():
        if section == DEFAULTSECT:
            continue

        print('[%s]' % (section))
        for key, value in cfg.items(section):
            print('%s = %s' % (key, value))
# test.ini
[DEFAULT]
foo=bar

[section]
key="value ${DEFAULT:foo}"

编辑:

    如果
  • 重命名 DEFAULT 部分的目的是配置默认值,则这不是一个好主意。我勾选这个选项。
  • default_section 设置为 None 而不是 "whatever"
  • 添加完整的代码示例
  • 添加有关 ExtendedInterpolation 的详细信息