iro.config: using OrderedDict instead of Dict + List for Options.
from twisted.python import log
from ConfigParser import ConfigParser
import signal
from functools import partial
from collections import OrderedDict
from validate import vInteger
from error import NeededOption
class MyConfigParser(ConfigParser):
"""Configparser that also validate configfile.
It is possile to restiger function, that are called, when config file is reloaded
"""
def __init__(self):
ConfigParser.__init__(self)
self.reloadList=[]
def read(self,files):
"""reads an validate configuration file"""
from offer import getProvider
r = ConfigParser.read(self, files)
for s in self.sections():
if s == "main":
main.validate(self.items(s))
else:
getProvider("tmp", self.get(s,'typ'), self.items(s))
return r
def reload_(self):
"""run all registered function."""
for f in self.reloadList:
f()
def registerReload(self, func):
"""adds **func** to reloadList.
func ist called with no arguments.
"""
self.reloadList.append(func)
class Option():
"""One Option in the configuration file"""
def __init__(self, validate, long="", help="", must=False, default=None):
"""
:param func validate: a validate function, it has to return the value, if valid and raise an error if not.
:param string long: long description
:param string help: the help text
:param boolean must: Is this option nessasary
:param default: default value
"""
self.validate = validate
self.long=long
self.help = help
self.must = must
self.default = default
class Config:
"""Base class for all classes, that uses option from configfile.
If one option is valid, the attribute is created with the value of the validate function.
"""
def __init__(self, name, options=None):
"""
:param string name: section name.
:param `collections.OrderedDict` options: Orderd Dict of the configuration options (see :attr:`options`)
"""
self.name = name
self.options = options
"""Options :class:`collections.OrderedDict` for Options used in configuration file (see :class:`iro.config.Option`). Ordering of configuration fields are done by :attr:`order`.
Sample::
OrderedDict([
("dburl",Option(lambda x,y:x,long="Connection URL to database",must=True)),
("port",Option(partial(vInteger,minv=0),long="Port under that twisted is running",must=True)),
])
A child class typically use update to add more options.
"""
self._init = True
"""indecates, if the config is loaded with config values."""
def _read(self, cfg, write=False):
"""Test or set configuration options.
:param dict cfg: The Configuration dict. Normally you use ``configParser.items("section")``.
:param boolean write: test or set the option to actual object.
:raises: :exc:`iro.error.NeededOption`
"""
c = dict(cfg)
for o in self.options:
option = self.options[o]
try:
value = option.validate(c[o],o)
if write:
self._init = False
setattr(self,o,value)
except KeyError:
if option.must:
raise NeededOption(self.name, o)
elif write and option.default is not None:
setattr(self, o, option.default)
def validate(self, cfg):
"""Validate configuration.
:param dict cfg: The Configuration dict. Normally you use ``configParser.items("section")``.
"""
self._read(cfg, False)
def load(self, cfg):
"""Loads configuration into object.
:param dict cfg: The Configuration dict. Normally you use ``configParser.items("section")``.
"""
self._read(cfg, True)
def same(self, other):
"""returns ``True``, if the options of other object are the same"""
for o in self.options:
if getattr(self,o) != getattr(other,o):
return False
else:
return True
def sampleConf(self):
"""returns a sample Configuration section.
This function also adds the long help text to the sample section.
:return: a list of lines
"""
ret=[]
for o in self.options:
opt=self.options[o]
if opt.long:
ret.append("# "+opt.long)
if opt.must:
s= "%s = "%o
if opt.default is not None:
s += str(opt.default)
ret.append(s)
else:
ret.append("# %s = %s"%(o,opt.default))
ret.append("")
return ["[%s]"%self.name,]+ret
def readConfig():
"""Read the configuration and update all registered object (see :meth:`MyConfigParser.reload_`)."""
log.msg("Reading configs.")
configParser.read(confFiles)
configParser.reload_()
if main._init:
main.load(configParser.items("main"))
else:
m = Config("main", main_options)
m.load(configParser.items("main"))
if not main.same(m):
raise Exception("Main options can't be reloaded, please restart your Application.")
def init():
"""Load the main options."""
configParser.read(confFiles)
main.load(configParser.items("main"))
def registerSignal():
'''register readConfig to SIGUSR2'''
def rC(signal, frame):
readConfig()
signal.signal(signal.SIGUSR2,rC)
configParser = MyConfigParser()
"""configParser to get configuration."""
confFiles=["iro.conf", "~/iro.conf","/etc/iro/iro.conf"]
"""Configfile list """
main_options = OrderedDict([
("dburl",Option(lambda x,y:x,long="Connection URL to database",must=True)),
("port",Option(partial(vInteger,minv=0),long="Port under that twisted is running",must=True)),
])
"options for main section"
main = Config("main", main_options)
"""Main config options"""