1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2012-2017 Team tiramisu (see AUTHORS for all contributors)
4 # This program is free software: you can redistribute it and/or modify it
5 # under the terms of the GNU Lesser General Public License as published by the
6 # Free Software Foundation, either version 3 of the License, or (at your
7 # option) any later version.
9 # This program is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14 # You should have received a copy of the GNU Lesser General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 # The original `Config` design model is unproudly borrowed from
18 # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
19 # the whole pypy projet is under MIT licence
20 # ____________________________________________________________
21 "options handler global entry point"
27 from .error import PropertiesOptionError, ConfigError, ConflictError
28 from .option import OptionDescription, Option, SymLinkOption, \
29 DynSymLinkOption, SynDynOptionDescription
30 from .option.baseoption import valid_name
31 from .setting import groups, Settings, default_encoding, undefined
32 from .storage import get_storages, get_storage, set_storage, \
33 _impl_getstate_setting, get_storages_validation
34 from .value import Values, Multi
38 if sys.version_info[0] >= 3: # pragma: optional cover
42 class SubConfig(object):
43 """Sub configuration management entry.
44 Tree if OptionDescription's responsability. SubConfig are generated
45 on-demand. A Config is also a SubConfig.
46 Root Config is call context below
48 __slots__ = ('_impl_context', '_impl_descr', '_impl_path')
50 def __init__(self, descr, context, subpath=None):
51 """ Configuration option management master class
53 :param descr: describes the configuration schema
54 :type descr: an instance of ``option.OptionDescription``
55 :param context: the current root config
56 :type context: `Config`
57 :type subpath: `str` with the path name
59 # main option description
61 if descr is not None and not isinstance(descr, OptionDescription) and \
62 not isinstance(descr, SynDynOptionDescription): # pragma: optional cover
65 raise TypeError(_('descr must be an optiondescription, not {0}'
66 ).format(type(descr)))
67 self._impl_descr = descr
68 # sub option descriptions
69 if not isinstance(context, weakref.ReferenceType): # pragma: optional cover
70 raise ValueError('context must be a Weakref')
71 self._impl_context = context
72 self._impl_path = subpath
74 def cfgimpl_reset_cache(self,
76 only=('values', 'settings'),
79 """reset all settings in cache
81 :param only_expired: if True reset only expired cached values
82 :type only_expired: boolean
84 context = self._cfgimpl_get_context()
86 values = context.cfgimpl_get_values()
87 if 'settings' in only:
88 settings = context.cfgimpl_get_settings()
91 values._p_.reset_expired_cache(int(time()))
92 if 'settings' in only:
93 settings._p_.reset_expired_cache(int(time()))
94 elif not None in (opt, path):
95 if opt.__class__.__name__ == 'DynOptionDescription':
96 descr = context.cfgimpl_get_description()
97 spath = path.split('.')
98 subpath = '.'.join(spath[:-1])
99 dynopt = getattr(descr, subpath)._getattr(spath[-1], context=context,
101 for suffix in dynopt._impl_get_suffixes(context):
102 path = subpath + '.' + spath[-1] + suffix
104 values._p_.delcache(path)
105 if 'settings' in only:
106 settings._p_.delcache(path)
107 elif not isinstance(opt, DynSymLinkOption) and opt._is_subdyn():
108 descr = context.cfgimpl_get_description()
109 spath = path.split('.')
111 subpath = '.'.join(spath[:-2])
112 dynsubopt = getattr(descr, subpath)
116 except AttributeError:
117 subpath = '.'.join(spath[:-3])
118 dynsubopt = getattr(descr, subpath)
122 dynopt = dynsubopt._getattr(spath1, context=context, dyn=False)
123 for suffix in dynopt._impl_get_suffixes(context):
124 path = subpath + '.' + spath1 + suffix + '.' + spath2 + suffix
126 path += '.' + spath3 + suffix
128 values._p_.delcache(path)
129 if 'settings' in only:
130 settings._p_.delcache(path)
133 values._p_.delcache(path)
134 if 'settings' in only:
135 settings._p_.delcache(path)
136 for option in getattr(opt, '_dependencies', []):
138 option.reset_cache(opt, values, 'values')
139 if 'settings' in only:
140 option.reset_cache(opt, settings, 'settings')
143 values._p_.reset_all_cache()
144 if 'settings' in only:
145 settings._p_.reset_all_cache()
147 def cfgimpl_get_home_by_path(self, path, force_permissive=False,
148 returns_raise=False, _setting_properties=undefined):
149 """:returns: tuple (config, name)"""
150 path = path.split('.')
151 for step in path[:-1]:
152 self = self.getattr(step,
153 force_permissive=force_permissive,
154 returns_raise=returns_raise,
155 _setting_properties=_setting_properties)
156 if isinstance(self, Exception):
158 return self, path[-1]
160 # ______________________________________________________________________
161 def __iter__(self, force_permissive=False):
162 """Pythonesque way of parsing group's ordered options.
163 iteration only on Options (not OptionDescriptions)"""
164 for child in self.cfgimpl_get_description()._impl_getchildren(
165 context=self._cfgimpl_get_context()):
166 if not child.impl_is_optiondescription():
168 name = child.impl_getname()
169 yield name, self.getattr(name,
170 force_permissive=force_permissive)
171 except GeneratorExit: # pragma: optional cover
172 if sys.version_info[0] < 3:
175 raise GeneratorExit()
176 except PropertiesOptionError: # pragma: optional cover
177 pass # option with properties
179 def iter_all(self, force_permissive=False):
180 """A way of parsing options **and** groups.
181 iteration on Options and OptionDescriptions."""
182 for child in self.cfgimpl_get_description().impl_getchildren():
184 yield child.impl_getname(), self.getattr(child.impl_getname(),
185 force_permissive=force_permissive)
186 except GeneratorExit: # pragma: optional cover
187 if sys.version_info[0] < 3:
190 raise GeneratorExit()
191 except PropertiesOptionError: # pragma: optional cover
192 pass # option with properties
194 def iter_groups(self, group_type=None, force_permissive=False):
195 """iteration on groups objects only.
196 All groups are returned if `group_type` is `None`, otherwise the groups
197 can be filtered by categories (families, or whatever).
199 :param group_type: if defined, is an instance of `groups.GroupType`
200 or `groups.MasterGroupType` that lives in
203 if group_type is not None and not isinstance(group_type,
204 groups.GroupType): # pragma: optional cover
205 raise TypeError(_("unknown group_type: {0}").format(group_type))
206 for child in self.cfgimpl_get_description()._impl_getchildren(
207 context=self._cfgimpl_get_context()):
208 if child.impl_is_optiondescription():
210 if group_type is None or (group_type is not None and
211 child.impl_get_group_type()
213 name = child.impl_getname()
214 yield name, self.getattr(name, force_permissive=force_permissive)
215 except GeneratorExit: # pragma: optional cover
216 if sys.version_info[0] < 3:
219 raise GeneratorExit()
220 except PropertiesOptionError: # pragma: optional cover
222 # ______________________________________________________________________
225 "Config's string representation"
227 for name, grp in self.iter_groups():
228 lines.append("[{0}]".format(name))
229 for name, value in self:
231 lines.append("{0} = {1}".format(name, value))
232 except UnicodeEncodeError: # pragma: optional cover
233 lines.append("{0} = {1}".format(name,
234 value.encode(default_encoding)))
235 return '\n'.join(lines)
239 def _cfgimpl_get_context(self):
240 """context could be None, we need to test it
241 context is None only if all reference to `Config` object is deleted
242 (for example we delete a `Config` and we manipulate a reference to
243 old `SubConfig`, `Values`, `Multi` or `Settings`)
245 context = self._impl_context()
246 if context is None: # pragma: optional cover
247 raise ConfigError(_('the context does not exist anymore'))
250 def cfgimpl_get_context(self):
251 return self._cfgimpl_get_context()
253 def cfgimpl_get_description(self):
254 if self._impl_descr is None: # pragma: optional cover
255 raise ConfigError(_('no option description found for this config'
256 ' (may be GroupConfig)'))
258 return self._impl_descr
260 def cfgimpl_get_settings(self):
261 return self._cfgimpl_get_context()._impl_settings
263 def cfgimpl_get_values(self):
264 return self._cfgimpl_get_context()._impl_values
266 # ____________________________________________________________
268 def __setattr__(self, name, value):
269 "attribute notation mechanism for the setting of the value of an option"
270 self.setattr(name, value)
272 def _setattr(self, name, value, force_permissive=False, not_raises=False):
273 """use setattr instead of _setattr
275 self.setattr(name, value, force_permissive=force_permissive,
276 not_raises=not_raises)
278 def setattr(self, name, value, force_permissive=False, not_raises=False, index=None,
279 _setting_properties=undefined):
280 if name.startswith('_impl_'):
281 return object.__setattr__(self, name, value)
282 context = self._cfgimpl_get_context()
283 if _setting_properties is undefined:
284 _setting_properties = context.cfgimpl_get_settings()._getproperties(read_write=True)
285 if '.' in name: # pragma: optional cover
286 homeconfig, name = self.cfgimpl_get_home_by_path(name,
287 force_permissive=force_permissive,
288 _setting_properties=_setting_properties)
289 return homeconfig.setattr(name, value, force_permissive,
290 not_raises, index=index,
291 _setting_properties=_setting_properties)
292 child = self.cfgimpl_get_description().__getattr__(name,
294 if isinstance(child, OptionDescription) or isinstance(child, SynDynOptionDescription):
295 raise TypeError(_("can't assign to an OptionDescription")) # pragma: optional cover
296 elif isinstance(child, SymLinkOption) and \
297 not isinstance(child, DynSymLinkOption): # pragma: no dynoptiondescription cover
298 path = context.cfgimpl_get_description().impl_get_path_by_opt(
299 child._impl_getopt())
300 context.setattr(path, value, force_permissive, not_raises, index=index,
301 _setting_properties=_setting_properties)
303 subpath = self._get_subpath(name)
304 self.cfgimpl_get_values().setitem(child, value, subpath,
305 force_permissive=force_permissive,
306 not_raises=not_raises, index=index,
307 _setting_properties=_setting_properties)
309 def __delattr__(self, name):
310 context = self._cfgimpl_get_context()
311 child = self.cfgimpl_get_description().__getattr__(name, context)
312 self.cfgimpl_get_values().__delitem__(child)
314 def __getattr__(self, name):
315 return self.getattr(name)
317 def _getattr(self, name, force_permissive=False, validate=True): # pragma: optional cover
318 """use getattr instead of _getattr
320 return self.getattr(name, force_permissive, validate)
322 def _get_subpath(self, name):
323 if self._impl_path is None:
326 subpath = self._impl_path + '.' + name
329 def getattr(self, name, force_permissive=False, validate=True,
330 _setting_properties=undefined, _self_properties=undefined, index=None,
331 returns_raise=False):
333 attribute notation mechanism for accessing the value of an option
334 :param name: attribute name
335 :return: option's value if name is an option name, OptionDescription
338 # attribute access by passing a path,
339 # for instance getattr(self, "creole.general.family.adresse_ip_eth0")
340 context = self._cfgimpl_get_context()
341 if _setting_properties is undefined:
342 _setting_properties = context.cfgimpl_get_settings()._getproperties(read_write=True)
344 homeconfig, name = self.cfgimpl_get_home_by_path(
345 name, force_permissive=force_permissive,
346 returns_raise=returns_raise, _setting_properties=_setting_properties)
347 if isinstance(homeconfig, Exception):
350 cfg = homeconfig.getattr(name, force_permissive=force_permissive,
352 _setting_properties=_setting_properties,
353 _self_properties=_self_properties,
354 index=index, returns_raise=returns_raise)
356 option = self.cfgimpl_get_description().__getattr__(name,
358 subpath = self._get_subpath(name)
359 if isinstance(option, DynSymLinkOption):
360 cfg = self.cfgimpl_get_values()._get_cached_value(
361 option, path=subpath,
363 force_permissive=force_permissive,
364 setting_properties=_setting_properties,
365 self_properties=_self_properties,
367 elif isinstance(option, SymLinkOption): # pragma: no dynoptiondescription cover
368 path = context.cfgimpl_get_description().impl_get_path_by_opt(
369 option._impl_getopt())
370 cfg = context.getattr(path, validate=validate,
371 force_permissive=force_permissive,
372 _setting_properties=_setting_properties,
373 _self_properties=_self_properties,
374 index=index, returns_raise=True)
375 elif option.impl_is_optiondescription():
376 props = self.cfgimpl_get_settings().validate_properties(
377 option, True, False, path=subpath,
378 force_permissive=force_permissive,
379 self_properties=_self_properties,
380 setting_properties=_setting_properties)
386 return SubConfig(option, self._impl_context, subpath)
388 cfg = self.cfgimpl_get_values()._get_cached_value(
389 option, path=subpath,
391 force_permissive=force_permissive,
392 setting_properties=_setting_properties,
393 self_properties=_self_properties,
395 if not returns_raise and isinstance(cfg, Exception):
399 def find(self, bytype=None, byname=None, byvalue=undefined, type_='option',
400 check_properties=True, force_permissive=False):
402 finds a list of options recursively in the config
404 :param bytype: Option class (BoolOption, StrOption, ...)
405 :param byname: filter by Option.impl_getname()
406 :param byvalue: filter by the option's value
407 :returns: list of matching Option objects
409 return self._cfgimpl_get_context()._find(bytype, byname, byvalue,
412 _subpath=self.cfgimpl_get_path(False),
413 check_properties=check_properties,
414 force_permissive=force_permissive)
416 def find_first(self, bytype=None, byname=None, byvalue=undefined,
417 type_='option', raise_if_not_found=True, check_properties=True,
418 force_permissive=False):
420 finds an option recursively in the config
422 :param bytype: Option class (BoolOption, StrOption, ...)
423 :param byname: filter by Option.impl_getname()
424 :param byvalue: filter by the option's value
425 :returns: list of matching Option objects
427 return self._cfgimpl_get_context()._find(
428 bytype, byname, byvalue, first=True, type_=type_,
429 _subpath=self.cfgimpl_get_path(False), raise_if_not_found=raise_if_not_found,
430 check_properties=check_properties,
431 force_permissive=force_permissive)
433 def _find(self, bytype, byname, byvalue, first, type_='option',
434 _subpath=None, check_properties=True, raise_if_not_found=True,
435 force_permissive=False, only_path=undefined,
436 only_option=undefined, setting_properties=undefined):
438 convenience method for finding an option that lives only in the subtree
440 :param first: return only one option if True, a list otherwise
441 :return: find list or an exception if nothing has been found
444 def _filter_by_value():
445 if byvalue is undefined:
447 value = self.getattr(path, force_permissive=force_permissive,
448 _setting_properties=setting_properties,
450 if isinstance(value, Exception):
451 if isinstance(value, PropertiesOptionError):
454 elif isinstance(value, Multi):
455 return byvalue in value
457 return value == byvalue
459 if type_ not in ('option', 'path', 'value'): # pragma: optional cover
460 raise ValueError(_('unknown type_ type {0}'
461 'for _find').format(type_))
463 # if value and/or check_properties are set, need all avalaible option
464 # If first one has no good value or not good property check second one
466 only_first = first is True and byvalue is None and \
467 check_properties is None
468 if only_path is not undefined:
469 options = [(only_path, only_option)]
471 options = self.cfgimpl_get_description().impl_get_options_paths(
472 bytype, byname, _subpath, only_first,
473 self._cfgimpl_get_context())
474 for path, option in options:
475 if not _filter_by_value():
477 #remove option with propertyerror, ...
478 if byvalue is undefined and check_properties:
479 value = self.getattr(path,
480 force_permissive=force_permissive,
481 _setting_properties=setting_properties,
483 if isinstance(value, Exception):
484 if isinstance(value, PropertiesOptionError):
490 elif type_ == 'path':
492 elif type_ == 'option':
497 find_results.append(retval)
498 return self._find_return_results(find_results, raise_if_not_found)
500 def _find_return_results(self, find_results, raise_if_not_found):
501 if find_results == []: # pragma: optional cover
502 if raise_if_not_found:
503 raise AttributeError(_("no option found in config"
504 " with these criteria"))
508 def make_dict(self, flatten=False, _currpath=None, withoption=None,
509 withvalue=undefined, force_permissive=False,
510 setting_properties=undefined, fullpath=False):
511 """exports the whole config into a `dict`, for example:
513 >>> print cfg.make_dict()
514 {'od2.var4': None, 'od2.var5': None, 'od2.var6': None}
518 :param flatten: returns a dict(name=value) instead of a dict(path=value)
521 >>> print cfg.make_dict(flatten=True)
522 {'var5': None, 'var4': None, 'var6': None}
524 :param withoption: returns the options that are present in the very same
525 `OptionDescription` than the `withoption` itself::
527 >>> print cfg.make_dict(withoption='var1')
528 {'od2.var4': None, 'od2.var5': None,
530 'od2.var1': u'value',
535 :param withvalue: returns the options that have the value `withvalue`
538 >>> print c.make_dict(withoption='var1',
543 'od2.var1': u'value'}
545 :returns: dict of Option's name (or path) and values
548 if _currpath is None:
550 if withoption is None and withvalue is not undefined: # pragma: optional cover
551 raise ValueError(_("make_dict can't filtering with value without "
553 if setting_properties is undefined:
554 setting_properties = self.cfgimpl_get_settings()._getproperties(
556 if withoption is not None:
557 context = self._cfgimpl_get_context()
558 for path in context._find(bytype=None, byname=withoption,
559 byvalue=withvalue, first=False,
560 type_='path', _subpath=self.cfgimpl_get_path(False),
561 force_permissive=force_permissive,
562 setting_properties=setting_properties):
563 path = '.'.join(path.split('.')[:-1])
564 opt = context.unwrap_from_path(path, force_permissive=True)
565 mypath = self.cfgimpl_get_path()
566 if mypath is not None:
569 withvalue = undefined
572 tmypath = mypath + '.'
573 if not path.startswith(tmypath): # pragma: optional cover
574 raise AttributeError(_('unexpected path {0}, '
575 'should start with {1}'
576 '').format(path, mypath))
577 path = path[len(tmypath):]
578 self._make_sub_dict(opt, path, pathsvalues, _currpath, flatten,
579 force_permissive=force_permissive,
580 setting_properties=setting_properties,
582 #withoption can be set to None below !
583 if withoption is None:
584 for opt in self.cfgimpl_get_description().impl_getchildren():
585 path = opt.impl_getname()
586 self._make_sub_dict(opt, path, pathsvalues, _currpath, flatten,
587 force_permissive=force_permissive,
588 setting_properties=setting_properties,
591 options = dict(pathsvalues)
595 def _make_sub_dict(self, opt, path, pathsvalues, _currpath, flatten,
596 setting_properties, force_permissive=False, fullpath=False):
597 value = self.getattr(path,
598 force_permissive=force_permissive,
599 _setting_properties=setting_properties,
601 if isinstance(value, Exception):
602 if not isinstance(value, PropertiesOptionError):
605 if opt.impl_is_optiondescription():
606 pathsvalues += value.make_dict(flatten,
607 _currpath + path.split('.'),
608 force_permissive=force_permissive,
609 setting_properties=setting_properties,
613 name = opt.impl_getname()
616 root_path = self.cfgimpl_get_path()
617 if root_path is None:
618 name = opt.impl_getname()
620 name = '.'.join([root_path, opt.impl_getname()])
622 name = '.'.join(_currpath + [opt.impl_getname()])
623 pathsvalues.append((name, value))
625 def cfgimpl_get_path(self, dyn=True):
626 descr = self.cfgimpl_get_description()
627 if not dyn and descr.impl_is_dynoptiondescription():
628 context_descr = self._cfgimpl_get_context().cfgimpl_get_description()
629 return context_descr.impl_get_path_by_opt(descr._impl_getopt())
630 return self._impl_path
633 class _CommonConfig(SubConfig):
634 "abstract base class for the Config, GroupConfig and the MetaConfig"
635 __slots__ = ('_impl_values', '_impl_settings', '_impl_meta', '_impl_test')
637 def _impl_build_all_caches(self, force_store_values):
638 descr = self.cfgimpl_get_description()
639 if not descr.impl_already_build_caches():
640 descr.impl_build_cache_option()
641 descr.impl_build_cache(self)
642 descr.impl_build_force_store_values(self, force_store_values)
645 "read only is a global config's setting, see `settings.py`"
646 self.cfgimpl_get_settings().read_only()
648 def read_write(self):
649 "read write is a global config's setting, see `settings.py`"
650 self.cfgimpl_get_settings().read_write()
652 def getowner(self, opt, index=None, force_permissive=False):
653 """convenience method to retrieve an option's owner
654 from the config itself
656 if not isinstance(opt, Option) and \
657 not isinstance(opt, SymLinkOption) and \
658 not isinstance(opt, DynSymLinkOption): # pragma: optional cover
659 raise TypeError(_('opt in getowner must be an option not {0}'
660 '').format(type(opt)))
661 return self.cfgimpl_get_values().getowner(opt, index=index,
662 force_permissive=force_permissive)
664 def unwrap_from_path(self, path, force_permissive=False):
665 """convenience method to extract and Option() object from the Config()
666 and it is **fast**: finds the option directly in the appropriate
671 context = self._cfgimpl_get_context()
673 homeconfig, path = self.cfgimpl_get_home_by_path(
674 path, force_permissive=force_permissive)
675 return homeconfig.cfgimpl_get_description().__getattr__(path, context=context)
676 return self.cfgimpl_get_description().__getattr__(path, context=context)
678 def cfgimpl_get_path(self, dyn=True):
681 def cfgimpl_get_meta(self):
682 if self._impl_meta is not None:
683 return self._impl_meta()
686 def impl_set_information(self, key, value):
687 """updates the information's attribute
689 :param key: information's key (ex: "help", "doc"
690 :param value: information's value (ex: "the help string")
692 self._impl_values.set_information(key, value)
694 def impl_get_information(self, key, default=undefined):
695 """retrieves one information's item
697 :param key: the item string (ex: "help")
699 return self._impl_values.get_information(key, default)
701 def impl_del_information(self, key, raises=True):
702 self._impl_values.del_information(key, raises)
705 def __getstate__(self):
706 if self._impl_meta is not None:
707 raise ConfigError(_('cannot serialize Config with MetaConfig')) # pragma: optional cover
709 for subclass in self.__class__.__mro__:
710 if subclass is not object:
711 slots.update(subclass.__slots__)
712 slots -= frozenset(['_impl_context', '_impl_meta', '__weakref__'])
716 state[slot] = getattr(self, slot)
717 except AttributeError: # pragma: optional cover
719 storage = self._impl_values._p_._storage
720 if not storage.serializable:
721 raise ConfigError(_('this storage is not serialisable, could be a '
722 'none persistent storage')) # pragma: optional cover
723 state['_storage'] = {'session_id': storage.session_id,
724 'persistent': storage.persistent}
725 state['_impl_setting'] = _impl_getstate_setting()
728 def __setstate__(self, state):
729 for key, value in state.items():
730 if key not in ['_storage', '_impl_setting']:
731 setattr(self, key, value)
732 set_storage('config', **state['_impl_setting'])
733 self._impl_context = weakref.ref(self)
734 self._impl_settings.context = weakref.ref(self)
735 self._impl_values.context = weakref.ref(self)
736 storage = get_storage('config', test=self._impl_test, **state['_storage'])
737 self._impl_values._impl_setstate(storage)
738 self._impl_settings._impl_setstate(storage)
739 self._impl_meta = None
741 def _gen_fake_values(self, session):
742 fake_config = Config(self._impl_descr, persistent=False,
743 force_values=get_storages_validation(),
744 force_settings=self.cfgimpl_get_settings())
745 fake_config.cfgimpl_get_values()._p_.importation(self.cfgimpl_get_values()._p_.exportation(session, fake=True))
749 config = Config(self._impl_descr, _duplicate=True)
750 session = self.cfgimpl_get_values()._p_.getsession()
751 config.cfgimpl_get_values()._p_.importation(self.cfgimpl_get_values()._p_.exportation(
753 config.cfgimpl_get_settings()._p_.set_modified_properties(self.cfgimpl_get_settings(
754 )._p_.get_modified_properties())
755 config.cfgimpl_get_settings()._p_.set_modified_permissives(self.cfgimpl_get_settings(
756 )._p_.get_modified_permissives())
760 # ____________________________________________________________
761 class Config(_CommonConfig):
762 "main configuration management entry"
763 __slots__ = ('__weakref__', '_impl_test', '_impl_name')
765 def __init__(self, descr, session_id=None, persistent=False,
766 name=undefined, force_values=None, force_settings=None,
767 _duplicate=False, mandatory_name=False, _force_store_values=True):
768 """ Configuration option management master class
770 :param descr: describes the configuration schema
771 :type descr: an instance of ``option.OptionDescription``
772 :param context: the current root config
773 :type context: `Config`
774 :param session_id: session ID is import with persistent Config to
775 retrieve good session
776 :type session_id: `str`
777 :param persistent: if persistent, don't delete storage when leaving
778 :type persistent: `boolean`
780 if force_settings is not None and force_values is not None:
781 self._impl_settings = force_settings
782 self._impl_values = Values(self, force_values)
784 settings, values = get_storages(self, session_id, persistent)
785 if name is undefined:
787 if session_id is not None:
789 if mandatory_name and name is None:
790 raise ValueError(_("name is mandatory for the config").format(name))
791 if name is not None and not valid_name(name): # pragma: optional cover
792 raise ValueError(_("invalid name: {0} for config").format(name))
793 self._impl_settings = Settings(self, settings)
794 self._impl_values = Values(self, values)
795 super(Config, self).__init__(descr, weakref.ref(self))
796 self._impl_meta = None
797 #undocumented option used only in test script
798 self._impl_test = False
799 if _duplicate is False and (force_settings is None or force_values is None):
800 self._impl_build_all_caches(_force_store_values)
801 self._impl_name = name
803 def impl_getname(self):
804 return self._impl_name
806 def impl_getsessionid(self):
807 return self._impl_values._p_._storage.session_id
810 class GroupConfig(_CommonConfig):
811 __slots__ = ('__weakref__', '_impl_children', '_impl_name')
813 def __init__(self, children, session_id=None, persistent=False,
814 _descr=None, name=undefined):
815 if not isinstance(children, list):
816 raise ValueError(_("groupconfig's children must be a list"))
818 for child in children:
819 if not isinstance(child, _CommonConfig):
820 raise ValueError(_("groupconfig's children must be Config, MetaConfig or GroupConfig"))
821 name_ = child._impl_name
823 raise ValueError(_('name must be set to config before creating groupconfig'))
825 if len(names) != len(set(names)):
826 for idx in xrange(1, len(names) + 1):
829 raise ConflictError(_('config name must be uniq in '
830 'groupconfig for {0}').format(name))
831 self._impl_children = children
832 settings, values = get_storages(self, session_id, persistent)
833 self._impl_settings = Settings(self, settings)
834 self._impl_values = Values(self, values)
835 super(GroupConfig, self).__init__(_descr, weakref.ref(self))
836 self._impl_meta = None
837 #undocumented option used only in test script
838 self._impl_test = False
839 if name is undefined:
841 self._impl_name = name
843 def cfgimpl_get_children(self):
844 return self._impl_children
846 def cfgimpl_reset_cache(self,
848 only=('values', 'settings'),
851 if isinstance(self, MetaConfig):
852 super(GroupConfig, self).cfgimpl_reset_cache(only_expired=only_expired, only=only, opt=opt, path=path)
853 for child in self._impl_children:
854 child.cfgimpl_reset_cache(only_expired=only_expired, only=only, opt=opt, path=path)
856 def set_value(self, path, value):
857 """Setattr not in current GroupConfig, but in each children
859 for child in self._impl_children:
860 if isinstance(child, MetaConfig):
861 child.set_value(path, value, only_config=True)
862 elif isinstance(child, GroupConfig):
863 child.set_value(path, value)
865 child.setattr(path, value, not_raises=True)
867 def find_firsts(self, byname=None, bypath=undefined, byoption=undefined,
868 byvalue=undefined, raise_if_not_found=True, _sub=False,
869 check_properties=True):
870 """Find first not in current GroupConfig, but in each children
874 #if MetaConfig, all children have same OptionDescription in context
875 #so search only one time the option for all children
876 if bypath is undefined and byname is not None and \
877 isinstance(self, MetaConfig):
878 bypath = self._find(bytype=None, byvalue=undefined, byname=byname,
879 first=True, type_='path',
880 check_properties=None,
881 raise_if_not_found=raise_if_not_found)
883 byoption = self.cfgimpl_get_description(
884 ).impl_get_opt_by_path(bypath)
886 for child in self._impl_children:
887 if isinstance(child, GroupConfig):
888 ret.extend(child.find_firsts(byname=byname, bypath=bypath,
891 check_properties=check_properties,
892 raise_if_not_found=False,
894 elif child._find(None, byname, byvalue, first=True,
895 type_='path', raise_if_not_found=False,
896 check_properties=check_properties,
897 only_path=bypath, only_option=byoption):
902 return GroupConfig(self._find_return_results(ret, raise_if_not_found))
905 return object.__repr__(self)
909 for child in self._impl_children:
910 ret += '({0})\n'.format(child._impl_name)
911 ret += super(GroupConfig, self).__str__()
914 def getattr(self, name, force_permissive=False, validate=True,
915 _setting_properties=undefined, _self_properties=undefined, index=None,
916 returns_raise=False):
917 for child in self._impl_children:
918 if name == child._impl_name:
920 return super(GroupConfig, self).getattr(name, force_permissive,
922 _self_properties=_self_properties,
924 _setting_properties=_setting_properties,
925 returns_raise=returns_raise)
928 class MetaConfig(GroupConfig):
931 def __init__(self, children, session_id=None, persistent=False,
932 name=undefined, optiondescription=None):
934 if optiondescription is not None:
936 for child_session_id in children:
937 #FIXME _force_store_values doit etre a true si inexistant !
938 new_children.append(Config(optiondescription, persistent=True,
939 session_id=child_session_id, _force_store_values=False))
940 children = new_children
941 for child in children:
942 if not isinstance(child, _CommonConfig):
943 raise TypeError(_("metaconfig's children "
944 "should be config, not {0}"
945 ).format(type(child)))
946 if child.cfgimpl_get_meta() is not None:
947 raise ValueError(_("child has already a metaconfig's"))
949 descr = child.cfgimpl_get_description()
950 elif not descr is child.cfgimpl_get_description():
951 raise ValueError(_('all config in metaconfig must '
952 'have the same optiondescription'))
953 child._impl_meta = weakref.ref(self)
955 super(MetaConfig, self).__init__(children, session_id, persistent,
958 def set_value(self, path, value, force_default=False,
959 force_dont_change_value=False, force_default_if_same=False,
961 """only_config: could be set if you want modify value in all Config included in
965 if force_default or force_default_if_same or force_dont_change_value:
966 raise ValueError(_('force_default, force_default_if_same or '
967 'force_dont_change_value cannot be set with'
969 return super(MetaConfig, self).set_value(path, value)
970 if force_default or force_default_if_same or force_dont_change_value:
971 if force_default and force_dont_change_value:
972 raise ValueError(_('force_default and force_dont_change_value'
973 ' cannot be set together'))
974 opt = self.cfgimpl_get_description().impl_get_opt_by_path(path)
975 for child in self._impl_children:
976 if force_default_if_same or force_default:
977 if force_default_if_same:
978 if not child.cfgimpl_get_values()._contains(path):
979 child_value = undefined
981 child_value = child.getattr(path)
982 if force_default or value == child_value:
983 child.cfgimpl_get_values().reset(opt, path=path,
986 if force_dont_change_value:
987 child_value = child.getattr(path)
988 if value != child_value:
989 setattr(child, path, child_value)
991 setattr(self, path, value)
993 def new_config(self, session_id=None, persistent=False, name=undefined):
994 return Config(self._impl_descr, _duplicate=True, session_id=session_id, name=name,
995 persistent=persistent, mandatory_name=True)