Consider the following MWE named foo.py
:
#!/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))
... and the accompanying foo.ini
:
[DEFAULT]
basedir = /foo/bar
sourcedir = %(basedir)s/baz
[task:1]
sourcedir = /usr
workdir = %(sourcedir)s/src
[task:2]
hdrdir = %(sourcedir)s/include
The script will yield the following when run:
[task:1]
basedir = /foo/bar
sourcedir = /usr
workdir = /usr/src
[task:2]
basedir = /foo/bar
sourcedir = /foo/bar/baz
hdrdir = /foo/bar/baz/include
The desired outcome is to iterate exclusively over the (interpolated) options (via ConfigParser.items()
) local to the given section.
Passing raw=True
to ConfigParser.items()
doesn't affect the list of items other than that the values are not being interpolated (which is anyway not the desired outcome).
Now I could do something like in this modified version of the initial script:
#!/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))
... which yields:
; 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
So it'll work at first glance, but would filter out section-local options which have a value identical to the global one of the same name.
Now this may seem only like a cosmetic issue, but in my case there is a semantic difference, still, between whether the option exists locally inside a section or whether its value was inherited from the default section (think multiple files, multiple inherited options ...). Point being that this is a naïve approach which doesn't work for me.
So the question: how can I get just the options that are really inside a section of an .ini
file, without all those global inherited options?
Is there a way to do this without reaching into the guts of the configparser
module and tampering with non-public methods and stuff?