1
\$\begingroup\$

I have written ExtendedConfigParser class that adds ability to dot access config values like ExtendedConfigParser().section_name.option_name:

from ConfigParser import SafeConfigParser


class ExtendedConfigParser(SafeConfigParser):

    def __init__(self, config_file=None, type_conversion={}, *args, **kwargs):
        self.config_file = config_file
        self.type_conversion = type_conversion
        SafeConfigParser.__init__(self, *args, **kwargs)
        self.chain = []
        self.cmap = {}
        if config_file:
            self.read(config_file)

    def section2dict(self, secname):
        opts = self.options(secname)
        def opt_conv_type(optname, value):
            if optname in self.cmap:
                return self.cmap[optname](value)
            return value
        return {o:opt_conv_type(o, self.get(secname, o)) for o in opts}

    def add_type_conversion_map(self, cmap):
        self.cmap.update(cmap)

    def __getattr__(self, attrname):
        if attrname in self.sections():
            self.chain.append(attrname)
            return self
        if len(self.chain) == 1 and attrname in self.section2dict(self.chain[0]):
            return self.section2dict(self.chain[0])[attrname]
        self.chain = []
        return getattr(SafeConfigParser, attrname)(self)

Let's have config file like this:

[globals]

webui_port = 8888

redis_host = 127.0.0.1
redis_port = 5600

Then it works like this:

cmap = {'redis_port':int, 'webui_port':int}
cfg = ExtendedConfigParser(config_file=confile, type_conversion=cmap)
print cfg.globals.webui_port

What I do not like about the code above is that I'm doing sort of manual attribute resolution in __getattr__. Is there a better (cleaner, more general) solution?

And also what will happen if a section name happens to be called like SafeConfigParser attribute? It would override it, correct? How can I avoid that?

\$\endgroup\$
1
\$\begingroup\$

I'd start with the caveat that you're buying some syntax sugar at the cost of possible bugs - the need to do the attribute resolution, and as you point out the possibility of name clashes, makes it seem like it may not be worth the effort unless you need to access these class properties in lots of places in your code.

That said you can simplify this by converting your sections - which are dictionaries -- to namedtuples:

from collections import namedtuple

def section2dict(self, secname):
   if secname in self.__dict__:
      raise KeyError, "%s is cannot be used as a property name" % secname        
      opts = self.options(secname)
      #nb: I didn't bother recreating the type code, but you would do it here...
      nt = namedtuple(secname, opts.keys())(**opts)
      self.__dict__[secname] = nt

By sticking the result into dict you don't have to override getattr yourself, which limits the intervention to a single place although it does deprive you of the option to do guard checks for bad values, etc. This example would except on name collisions and get you off the hook for manually redirecting double-dotted attribute queries.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.