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?
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?
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
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'))
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: