4

I would like to read a configuration file in Python completely into a data structure without explicitly 'getting' each value. The reason for doing so is that I intend to modify these values programatically (for instance, I'll have a variable that says I want to modify [Foo] Bar = 1 to be [Foo] Bar = 2), with the intention of writing a new configuration file based on my changes.

At present, I'm reading all the values by hand:

parser = SafeConfigParser()
parser.read(cfgFile)
foo_bar1 = int(parser.get('Foo', 'Bar1'))
foo_bar2 = int(parser.get('Foo', 'Bar2'))

What I would love to have (didn't find much Google-wise) is a method to read them into a list, have them be identified easily so that I can pull that value out of the list and change it.

Essentially referencing it as (or similarly to):

config_values = parser.read(cfgFile)
foo_bar1      = config_values('Foo.bar1')
erik
  • 3,810
  • 6
  • 32
  • 63
  • Could you just overload `__getitem__` (or `__call__`) on a class that wraps the config parser? Then, when you do `class['Foo.Bar1]` (or `class('Foo.Bar1')`) you can split on the period and look it up in the config_parser object? It seems that what you want to do is very close to what the parser already does. – Sam Mussmann Nov 09 '12 at 14:36
  • 1
    @SamMussmann -- It looks like we were thinking the same thing. – mgilson Nov 09 '12 at 14:36

3 Answers3

3

Sorry if I'm misunderstanding -- This really doesn't seem much different than what you have -- It seems like a very simple subclass would work:

class MyParser(SafeConfigParser):
    def __call__(self,path,type=int):
        return type(self.get(*path.split('.')))

and of course, you wouldn't actually need a subclass either. You could just put the stuff in __call__ into a separate function ...

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • This looks interesting, but I'm slightly confused. Is this overloading the parser.read function? I'd like to just read all the config values into a list and then pull them out dictionary style (similar to @Ryan G's answer in a way) – erik Nov 09 '12 at 15:10
2

Are you running python 2.7?

There's a nifty way, I discovered a few months back, to parse the config file and setup a dictionary using dictionary comprehension.

config = ConfigParser.ConfigParser()
config.read('config.cfg')
# Create a dictionary of complete config file, {'section':{'option':'values'}, ...}
configDict = {section:{option:config.get(section,option) for option in config.options(section)} for section in config.sections()}

Although this way is harder to read, it take's up less space, and you don't have to explicitly state every variable that you want to get.

  • Note: This won't work on python 2.6 (*that I know of). I've written scripts in the past where I used this, and since I'm running 2.7 on Windows, somebody on a linux machine, running 2.6, will crash on the dictionary comprehension.

--Edit--

You will need to still manually change the data types of the values.

Ryan G
  • 9,184
  • 4
  • 27
  • 27
  • That would be lovely...but yea I'm running 2.6 (had 2.7 tag on by mistake) – erik Nov 09 '12 at 15:09
  • @espais: you could use it on Python 2.6 if you replace `{k: v for ..}` with `dict((k, v) for ..)` – jfs Nov 09 '12 at 15:35
  • @J.F.Sebastian: Am I missing something? `cfg_dict = dict(section: dict(option:cfg.get(section,option) for option in cfg.options(section)) for section in cfg.sections())` throws an error on the 2nd param after `cfg.options(section))` – erik Nov 09 '12 at 15:56
  • @espais: yes. Replace `a: b` with `(a, b)` – jfs Nov 09 '12 at 16:04
  • @J.F.Sebastian: sorry for my lack of Python-knowledge....but now I see this: `SyntaxError: Generator expression must be parenthesized if not sole argument`...I tried parenthesizing the 2nd argument but that didn't help – erik Nov 09 '12 at 16:15
  • @espais: I've posted it as [an answer](http://stackoverflow.com/a/13311897/4279). – jfs Nov 09 '12 at 16:22
2
import sys
from ConfigParser import SafeConfigParser

parser = SafeConfigParser()
parser.readfp(sys.stdin)

config = dict((section, dict((option, parser.get(section, option))
                             for option in parser.options(section)))
              for section in parser.sections())
print config

Input

[a]
b = 1
c = 2
[d]
e = 3

Output

{'a': {'c': '2', 'b': '1'}, 'd': {'e': '3'}}
jfs
  • 399,953
  • 195
  • 994
  • 1,670