# 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"""