I'm using configparser
to read configuration for a backup-system.
one of the options (the output path) often includes the section-name itself.
[DEFAULT]
compression=gzip
[foo]
output=/Backups/foo/
[pizza]
output=/BigDisk/Backups/
[bar]
output=/Backups/bar/
Now I would like to get rid manually appending the section name, and instead would like to use string interpolation. Something like the following:
[DEFAULT]
compression=gzip
output=/Backups/%(SECTION)s/
[foo]
[pizza]
output=/BigDisk/Backups/
[bar]
Unfortunately i haven't found the variable-name that would expand to the section name yet (it's obviously not SECTION
).
Is it possible at all (ideally without having to build my own ConfigParser sub-class)?
An ugly workaround would be to add an option append_section_name
(and set it to yes
in the DEFAULT
section and to no
in the pizza
section) and manually do the interpolation. I'd rather not...
I had a need to do the same thing and I found that it's quite easy to do in Python 3.x (tested on Python 3.4):
- Make a subclass of the
ExtendedInterpolation
class (MyExtendedInterpolation
) and override the before_get()
method. First, create a new value doing your custom substitution. I found it easiest to use the string Template
class and a call to the safe_substitute()
method. Next, return the result of the call to the superclass before_get()
method.
- Create your
ConfigParser
object using your custom interpolation class.
- Use the
ConfigParser
object as normal.
Hope it helps.
Working code below:
#!/usr/bin/env python3
from configparser import ConfigParser, ExtendedInterpolation
from string import Template
class MyExtendedInterpolation(ExtendedInterpolation):
def before_get(self, parser, section, option, value, defaults):
value = Template(value).safe_substitute({'SECTION': section})
return super().before_get(parser, section, option, value, defaults)
if __name__ == '__main__':
cp = ConfigParser(interpolation=MyExtendedInterpolation())
cp.read_string("""
[DEFAULT]
compression=gzip
output=/Backups/${SECTION}/
[foo]
[pizza]
output=/BigDisk/Backups/
[bar]
""")
for section in cp.sections():
print('[{!s}]'.format(section))
for option in cp[section]:
print(' {!s} = {!s}'.format(option, cp.get(section, option)))
Script output below. Notice how the output
option value includes the section name for the [foo]
and [bar]
sections.
[foo]
compression = gzip
output = /Backups/foo/
[pizza]
output = /BigDisk/Backups/
compression = gzip
[bar]
compression = gzip
output = /Backups/bar/
Relevant references:
(configparser module) 14.2.5. Interpolation of values
(string module) 6.1.4. Template strings
The idea of user @andy20170414 is good but has an unexpected behavior of breaking the escape procedure.
He replaces the original string using that Template()
and that will escape the all th $
. When the Interpolation receives the text to interpolate, it will receive only one $
(already escaped) and will give you error during parsing.
I think that the correct way to accomplish what op wants is as the @andy20170414 answer with this modification:
class MyExtendedInterpolation(ExtendedInterpolation):
def before_get(self, parser, section, option, value, defaults):
defaults.maps.append({'section': section})
return super().before_get(parser, section, option, value, defaults)
Keep in mind that the keys are compared in a .tolower()
manner, as such keep that section
key in lower case.