Moved VirtualMailManager/Exceptions to VirtualMailManager/errors.
Renamed VMM*Exception classes to *Error.
No longer add the attribute 'message' to VMMError if it doesn't exist, like in
Python 2.4. It has been deprecated as of Python 2.6.
Also removed the methods code() and msg(), the values are now accessible via
the attributes 'code' and 'msg'.
# -*- coding: UTF-8 -*-# Copyright (c) 2007 - 2010, Pascal Volk# See COPYING for distribution information.""" VirtualMailManager.Config VMM's configuration module for simplified configuration access."""fromConfigParserimport \Error,MissingSectionHeaderError,NoOptionError,NoSectionError, \ParsingError,RawConfigParserfromcStringIOimportStringIO# TODO: move interactive stff to clifromVirtualMailManagerimportexec_ok,get_unicode,is_dirfromVirtualMailManager.constants.ERRORimportCONF_ERRORfromVirtualMailManager.errorsimportConfigError_=lambdamsg:msgclassBadOptionError(Error):"""Raised when a option isn't in the format 'section.option'."""passclassConfigValueError(Error):"""Raised when creating or validating of new values fails."""passclassNoDefaultError(Error):"""Raised when the requested option has no default value."""def__init__(self,section,option):Error.__init__(self,'Option %r in section %r has no default value'%(option,section))classLazyConfig(RawConfigParser):"""The **lazy** derivate of the `RawConfigParser`. There are two additional getters: `pget()` The polymorphic getter, which returns a option's value with the appropriate type. `dget()` Like `LazyConfig.pget()`, but returns the option's default, from `LazyConfig._cfg['sectionname']['optionname'].default`, if the option is not configured in a ini-like configuration file. `set()` differs from `RawConfigParser`'s `set()` method. `set()` takes the `section` and `option` arguments combined to a single string in the form "section.option". """def__init__(self):RawConfigParser.__init__(self)self._modified=False# sample _cfg dict. Create your own in your derived class.self._cfg={'sectionname':{'optionname':LazyConfigOption(int,1,self.getint),}}defbool_new(self,value):"""Converts the string `value` into a `bool` and returns it. | '1', 'on', 'yes' and 'true' will become `True` | '0', 'off', 'no' and 'false' will become `False` Throws a `ConfigValueError` for all other values, except bools. """ifisinstance(value,bool):returnvalueifvalue.lower()inself._boolean_states:returnself._boolean_states[value.lower()]else:raiseConfigValueError(_(u"Not a boolean: '%s'")%get_unicode(value))defgetboolean(self,section,option):"""Returns the boolean value of the option, in the given section. For a boolean True, the value must be set to '1', 'on', 'yes', 'true' or True. For a boolean False, the value must set to '0', 'off', 'no', 'false' or False. If the option has another value assigned this method will raise a ValueError. """# if the setting was modified it may be still a boolean value lets seetmp=self.get(section,option)ifisinstance(tmp,bool):returntmpifnottmp.lower()inself._boolean_states:raiseValueError('Not a boolean: %s'%tmp)returnself._boolean_states[tmp.lower()]def_get_section_option(self,section_option):"""splits ``section_option`` (section.option) in two parts and returns them as list ``[section, option]``, if: * it likes the format of ``section_option`` * the ``section`` is known * the ``option`` is known Else one of the following exceptions will be thrown: * `BadOptionError` * `NoSectionError` * `NoOptionError` """sect_opt=section_option.lower().split('.')# TODO: cache itiflen(sect_opt)!=2:# do we need a regexp to check the format?raiseBadOptionError(_(u"Bad format: '%s' - expected: section.option")%get_unicode(section_option))ifnotsect_opt[0]inself._cfg:raiseNoSectionError(sect_opt[0])ifnotsect_opt[1]inself._cfg[sect_opt[0]]:raiseNoOptionError(sect_opt[1],sect_opt[0])returnsect_optdefitems(self,section):"""returns an iterable that returns key, value ``tuples`` from the given ``section``. """ifsectioninself._sections:# check if the section was parsedsect=self._sections[section]elifnotsectioninself._cfg:raiseNoSectionError(section)else:return((k,self._cfg[section][k].default) \forkinself._cfg[section].iterkeys())# still here? Get defaults and merge defaults with configured settingdefaults=dict((k,self._cfg[section][k].default) \forkinself._cfg[section].iterkeys())defaults.update(sect)if'__name__'indefaults:deldefaults['__name__']returndefaults.iteritems()defdget(self,option):"""Returns the value of the `option`. If the option could not be found in the configuration file, the configured default value, from ``LazyConfig._cfg`` will be returned. Arguments: `option` : string the configuration option in the form "section.option" Throws a `NoDefaultError`, if no default value was passed to `LazyConfigOption.__init__()` for the `option`. """section,option=self._get_section_option(option)try:returnself._cfg[section][option].getter(section,option)except(NoSectionError,NoOptionError):ifnotself._cfg[section][option].defaultisNone:# may be Falsereturnself._cfg[section][option].defaultelse:raiseNoDefaultError(section,option)defpget(self,option):"""Returns the value of the `option`."""section,option=self._get_section_option(option)returnself._cfg[section][option].getter(section,option)defset(self,option,value):"""Set the `value` of the `option`. Throws a `ValueError` if `value` couldn't be converted using `LazyConfigOption.cls`. """# pylint: disable-msg=W0221# @pylint: _L A Z Y_section,option=self._get_section_option(option)val=self._cfg[section][option].cls(value)ifself._cfg[section][option].validate:val=self._cfg[section][option].validate(val)ifnotRawConfigParser.has_section(self,section):self.add_section(section)RawConfigParser.set(self,section,option,val)self._modified=Truedefhas_section(self,section):"""Checks if `section` is a known configuration section."""returnsection.lower()inself._cfgdefhas_option(self,option):"""Checks if the option (section.option) is a known configuration option. """# pylint: disable-msg=W0221# @pylint: _L A Z Y_try:self._get_section_option(option)returnTrueexcept(BadOptionError,NoSectionError,NoOptionError):returnFalsedefsections(self):"""Returns an iterator object for all configuration sections."""returnself._cfg.iterkeys()classLazyConfigOption(object):"""A simple container class for configuration settings. `LazyConfigOption` instances are required by `LazyConfig` instances, and instances of classes derived from `LazyConfig`, like the `Config` class. """__slots__=('__cls','__default','__getter','__validate')def__init__(self,cls,default,getter,validate=None):"""Creates a new `LazyConfigOption` instance. Arguments: `cls` : type The class/type of the option's value `default` Default value of the option. Use ``None`` if the option should not have a default value. `getter` : callable A method's name of `RawConfigParser` and derived classes, to get a option's value, e.g. `self.getint`. `validate` : NoneType or a callable None or any method, that takes one argument, in order to check the value, when `LazyConfig.set()` is called. """self.__cls=clsifnotdefaultisNone:# enforce the type of the default valueself.__default=self.__cls(default)else:self.__default=defaultifnotcallable(getter):raiseTypeError('getter has to be a callable, got a %r'%getter.__class__.__name__)self.__getter=getterifvalidateandnotcallable(validate):raiseTypeError('validate has to be callable or None, got a %r'%validate.__class__.__name__)self.__validate=validate@propertydefcls(self):"""The class of the option's value e.g. `str`, `unicode` or `bool`. """returnself.__cls@propertydefdefault(self):"""The option's default value, may be `None`"""returnself.__default@propertydefgetter(self):"""The getter method or function to get the option's value"""returnself.__getter@propertydefvalidate(self):"""A method or function to validate the value"""returnself.__validateclassConfig(LazyConfig):"""This class is for reading vmm's configuration file."""def__init__(self,filename):"""Creates a new Config instance Arguments: `filename` : str path to the configuration file """LazyConfig.__init__(self)self._cfg_filename=filenameself._cfg_file=Noneself.__missing={}LCO=LazyConfigOptionbool_t=self.bool_newself._cfg={'account':{'delete_directory':LCO(bool_t,False,self.getboolean),'directory_mode':LCO(int,448,self.getint),'disk_usage':LCO(bool_t,False,self.getboolean),'password_length':LCO(int,8,self.getint),'random_password':LCO(bool_t,False,self.getboolean),'imap':LCO(bool_t,True,self.getboolean),'pop3':LCO(bool_t,True,self.getboolean),'sieve':LCO(bool_t,True,self.getboolean),'smtp':LCO(bool_t,True,self.getboolean),},'bin':{'dovecotpw':LCO(str,'/usr/sbin/dovecotpw',self.get,exec_ok),'du':LCO(str,'/usr/bin/du',self.get,exec_ok),'postconf':LCO(str,'/usr/sbin/postconf',self.get,exec_ok),},'database':{'host':LCO(str,'localhost',self.get),'name':LCO(str,'mailsys',self.get),'pass':LCO(str,None,self.get),'user':LCO(str,None,self.get),},'domain':{'auto_postmaster':LCO(bool_t,True,self.getboolean),'delete_directory':LCO(bool_t,False,self.getboolean),'directory_mode':LCO(int,504,self.getint),'force_deletion':LCO(bool_t,False,self.getboolean),},'maildir':{'folders':LCO(str,'Drafts:Sent:Templates:Trash',self.get),'name':LCO(str,'Maildir',self.get),},'misc':{'base_directory':LCO(str,'/srv/mail',self.get,is_dir),'dovecot_version':LCO(int,12,self.getint),'gid_mail':LCO(int,8,self.getint),'password_scheme':LCO(str,'CRAM-MD5',self.get,self.known_scheme),'transport':LCO(str,'dovecot:',self.get),},}defload(self):"""Loads the configuration, read only. Raises a ConfigError if the configuration syntax is invalid. """try:self._cfg_file=open(self._cfg_filename,'r')self.readfp(self._cfg_file)except(MissingSectionHeaderError,ParsingError),err:raiseConfigError(str(err),CONF_ERROR)finally:ifself._cfg_fileandnotself._cfg_file.closed:self._cfg_file.close()defcheck(self):"""Performs a configuration check. Raises a ConfigError if the check fails. """# TODO: There are only two settings w/o defaults.# So there is no need for cStringIOifnotself.__chk_cfg():errmsg=StringIO()errmsg.write(_(u'Missing options, which have no default value.\n'))errmsg.write(_(u'Using configuration file: %s\n')%self._cfg_filename)forsection,optionsinself.__missing.iteritems():errmsg.write(_(u'* Section: %s\n')%section)foroptioninoptions:errmsg.write((u' %s\n')%option)raiseConfigError(errmsg.getvalue(),CONF_ERROR)defknown_scheme(self,scheme):"""Converts `scheme` to upper case and checks if is known by Dovecot (listed in VirtualMailManager.SCHEMES). Throws a `ConfigValueError` if the scheme is not listed in VirtualMailManager.SCHEMES. """scheme=scheme.upper()# TODO: VMM.SCHEMESdefunicode(self,section,option):"""Returns the value of the `option` from `section`, converted to Unicode. """returnget_unicode(self.get(section,option))def__chk_cfg(self):"""Checks all section's options for settings w/o a default value. Returns `True` if everything is fine, else `False`. """errors=Falseforsectioninself._cfg.iterkeys():missing=[]foroption,valueinself._cfg[section].iteritems():if(value.defaultisNoneandnotRawConfigParser.has_option(self,section,option)):missing.append(option)errors=Trueifmissing:self.__missing[section]=missingreturnnoterrorsdel_