I have searched stackoverflow but can't seem to find an answer similar to mine.
Most people want to parse duplicate keys, however I want to write them. (I'd prefer to do this using async file writes but that's another story)
Specifically for systemd-networkd config. They use the ini format, but allow duplicate keys. For example
[Match]
Name=eth0
[Network]
Address=10.0.0.12/24
Gateway=10.0.0.1
DNS=1.1.1.1
DNS=8.8.8.8
I want to be able to write this like so:
conf = ConfigParser()
conf.optionxform = str
conf["Match"] = {"Name": "eth0"}
conf["Network"] = {
"DHCP": "no",
"Address": "10.0.0.12/24",
"Gateway": "10.0.0.1",
"Dns": ["1.1.1.1", "8.8.8.8"],
}
However, obviously, this gets written as a literal string.
I've written the following abomination that does the job, but surely there has to be an easier / more sane way?
from configparser import ConfigParser
import ast
class CustomParser(ConfigParser):
def _write_section(self, fp, section_name, section_items, delimiter):
"""Write a single section to the specified `fp`."""
def write_item(item):
item = self._interpolation.before_write(
self, section_name, key, item
)
if item is not None or not self._allow_no_value:
item = delimiter + str(item).replace("\n", "\n\t")
else:
item = ""
fp.write("{}{}\n".format(key, item))
fp.write("[{}]\n".format(section_name))
for key, value in section_items:
parsed = None
try:
parsed = ast.literal_eval(value)
except Exception:
pass
if isinstance(parsed, list):
for list_item in parsed:
write_item(list_item)
else:
write_item(value)
fp.write("\n")
One solution would be create custom dict and initialize ConfigParser()
with that dict (dict_type=
parameter in constructor):
import sys
from ast import literal_eval
from configparser import ConfigParser
class my_dict(dict):
def items(self):
for k, v in super().items():
if v.startswith("[") and v.endswith("]"):
for i in literal_eval(v):
yield k, i
else:
yield k, v
conf = ConfigParser(dict_type=my_dict)
conf["Match"] = {"Name": "eth0"}
conf["Network"] = {
"DHCP": "no",
"Address": "10.0.0.12/24",
"Gateway": "10.0.0.1",
"Dns": ["1.1.1.1", "8.8.8.8"],
}
conf.write(sys.stdout)
Prints:
[Match]
name = eth0
[Network]
dhcp = no
address = 10.0.0.12/24
gateway = 10.0.0.1
dns = 1.1.1.1
dns = 8.8.8.8