--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/iro/config.py Thu Sep 27 17:15:46 2012 +0200
@@ -0,0 +1,219 @@
+# 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
+
+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","/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"""