0001"""
0002**Deprecated** (13 Aug 2006)
0003
0004A config file loader that can load and nest multiple config files, the
0005config files can have structure, and the values can be tracked back to
0006their original file and line number.
0007
0008To start, you'd do something like:
0009
0010 >>> config = LazyLoader()
0011
0012You could use ``.load(filename)`` to load a config file; for the
0013examples it is convenient to instead use loadstring, and give a fake
0014filename:
0015
0016 >>> config_data = \"\"\"
0017 ... [server]
0018 ... port = 8000
0019 ... host = localhost
0020 ... document_root = /var/www
0021 ... \"\"\"
0022 >>> config.loadstring(config_data, filename='config_data.conf')
0023 >>> config['server']['port']
0024 '8000'
0025 >>> config['server'].convert('port', int)
0026 8000
0027 >>> config['server'].convert('host', int)
0028 Traceback (most recent call last):
0029 ...
0030 ValueError: Error in config_data.conf (section [server]), line 4 ('localhost'):
0031 ValueError: invalid literal for int(): localhost
0032
0033Note that names are normalized, removing case, underscores, and
0034spaces. So to get to the document root:
0035
0036 >>> config['server']['documentroot']
0037 '/var/www'
0038
0039You can also merge in values; for instance, consider a virtual host
0040that overrides global values:
0041
0042 >>> vhost_data = \"\"\"
0043 ... [vhost(my.host.com)]
0044 ... document_root = /path/to/root
0045 ... \"\"\"
0046 >>> vhost_config = LazyLoader()
0047 >>> vhost_config.loadstring(vhost_data, filename='vhost_data.conf')
0048 >>> vhost_config['vhost'].keys()
0049 ['my.host.com']
0050
0051Note that key and section names can be nested with .'s, and ()'s quote
0052the values (so the key is ['my.host.com'] instead of
0053['my']['host']['com']). Then we may want to merge this in, based on a
0054condition (e.g., the hostname matches my.host.com):
0055
0056 >>> config['server'].merge(vhost_config['vhost']['my.host.com'])
0057 >>> config['server']['documentroot']
0058 '/path/to/root'
0059
0060"""
0061
0062
0063import warnings
0064warnings.warn('initools.lazyloader is not supported or recommended for use.',
0065 DeprecationWarning)
0066
0067from lazyiniparser import LazyINIParser, Item
0068from nested import NestedDict
0069import inischema
0070import re
0071from UserDict import UserDict, DictMixin
0072
0073class LazyLoader(NestedDict):
0074
0075 def __init__(self, configs=None, mutable=False, nest=True,
0076 section_name=None, master=None):
0077 self.section_name = section_name
0078 self.master = master
0079 NestedDict.__init__(self, configs=configs, mutable=mutable,
0080 nest=nest)
0081
0082 def load(self, filename):
0083 parser = LazyINIParser(allow_empty_sections=True)
0084 parser.load(filename)
0085 config = self._convert_configuration(parser.configuration)
0086 self.add_config(config)
0087
0088 def loadstring(self, s, filename=None):
0089 parser = LazyINIParser(allow_empty_sections=True)
0090 parser.loadstring(s, filename=filename)
0091 config = self._convert_configuration(parser.configuration)
0092 self.add_config(config)
0093
0094 def merge(self, lazyloader):
0095 if self.master:
0096 self.master._propagate_merge([self.section_name], lazyloader)
0097 else:
0098 self._propagate_merge([], lazyloader)
0099
0100 def _propagate_merge(self, slave_keys, lazyloader):
0101 if self.master:
0102 slave_keys.insert(0, self.section_name)
0103 self.master._propagate_merge(slave_keys, lazyloader)
0104 else:
0105 configs = filter(None, lazyloader.configs)
0106 new_configs = []
0107 for config in configs:
0108 if slave_keys:
0109 new_config = {}
0110 set_config = new_config
0111 for key in slave_keys[:-1]:
0112 set_config[key] = {}
0113 set_config = set_config[key]
0114 set_config[slave_keys[-1]] = config
0115 else:
0116 new_config = config
0117 self.add_config(new_config)
0118
0119 def __getitem__(self, key):
0120 try:
0121 return NestedDict.__getitem__(self, key)
0122 except KeyError, e:
0123 if self.section_name is None:
0124 section = 'global section'
0125 else:
0126 section = 'section [%s]' % self.section_name
0127 raise KeyError(
0128 "Key %r not found in %s" % (key, section))
0129
0130 def _convert_configuration(self, conf):
0131 data = {}
0132 for section in conf.sections:
0133 section_keys = self._parse_keys(section.name)
0134 if section_keys == ['global']:
0135 section_keys = []
0136 for item in section.items:
0137 item_keys = self._parse_keys(item.name)
0138 all_keys = section_keys + item_keys
0139 pos = data
0140 for key in all_keys[:-1]:
0141 pos = pos.setdefault(key, {})
0142 pos.setdefault(all_keys[-1], []).append(item)
0143 return data
0144
0145 def _parse_keys(self, name):
0146 keys = []
0147 orig_name = name
0148 name = name.strip()
0149 while 1:
0150 next_period = name.find('.')
0151 next_paren = name.find('(')
0152 if next_paren == -1 and next_period == -1:
0153 next = self._canonical_name(name)
0154 if next:
0155 keys.append(next)
0156 return keys
0157 elif (next_paren == -1
0158 or next_period != -1 and next_period < next_paren):
0159 next = self._canonical_name(name[:next_period])
0160 if next:
0161 keys.append(next)
0162 name = name[next_period+1:]
0163 else:
0164 assert next_paren != -1
0165 assert next_period == -1 or next_paren < next_period
0166 next = self._canonical_name(name[:next_paren])
0167 if next:
0168 keys.append(next)
0169 name = name[next_paren+1:]
0170 next_close = name.find(')')
0171 if next_close == -1:
0172 raise inischema.ParseValueError(
0173 "Key name contains a ( with no closing ): %r"
0174 % orig_name)
0175 next = name[:next_close]
0176 keys.append(next)
0177 name = name[next_close+1:]
0178
0179 _canonical_re = re.compile('[_ \t-]')
0180 def _canonical_name(self, name):
0181 return self._canonical_re.sub('', name.lower())
0182
0183 def _convert_single(self, key, value_list):
0184 if isinstance(value_list[0], (dict, DictMixin, UserDict)):
0185 return self.__class__(
0186 value_list, mutable=self.mutable, nest=True,
0187 section_name=key, master=self)
0188 elif isinstance(value_list[0][-1], Item):
0189 return value_list[0][-1].content
0190 else:
0191 return value_list[0][-1]
0192
0193 def getlist(self, key, add_inherited=True):
0194 results = self.raw_get(key, add_inherited=add_inherited)
0195 converted = []
0196 for result_set in results:
0197 if not isinstance(result_set, (list, tuple)):
0198 result_set = [result_set]
0199 for item in result_set:
0200 if isinstance(item, (dict, UserDict, DictMixin)):
0201 value = self.__class__(
0202 [item], mutable=self.mutable, nest=self.nest)
0203 elif isinstance(item, Item):
0204 value = item.content
0205 else:
0206 value = item
0207 converted.append(value)
0208 return converted
0209
0210 def _make_converter(self, key, converter):
0211 if not isinstance(converter, inischema.opt):
0212 converter = inischema.optconverter.get_converter(
0213 key, converter_func=converter)
0214 return converter
0215
0216 def convert(self, key, converter):
0217 converter = self._make_converter(key, converter)
0218 try:
0219 return converter.convert(None, self[key])
0220 except inischema.ParseValueError, e:
0221 item = self.raw_get(key)[0][0]
0222 message = ('Error in %s (section [%s]), line %i (%r):\n%s'
0223 % (item.filename, item.section.name, item.lineno,
0224 self[key], e))
0225 raise ValueError(message)
0226
0227 def convertlist(self, key, converter, add_inherited=True):
0228 converter = self._make_converter(key, converter)
0229 value_list = self.getlist(key, add_inherited=add_inherited)
0230 new_list = []
0231 try:
0232 for value in value_list:
0233 new_list.append(converter.convert(value))
0234 except inischema.ParseValueError, e:
0235 pass