Source code for iro.config

# Copyright (c) 2012 netzguerilla.net <iro@netzguerilla.net>
# 
# This file is part of Iro.
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
# #Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from twisted.python import log

from ConfigParser import ConfigParser
import signal
from functools import partial
try:
    from collections import OrderedDict
except ImportError:
    from ordereddict import OrderedDict

from validate import vInteger
from error import NeededOption 

[docs]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=[]
[docs] 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
[docs] def reload_(self): """run all registered function.""" for f in self.reloadList: f()
[docs] def registerReload(self, func): """adds **func** to reloadList. func ist called with no arguments. """ self.reloadList.append(func)
[docs]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
[docs]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)
[docs] def validate(self, cfg): """Validate configuration. :param dict cfg: The Configuration dict. Normally you use ``configParser.items("section")``. """ self._read(cfg, False)
[docs] def load(self, cfg): """Loads configuration into object. :param dict cfg: The Configuration dict. Normally you use ``configParser.items("section")``. """ self._read(cfg, True)
[docs] 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
[docs] 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
[docs]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.")
[docs]def init(): """Load the main options.""" configParser.read(confFiles) main.load(configParser.items("main"))
[docs]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","/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"""