15

I'm looking for an ini style config parser in Python that supports section inheritance similar to what Zend_Config_Ini does in PHP.

Does such a module exist or will I need to roll my own?

Maascamp
  • 348
  • 1
  • 3
  • 8

3 Answers3

20

Python's ConfigParser can load multiple files. Files read later on can override settings from the first file.

For example, my application has database settings in its internal default configuration file:

[database]
server = 127.0.0.1
port = 1234
...

I override these on a different server with a "environment.ini" file containing the same section but different values:

[database]
server = 192.168.0.12
port = 2345
...

In Python:

import os
from ConfigParser import ConfigParser
dbconf = ConfigParser()
dbconf.readfp(open('default.ini'))
if os.path.exists('environment.ini'):
    dbconf.readfp(open('environment.ini'))
dbconf.get('database', 'server') # Returns 192.168.0.12
tuomur
  • 6,888
  • 34
  • 37
  • 1
    Thanks for the info. Unfortunately this won't work for me due to a business requirement of having one master file that will be parsed in a number of programming languages. Looking like I'll need to implement myself. – Maascamp Jul 01 '11 at 23:21
  • 3
    Yes, I implemented one that met my requirements (Zend_Config_Ini style) and converts to python native types where possible. See here [https://bitbucket.org/maascamp/pyconfigini](https://bitbucket.org/maascamp/pyconfigini). Hope it helps. – Maascamp Mar 15 '12 at 00:43
  • 1
    @Maascamp Great Job! I was looking for a python ini parse such as Zend_Config_Ini, you should answer your own question and post your solution :) – dextervip Aug 13 '12 at 06:16
1

Here's what I used. extended_get method is what you need - it supports hierarchical sections.

import re
import io
import ConfigParser

class ZendConfigParser(ConfigParser.ConfigParser):
    def extended_get(self, section, key):
        if self.has_option(section, key):
            return self.get(section, key)
        else:
            orig_section, parent_section = self._get_orig_section(section)
            if orig_section != None:
                if self.has_option(orig_section,key):
                    return self.get(orig_section,key)
                else:
                    return self.extended_get(parent_section,key)
            else:
                return None



    def _get_orig_section(self, zend_section):
        orig_section = None
        parent_section = None
        for section in self.sections():
            if re.search(r'^[ \t]*' + zend_section + '\\b', section) != None:
                orig_section = section
                #look for a parent section
                match = re.match(r'\w+[ \t]*:[ \t]*(\w+)$', section)
                if match != None:
                    parent_section = match.groups()[0]
                break

        return (orig_section, parent_section)

config = ZendConfigParser()
config.read(file)
print(config.extended_get('production', 'database.params.host'))
neubert
  • 15,947
  • 24
  • 120
  • 212
xvga
  • 591
  • 1
  • 9
  • 15
0

I also did not find any ready solution. To solve it, I adapted the get function of ConfigParser to search in the child section and afterwards in the parent section:

config = SafeConfigParser()
config.read(filenames)
required_environment = "mysection"

# determine fallback requirement in case parameter is not found in required environment
fallback_environment = "default"
# loop through all sections of config files
for environment in config.sections():
    # check whether we find an inheritance based on the required section
    if re.search(required_environment + " *: *\w+", environment):
        # found inheritance, take parent as fallback section
        fallback_environment = re.sub(required_environment + r" : (\w+)", r"\1", environment)
        # take this name as requested section
        required_environment = environment

# override get method
_config_parser_get = config.get
def myConfigParserGet(id):
    # check different sections for desired value
    if config.has_option(required_environment, id):
        return _config_parser_get(required_environment, id)
    else:
        return _config_parser_get(fallback_environment, id)

config.get = myConfigParserGet

Restrictions:

  • only read-only access to config supported
  • only one level of inheritance
romor
  • 1,181
  • 15
  • 22