diff --git a/tests/test_duplicate_config.py b/tests/test_duplicate_config.py index 6a4e652..980c3c9 100644 --- a/tests/test_duplicate_config.py +++ b/tests/test_duplicate_config.py @@ -4,9 +4,8 @@ do_autopath() from py.test import raises from tiramisu.setting import groups -from tiramisu import Config, MetaConfig -from tiramisu import ChoiceOption, BoolOption, IntOption, \ - StrOption, OptionDescription +from tiramisu import Config, MetaConfig, ChoiceOption, BoolOption, IntOption, \ + StrOption, OptionDescription, groups from .test_state import _diff_opts, _diff_conf from tiramisu.storage import list_sessions @@ -37,15 +36,12 @@ def make_description(): leader = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) interface1 = OptionDescription('interface1', '', [leader]) - interface1.impl_set_group_type(groups.family) general = OptionDescription('general', '', [numero_etab, nom_machine, nombre_interfaces, activer_proxy_client, mode_conteneur_actif, mode_conteneur_actif2, adresse_serveur_ntp, time_zone, wantref_option]) - general.impl_set_group_type(groups.family) new = OptionDescription('new', '', [], properties=('hidden',)) - new.impl_set_group_type(groups.family) creole = OptionDescription('creole', 'first tiramisu configuration', [general, interface1, new]) descr = OptionDescription('baseconfig', 'baseconifgdescr', [creole]) return descr diff --git a/tests/test_leadership.py b/tests/test_leadership.py index 6070321..77da7a6 100644 --- a/tests/test_leadership.py +++ b/tests/test_leadership.py @@ -11,6 +11,9 @@ from tiramisu.error import LeadershipError, PropertiesOptionError, APIError, Con from tiramisu.storage import list_sessions +groups.family = groups.GroupType('family') + + def teardown_function(function): assert list_sessions() == [], 'session list is not empty when leaving "{}"'.format(function.__name__) diff --git a/tests/test_option.py b/tests/test_option.py index 759f728..7377256 100644 --- a/tests/test_option.py +++ b/tests/test_option.py @@ -7,11 +7,13 @@ do_autopath() from py.test import raises from tiramisu.error import APIError, ConfigError -from tiramisu import IntOption, SymLinkOption, OptionDescription, Config, Calculation -from tiramisu.setting import groups -from tiramisu.storage import list_sessions +from tiramisu import IntOption, SymLinkOption, OptionDescription, Config, Calculation, groups, list_sessions from tiramisu.i18n import _ +try: + groups.family +except: + groups.family = groups.GroupType('family') def teardown_function(function): assert list_sessions() == [], 'session list is not empty when leaving "{}"'.format(function.__name__) diff --git a/tiramisu/__init__.py b/tiramisu/__init__.py index bfd87fc..8c84b05 100644 --- a/tiramisu/__init__.py +++ b/tiramisu/__init__.py @@ -20,7 +20,7 @@ from .option import * from .error import APIError from .api import Config, MetaConfig, GroupConfig, MixConfig from .option import __all__ as all_options -from .setting import owners, undefined +from .setting import owners, groups, undefined from .storage import default_storage, Storage, list_sessions, \ delete_session @@ -38,6 +38,8 @@ allfuncs = ['Calculation', 'Config', 'APIError', 'undefined', + 'owners', + 'groups', 'default_storage', 'Storage', 'list_sessions', diff --git a/tiramisu/config.py b/tiramisu/config.py index 5960ce1..f8fd3e4 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -27,7 +27,7 @@ from .error import PropertiesOptionError, ConfigError, ConflictError, \ LeadershipError from .option import SynDynOptionDescription, DynOptionDescription, Leadership from .option.baseoption import BaseOption, valid_name -from .setting import OptionBag, ConfigBag, Settings, undefined +from .setting import OptionBag, ConfigBag, Settings, undefined, groups from .storage import get_storages, gen_storage_id, get_default_values_storages, list_sessions, Cache from .value import Values from .i18n import _ @@ -573,6 +573,7 @@ class _CommonConfig(SubConfig): def _impl_build_all_caches(self): descr = self.cfgimpl_get_description() if not descr.impl_already_build_caches(): + descr._group_type = groups.root descr._build_cache(display_name=self._display_name) config_bag = ConfigBag(context=self) descr.impl_build_force_store_values(config_bag) diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index f746cb2..bf7a864 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -222,9 +222,14 @@ class OptionDescriptionWalk(CacheOptionDescription): if name == cname + suffix: return child.to_dynoption(subpath, suffix) - raise AttributeError(_('unknown option "{0}" ' - 'in optiondescription "{1}"' - '').format(name, self.impl_getname())) + if self.impl_get_group_type() == groups.root: + raise AttributeError(_('unknown option "{0}" ' + 'in root optiondescription' + '').format(name)) + else: + raise AttributeError(_('unknown option "{0}" ' + 'in optiondescription "{1}"' + '').format(name, self.impl_get_display_name())) def get_children(self, config_bag: Union[ConfigBag, Undefined], @@ -329,15 +334,16 @@ class OptionDescription(OptionDescriptionWalk): :param group_type: an instance of `GroupType` or `LeadershipGroupType` that lives in `setting.groups` """ - if self._group_type != groups.default: - raise ValueError(_('cannot change group_type if already set ' - '(old {0}, new {1})').format(self._group_type, - group_type)) - if not isinstance(group_type, groups.GroupType): - raise ValueError(_('group_type: {0}' - ' not allowed').format(group_type)) - if isinstance(group_type, groups.LeadershipGroupType): - raise ConfigError('please use Leadership object instead of OptionDescription') + if __debug__: + if self._group_type != groups.default: + raise ValueError(_('cannot change group_type if already set ' + '(old {0}, new {1})').format(self._group_type, + group_type)) + if not isinstance(group_type, groups.GroupType): + raise ValueError(_('group_type: {0}' + ' not allowed').format(group_type)) + if isinstance(group_type, groups.LeadershipGroupType): + raise ConfigError('please use Leadership object instead of OptionDescription') self._group_type = group_type def impl_get_group_type(self) -> groups.GroupType: diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 20fdb98..76f3821 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -175,7 +175,8 @@ class OptionBag: for key in self.__slots__: if key == 'properties' and self.config_bag is undefined: continue - setattr(option_bag, key, getattr(self, key)) + if hasattr(self, key): + setattr(option_bag, key, getattr(self, key)) return option_bag @@ -285,6 +286,11 @@ class GroupModule(_NameSpace): """ pass + class RootGroupType(GroupType): + """root means this is the root optiondescription of whole config + """ + pass + class OwnerModule(_NameSpace): """emulates a module to manage unique owner names. @@ -321,9 +327,9 @@ groups.default = groups.DefaultGroupType('default') leader's option""" groups.leadership = groups.LeadershipGroupType('leadership') -""" groups.family - example of group, no special behavior with this group's type""" -groups.family = groups.GroupType('family') +""" groups.root + this group is the root optiondescription of whole config""" +groups.root = groups.RootGroupType('root') # ____________________________________________________________