9

I read the part of the docs and saw that the ConfigParser returns a list of key/value pairs for the options within a section. I figured that keys did not need to be unique within a section, otherwise the parser would just return a mapping. I designed my config file schema around this assumption, then sadly realized that this is not the case:

>>> from ConfigParser import ConfigParser
>>> from StringIO import StringIO
>>> fh = StringIO("""
... [Some Section]
... spam: eggs
... spam: ham
... """)
>>> parser = ConfigParser()
>>> parser.readfp(fh)
>>> print parser.items('Some Section')
[('spam', 'ham')]

Then I went back and found the part of the docs that I should have read:

Sections are normally stored in a builtin dictionary. An alternative dictionary type can be passed to the ConfigParser constructor. For example, if a dictionary type is passed that sorts its keys, the sections will be sorted on write-back, as will be the keys within each section.

To keep my existing configuration file scheme (which I really like now ;) I'm thinking of passing a mapping-like object as mentioned above that accumulates values instead of clobbering them. Is there a simpler way to prevent key/value collapse that I'm missing? Instead of making a crazy adapter (that could break if ConfigParser's implementation changes) should I just write a variant of the ConfigParser itself?

I feel like this may be one of those 'duh' moments where I'm only seeing the difficult solutions.

[Edit:] Here's a more precise example of how I'd like to use the same key multiple times:

[Ignored Paths]
ignore-extension: .swp
ignore-filename: tags
ignore-directory: bin

I dislike the comma-delimited-list syntax because it's hard on the eyes when you scale it to many values; for example, a comma delimited list of fifty extensions would not be particularly readable.

cdleary
  • 69,512
  • 53
  • 163
  • 191
  • 1
    I think using lists would be more readable than 50 lines of 'ignore-extension:...' – Jeremy Cantrell Nov 13 '08 at 21:09
  • 1
    Really? I disagree... with each on a separate line you can just scan down the list -- I've never found comma delimited lists of three-letter extensions to be pretty. I also anticipate people wanting to easily comment any given option out, which separate lines is conducive to. – cdleary Nov 13 '08 at 21:18

2 Answers2

10

ConfigParser isn't designed to handle such conditions. Furthermore, your config file doesn't make sense to me.

ConfigParser gives you a dict-like structure for each section, so when you call parser.items(section), I'm expecting similar output to dict.items(), which is just a list of key/value tuples. I would never expect to see something like:

[('spam', 'eggs'), ('spam', 'ham')]

Not to mention, how would you expect the following to behave?:

parser.get('Some Section', 'spam')

Which is the intended way to retrieve values.

If you want to store multiple values for the same key, I would suggest something like this in your config file:

[Some Section]
spam: eggs, ham

And this in your code:

spam_values = [v.strip() for v in parser.get('Some Section', 'spam').split(',')]

Of course, this will only work for values that don't contain commas themselves or handle quoting. For that, you should employ a more advanced technique (see this and this).

EDIT: If you don't mind the extra dependency, You could check out ConfigObj, which natively supports lists as a value type.

Community
  • 1
  • 1
Jeremy Cantrell
  • 26,392
  • 13
  • 55
  • 78
  • I would expect `parser.get` to return either the first or the last value associated with the key. I was hoping that the configuration was stored *internally* as items. I dislike the list syntax because it's more difficult to eyeball, but I may have to switch. – cdleary Nov 13 '08 at 20:36
  • I'm speculating, but I think a dict would be a more efficient base class for a section. If this is the case, it would explain the behavior. Either way, you're going to give yourself one hell of a headache trying to make it do what you want. – Jeremy Cantrell Nov 13 '08 at 21:03
  • Fair enough. I have enough headaches as-is. :) – cdleary Nov 13 '08 at 21:20
0

This deficiency of ConfigParser is the reason why pyglet used patched version of epydoc to replace ConfigParser ini with this simple format:

name: pyglet
url: http://www.pyglet.org/

output: html
target: doc/api/
...    
module: pyglet

exclude: pyglet.gl.gl
exclude: pyglet.gl.agl
exclude: pyglet.gl.lib_agl
exclude: pyglet.gl.wgl
...

If you don't need sections - this approach can be useful.

anatoly techtonik
  • 19,847
  • 9
  • 124
  • 140