iro/config.py
changeset 302 3f4bdea2abbf
parent 294 0e75bd39767d
child 306 b31978c0c043
equal deleted inserted replaced
90:eb04ac3a8327 302:3f4bdea2abbf
       
     1 # Copyright (c) 2012 netzguerilla.net <iro@netzguerilla.net>
       
     2 # 
       
     3 # This file is part of Iro.
       
     4 # 
       
     5 # Permission is hereby granted, free of charge, to any person obtaining a copy of
       
     6 # this software and associated documentation files (the "Software"), to deal in
       
     7 # the Software without restriction, including without limitation the rights to use,
       
     8 # copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
       
     9 # #Software, and to permit persons to whom the Software is furnished to do so,
       
    10 # subject to the following conditions:
       
    11 # 
       
    12 # The above copyright notice and this permission notice shall be included in
       
    13 # all copies or substantial portions of the Software.
       
    14 # 
       
    15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
       
    16 # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
       
    17 # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
       
    18 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
       
    19 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
       
    20 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       
    21 
       
    22 from twisted.python import log
       
    23 
       
    24 from ConfigParser import ConfigParser
       
    25 import signal
       
    26 from functools import partial
       
    27 try:
       
    28     from collections import OrderedDict
       
    29 except ImportError:
       
    30     from ordereddict import OrderedDict
       
    31 
       
    32 from validate import vInteger
       
    33 from error import NeededOption 
       
    34 
       
    35 class MyConfigParser(ConfigParser):
       
    36     """Configparser that also validate configfile.
       
    37 
       
    38     It is possile to restiger function, that are called, when config file is reloaded
       
    39     """
       
    40     def __init__(self):
       
    41         ConfigParser.__init__(self)
       
    42         self.reloadList=[]
       
    43 
       
    44     def read(self,files):
       
    45         """reads an validate configuration file"""
       
    46         from offer import getProvider
       
    47         r = ConfigParser.read(self, files)
       
    48         for s in self.sections():
       
    49             if s == "main":
       
    50                 main.validate(self.items(s))
       
    51             else:
       
    52                 getProvider("tmp", self.get(s,'typ'), self.items(s))
       
    53         return r
       
    54 
       
    55     def reload_(self):
       
    56         """run all registered function."""
       
    57         for f in self.reloadList:
       
    58             f()
       
    59 
       
    60     def registerReload(self, func):
       
    61         """adds **func** to reloadList.
       
    62         
       
    63         func ist called with no arguments.
       
    64         """
       
    65         self.reloadList.append(func)
       
    66 
       
    67 class Option():
       
    68     """One Option in the configuration file"""
       
    69     def __init__(self, validate, long="", help="", must=False, default=None):
       
    70         """
       
    71         :param func validate: a validate function, it has to return the value, if valid and raise an error if not.
       
    72         :param string long: long description
       
    73         :param string help: the help text
       
    74         :param boolean must: Is this option nessasary
       
    75         :param default: default value
       
    76         """
       
    77         self.validate = validate
       
    78         self.long=long
       
    79         self.help = help
       
    80         self.must = must
       
    81         self.default = default
       
    82 
       
    83 class Config:
       
    84     """Base class for all classes, that uses option from configfile.
       
    85     
       
    86     If one option is valid, the attribute is created with the value of the validate function.
       
    87     """
       
    88     def __init__(self, name, options=None):
       
    89         """
       
    90         :param string name: section name.
       
    91         :param `collections.OrderedDict` options: Orderd Dict of the configuration options (see :attr:`options`) 
       
    92         """
       
    93         self.name = name
       
    94         
       
    95         self.options = options
       
    96         """Options :class:`collections.OrderedDict` for Options used in configuration file (see :class:`iro.config.Option`). Ordering of configuration fields are done by :attr:`order`. 
       
    97         
       
    98         Sample::
       
    99         
       
   100             OrderedDict([
       
   101                 ("dburl",Option(lambda x,y:x,long="Connection URL to database",must=True)),
       
   102                 ("port",Option(partial(vInteger,minv=0),long="Port under that twisted is running",must=True)),
       
   103             ])
       
   104         
       
   105         A child class typically use update to add more options.
       
   106         """
       
   107         
       
   108         self._init = True
       
   109         """indecates, if the config is loaded with config values."""
       
   110 
       
   111 
       
   112     def _read(self, cfg, write=False):
       
   113         """Test or set configuration options.
       
   114         
       
   115         :param dict cfg: The Configuration dict. Normally you use ``configParser.items("section")``.
       
   116         :param boolean write: test or set the option to actual object.
       
   117 
       
   118         :raises: :exc:`iro.error.NeededOption`
       
   119         """
       
   120         c = dict(cfg)
       
   121 
       
   122         for o in self.options:
       
   123             option = self.options[o]
       
   124             try:
       
   125                 value = option.validate(c[o],o)
       
   126                 if write:
       
   127                     self._init = False
       
   128                     setattr(self,o,value)
       
   129             except KeyError:
       
   130                 if option.must:
       
   131                     raise NeededOption(self.name, o)
       
   132                 elif write and option.default is not None:
       
   133                     setattr(self, o, option.default)
       
   134 
       
   135     def validate(self, cfg):
       
   136         """Validate configuration.
       
   137 
       
   138         :param dict cfg: The Configuration dict. Normally you use ``configParser.items("section")``.
       
   139         """
       
   140         self._read(cfg, False)
       
   141 
       
   142     def load(self, cfg):
       
   143         """Loads configuration into object.
       
   144 
       
   145         :param dict cfg: The Configuration dict. Normally you use ``configParser.items("section")``.
       
   146         """
       
   147         self._read(cfg, True)
       
   148 
       
   149     def same(self, other):
       
   150         """returns ``True``, if the options of other object are the same"""
       
   151         for o in self.options:
       
   152             if getattr(self,o) != getattr(other,o):
       
   153                 return False
       
   154         else:
       
   155             return True
       
   156 
       
   157     def sampleConf(self):
       
   158         """returns a sample Configuration section.
       
   159 
       
   160         This function also adds the long help text to the sample section.
       
   161 
       
   162         :return: a list of lines
       
   163         """
       
   164         ret=[]
       
   165         for o in self.options:
       
   166             opt=self.options[o]
       
   167             if opt.long:
       
   168                 ret.append("# "+opt.long)
       
   169             if opt.must:
       
   170                 s= "%s = "%o
       
   171                 if opt.default is not None:
       
   172                     s += str(opt.default)
       
   173                 ret.append(s)
       
   174             else:
       
   175                 ret.append("# %s = %s"%(o,opt.default))
       
   176             ret.append("")
       
   177 
       
   178         return ["[%s]"%self.name,]+ret
       
   179 
       
   180 def readConfig():
       
   181     """Read the configuration and update all registered object (see :meth:`MyConfigParser.reload_`)."""
       
   182     log.msg("Reading configs.")
       
   183     configParser.read(confFiles)
       
   184     configParser.reload_()
       
   185     if main._init:
       
   186         main.load(configParser.items("main"))
       
   187     else:
       
   188         m = Config("main", main_options)
       
   189         m.load(configParser.items("main"))
       
   190         if not main.same(m):
       
   191             raise Exception("Main options can't be reloaded, please restart your Application.")
       
   192 
       
   193 def init():
       
   194     """Load the main options."""
       
   195     configParser.read(confFiles)
       
   196     main.load(configParser.items("main"))
       
   197 
       
   198 def registerSignal():
       
   199     '''register readConfig to SIGUSR2'''
       
   200     def rC(signal, frame):
       
   201         readConfig()
       
   202 
       
   203     signal.signal(signal.SIGUSR2,rC)
       
   204 
       
   205 configParser = MyConfigParser()
       
   206 """configParser to get configuration."""
       
   207 
       
   208 confFiles=["~/iro.conf","/etc/iro/iro.conf"]
       
   209 """Configfile list """
       
   210 
       
   211 main_options = OrderedDict([
       
   212     ("dburl",Option(lambda x,y:x,long="Connection URL to database",must=True)),
       
   213     ("port",Option(partial(vInteger,minv=0),long="Port under that twisted is running",must=True)),
       
   214     ])
       
   215 
       
   216 "options for main section"
       
   217 
       
   218 main = Config("main", main_options)
       
   219 """Main config options"""