This seems like the kind of thing you'll have to write your own parser to handle. I'd still drop it into a ConfigParser
object, but only so it can be more easily used in the future. configparser.ConfigParser
can do almost everything you need to do. I'm not aware of any way to tell it to treat
[SomeHeader]
foo = bar
foo = baz
as config["SomeHeader"]["foo"] == ["bar", "baz"]
as you mention near the end of your question.
You could try something like:
def is_section_header(text):
# what makes a section header? To me it's a word enclosed by
# square brackets.
match = re.match(r"\[([^\]]+)\]", text)
if match:
return match.group(1)
def get_option_value_tuple(line):
"""Returns a tuple of (option, comma-separated values as a string)"""
option, *values = line.split(" ")
return (option, ", ".join(values))
def parse(filename):
config = configparser.ConfigParser()
cursection = None
with open(filename) as inifile:
for line in inifile:
sectionheader = is_section_header(line)
if sectionheader:
cursection = sectionheader
try:
config.add_section(sectionheader)
except configparser.DuplicateSectionError:
# This section already exists!
# how should you handle this?
continue # ignore for now
else:
option, values = get_option_value_tuple(line.strip())
if config.has_option(cursection, option):
config[cursection][option] += (", " + values)
else:
config[cursection][option] = values
return config
This will make:
[SomeHeader]
foo bar baz
foo spam eggs
bar baz
parse the same as a standard parse of
[SomeHeader]
foo = bar, baz, spam, eggs
bar = baz