Python Notes

Wednesday, October 20, 2004

Inifile: an ini-style configuration template

IniFile is a ini-style configuration module that uses a template to define the configuration file structure. The code below includes some samples. Feel free to use as-is.


"""
inifile.py

Library for reading and writing ini-style config files. The ini file
structure is provided as a class, with nested classes acting as the
sections.

Usage:

# declare the ini file structure using a class statement
class MyIni(IniFile):
one = TypedAttribute(1)
two = TypedAttribute(2)
three = TypedAttribute(3, name='triple')
class NestedSection(IniSection):
str1 = TypedAttribute('one', name='str_one')
str2 = TypedAttribute('two', name='str_two')

# create a configuration instance
config = MyIni()

# read the config file
inifile.load()
or
inifile.read()

# write it back
inifile.save()
or
inifile.write()

-- sample ini file --
[server]
socketPort = 8080
threadPool = 10

[staticContent]
bitmaps = /var/local/bitmaps

[session]
storageType=ram

"""

from metatemplate import GenericTemplate, GenericAttribute, TypedAttribute, next_attribute_id
from types import StringType
from inspect import isclass
import re

debug_inifile = 0

class IniSection(GenericTemplate):

re_section = re.compile(r'^\[(.*)\]')
re_unquote = re.compile(r'^(?P[\"\']?)(?P.*)(?P=openquote)$')

def log(self, line):
"""simple auxiliary log functions"""
if debug_inifile:
# should use the logger interface!
print line

def unquote(self, value):
"""unquotes strings reads from the config file"""
return self.re_unquote.match(value).groupdict()['content']

def read(self, fileobj):
"""reads the config file info from a file-like object"""

for line in fileobj:
#attr_name_list = [f[0] for f in self.__fields__]
attr_name_list = self.__attr__.keys()
line = line.strip()
if not line: continue
matchresult = self.re_section.match(line)
if matchresult:
sectionname = matchresult.group(1)
self.log('Found section: %s' % sectionname)
while sectionname in attr_name_list:
# found a known section
section = getattr(self, sectionname, None)
if isinstance(section, IniSection):
self.log('Section: %s' % sectionname)
sectionname = section.read(fileobj)
self.log('Section finished: %s' % sectionname)
else:
# section name does refer to a section object
# ?should it raise a fatal exception/warning?
self.log('Section error: %s' % sectionname)
sectionname = None
else:
# section is not a child of this node, it's a
# sibling; returns the section name
self.log('Unknown section: %s' % sectionname)
return sectionname
else:
# found an attribute
name, value = line.split('=',1)
name = name.strip()
value = value.strip()
self.log('Normal attribute: %s = %s' % (name, value))

# retrieves the attribute using the alternative name
cls_attr_tuple = self.__class__.__attr__.get(name, None)
if not cls_attr_tuple:
# invalid attribute found in the config file!
# ? should it cause a exception or warning ?
print '1:', self.__class__.__fields__
print '2:', self.__class__.__attr__
print '3:', self.__class__.__attr__[name]
print '4:', cls_attr_tuple
print 'Error - Attribute not found: %s' % (name,)
raise KeyError

# retrieves the name used for binding
cls_attr_name, cls_attr = cls_attr_tuple

# sets the instance value
if isinstance(cls_attr, GenericAttribute):
setattr(self, cls_attr_name, self.unquote(value))
else:
# invalid attribute found in the config file!
# ? should it cause a exception or warning ?
raise KeyError

def write(self, fileobj, _initialpos = None):
"""writes the config file info to a file-like object"""
if not _initialpos:
_initialpos = fileobj.tell()

for fname, fobj in self.__fields__:
# looks at the class to retrieve the attribute definition
cls_attr = getattr(self.__class__, fname, None)
# gets the attribute value stored in the instance
ins_attr = getattr(self, fname, None)

# is a subsection?
if isclass(cls_attr) and issubclass(cls_attr, IniSection):
# writes an empty line before the section (if needed)
currentpos = fileobj.tell()
if currentpos > _initialpos:
fileobj.write('\n')

# writes section name & section contents
fileobj.write('[%s]\n' % fname)
ins_attr.write(fileobj, _initialpos = currentpos)

# is an attribute?
elif isinstance(cls_attr, GenericAttribute):
fileobj.write('%s=%s\n' % (cls_attr.name, str(ins_attr)))

class IniFile(IniSection):
def load(self, fname=None):
if not fname:
fname = self.name + '.ini'
inifile = open(fname, 'r')
self.read(inifile)
inifile.close()

def save(self, fname=None):
if not fname:
fname = self.name + '.ini'
inifile = open(fname, 'w')
self.write(inifile)
inifile.close()

15 Comments:

Post a Comment

<< Home