diff --git a/test/test_cache.py b/test/test_cache.py index 75f90be..3dcbc88 100644 --- a/test/test_cache.py +++ b/test/test_cache.py @@ -446,8 +446,7 @@ def test_cache_master_and_slaves_master(): compare(cfg._config.cfgimpl_get_values()._p_.get_cached(), {'val1.val1': {None: ([], None)}}) # cfg.option('val1.val1').value.set([undefined]) - compare(cfg._config.cfgimpl_get_settings()._p_.get_cached(), {None: {None: (set(['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings']), None)}, - 'val1': {None: (set([]), None)}}) + compare(cfg._config.cfgimpl_get_settings()._p_.get_cached(), {None: {None: (set(['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings']), None)}}) assert cfg._config.cfgimpl_get_values()._p_.get_cached() == {} cfg.config.dict() if TIRAMISU_VERSION == 2: @@ -467,8 +466,7 @@ def test_cache_master_and_slaves_master(): cfg.option('val1.val1').value.set([undefined, undefined]) cfg.config.dict() cfg.option('val1.val2', 1).value.set('oui') - compare(cfg._config.cfgimpl_get_settings()._p_.get_cached(), {None: {None: (set(['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings']), None)}, - 'val1': {None: (set([]), None)}}) + compare(cfg._config.cfgimpl_get_settings()._p_.get_cached(), {None: {None: (set(['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings']), None)}}) assert cfg._config.cfgimpl_get_values()._p_.get_cached() == {} if TIRAMISU_VERSION == 2: val1_val2_props = {None: (set([]), None), 0: (set([]), None), 1: (set([]), None)} @@ -520,8 +518,7 @@ def test_cache_master_callback(): 'val1.val2': {None: (val1_val2_props, None)}}) compare(cfg._config.cfgimpl_get_values()._p_.get_cached(), {'val1.val1': {None: ([], None)}}) cfg.option('val1.val1').value.set([undefined]) - compare(cfg._config.cfgimpl_get_settings()._p_.get_cached(), {None: {None: (set(['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings']), None)}, - 'val1': {None: (set([]), None)}}) + compare(cfg._config.cfgimpl_get_settings()._p_.get_cached(), {None: {None: (set(['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings']), None)}}) assert cfg._config.cfgimpl_get_values()._p_.get_cached() == {} cfg.config.dict() diff --git a/test/test_requires.py b/test/test_requires.py index 37c8872..cca6174 100644 --- a/test/test_requires.py +++ b/test/test_requires.py @@ -970,6 +970,62 @@ def test_master_slave_requires(): assert isinstance(ret['ip_admin_eth0.netmask_admin_eth0'][1], PropertiesOptionError) +def test_master_slave_requires_master(): + activate = BoolOption('activate', "Activer l'accès au réseau", True) + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True, + requires=[{'option': activate, 'expected': False, 'action': 'disabled'}]) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True) + interface1 = MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + maconfig = OptionDescription('toto', '', [activate, interface1]) + api = Config(maconfig) + api.property.read_write() + # + assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == [] + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.2']) + assert api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None + assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == ['192.168.1.2'] + # + api.option('activate').value.set(False) + raises(PropertiesOptionError, "api.option('ip_admin_eth0.ip_admin_eth0').value.get()") + raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()") + # + api.option('activate').value.set(True) + assert api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None + # + api.option('activate').value.set(False) + raises(PropertiesOptionError, "api.option('ip_admin_eth0.ip_admin_eth0').value.get()") + raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()") + assert api.config.dict() == {'activate': False} + + +def test_master_slave_requires_masterslaves(): + activate = BoolOption('activate', "Activer l'accès au réseau", True) + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True) + interface1 = MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0], + requires=[{'option': activate, 'expected': False, 'action': 'disabled'}]) + maconfig = OptionDescription('toto', '', [activate, interface1]) + api = Config(maconfig) + api.property.read_write() + # + assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == [] + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['192.168.1.2']) + assert api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None + assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == ['192.168.1.2'] + # + api.option('activate').value.set(False) + raises(PropertiesOptionError, "api.option('ip_admin_eth0.ip_admin_eth0').value.get()") + raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()") + # + api.option('activate').value.set(True) + assert api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() is None + # + api.option('activate').value.set(False) + raises(PropertiesOptionError, "api.option('ip_admin_eth0.ip_admin_eth0').value.get()") + raises(PropertiesOptionError, "api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get()") + assert api.config.dict() == {'activate': False} + + def test_master_slave_requires_no_master(): activate = BoolOption('activate', "Activer l'accès au réseau", True) ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True) diff --git a/tiramisu/config.py b/tiramisu/config.py index 7f2124e..be01ac5 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -24,9 +24,7 @@ from copy import copy from .error import PropertiesOptionError, ConfigError, ConflictError, SlaveError -from .option.syndynoptiondescription import SynDynOptionDescription -from .option.dynoptiondescription import DynOptionDescription -from .option.masterslave import MasterSlaves +from .option import SynDynOptionDescription, DynOptionDescription, MasterSlaves from .option.baseoption import BaseOption, valid_name from .setting import OptionBag, ConfigBag, groups, Settings, undefined from .storage import get_storages, get_default_values_storages diff --git a/tiramisu/option/__init__.py b/tiramisu/option/__init__.py index e22f41f..a2c295c 100644 --- a/tiramisu/option/__init__.py +++ b/tiramisu/option/__init__.py @@ -1,7 +1,7 @@ from .optiondescription import OptionDescription from .dynoptiondescription import DynOptionDescription from .syndynoptiondescription import SynDynOptionDescription -from .masterslave import MasterSlaves +from .masterslaves import MasterSlaves from .baseoption import submulti from .symlinkoption import SymLinkOption from .dynsymlinkoption import DynSymLinkOption diff --git a/tiramisu/option/baseoption.py b/tiramisu/option/baseoption.py index e540cb5..06bcf57 100644 --- a/tiramisu/option/baseoption.py +++ b/tiramisu/option/baseoption.py @@ -98,11 +98,7 @@ class Base(object): raise TypeError(_('invalid properties type {0} for {1},' ' must be a frozenset').format(type(properties), name)) - if calc_properties != frozenset([]) and properties: - set_forbidden_properties = calc_properties & properties - if set_forbidden_properties != frozenset(): - raise ValueError(_('conflict: properties already set in ' - 'requirement {0}').format(display_list(list(set_forbidden_properties)))) + self.validate_properties(name, calc_properties, properties) _setattr = object.__setattr__ _setattr(self, '_name', name) _setattr(self, '_informations', {'doc': doc}) @@ -113,6 +109,13 @@ class Base(object): if properties: _setattr(self, '_properties', properties) + def validate_properties(self, name, calc_properties, properties): + set_forbidden_properties = calc_properties & properties + if set_forbidden_properties != frozenset(): + raise ValueError(_('conflict: properties already set in requirement {0} for {1}' + '').format(display_list(list(set_forbidden_properties)), + name)) + def _get_function_args(self, function): args = set() diff --git a/tiramisu/option/ipoption.py b/tiramisu/option/ipoption.py index 087e166..0299b5b 100644 --- a/tiramisu/option/ipoption.py +++ b/tiramisu/option/ipoption.py @@ -111,7 +111,7 @@ class IPOption(Option): raise ValueError(msg.format(network, netmask, opts[1].impl_get_display_name(), - opts[2].impl_getname(), + opts[2].impl_get_display_name(), ip)) # test if ip is not network/broadcast IP opts[2]._cons_ip_netmask(current_opt, diff --git a/tiramisu/option/masterslave.py b/tiramisu/option/masterslaves.py similarity index 84% rename from tiramisu/option/masterslave.py rename to tiramisu/option/masterslaves.py index a5bedbc..b0011cb 100644 --- a/tiramisu/option/masterslave.py +++ b/tiramisu/option/masterslaves.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"master slave support" +"master slaves support" # Copyright (C) 2014-2018 Team tiramisu (see AUTHORS for all contributors) # # This program is free software: you can redistribute it and/or modify it @@ -27,7 +27,7 @@ from ..i18n import _ from ..setting import groups, undefined, OptionBag from .optiondescription import OptionDescription from .option import Option -from ..error import SlaveError, PropertiesOptionError +from ..error import SlaveError, PropertiesOptionError, RequirementError from ..function import ParamOption @@ -69,9 +69,9 @@ class MasterSlaves(OptionDescription): self.impl_get_display_name())) # no empty property for save if idx != 0: - properties = list(child._properties) - properties.remove('empty') - child._properties = frozenset(properties) + child_properties = list(child._properties) + child_properties.remove('empty') + child._properties = frozenset(child_properties) slaves.append(child) child._add_dependency(self) child._master_slaves = weakref.ref(self) @@ -82,6 +82,21 @@ class MasterSlaves(OptionDescription): if callbk.option in slaves: raise ValueError(_("callback of master's option shall " "not refered a slave's ones")) + # master should not have requires, only MasterSlaves should have + # so move requires to MasterSlaves + # if MasterSlaves has requires to, cannot manage the move so raises + master_requires = getattr(master, '_requires', None) + if master_requires: + if self.impl_getrequires(): + raise RequirementError(_('master {} in MasterSlaves {} cannot have both requirement' + '').format(master.impl_getname(), + self.impl_getname())) + master_calproperties = getattr(master, '_calc_properties', None) + if master_calproperties: + if properties is not None: + self.validate_properties(name, master_calproperties, frozenset(properties)) + setattr(self, '_calc_properties', master_calproperties) + setattr(self, '_requires', master_requires) def is_master(self, opt): master = self._children[0][0] @@ -158,18 +173,24 @@ class MasterSlaves(OptionDescription): resetted_opts): master = self.getmaster() slaves = self.getslaves() - self._reset_cache(master, + self._reset_cache(path, + master, slaves, values, settings, resetted_opts) def _reset_cache(self, + path, master, slaves, values, settings, resetted_opts): + super().reset_cache(path, + values, + settings, + resetted_opts) context = values._getcontext() mpath = master.impl_getpath(context) master.reset_cache(mpath, diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index f478cce..4828ed5 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -673,9 +673,9 @@ class Option(OnlyOption): log.debug(_('_cons_not_equal: {} are not different').format(display_list(equal))) if is_current: if warnings_only: - msg = _('should be different from the value of {}') + msg = _('should be different from the value of "{}"') else: - msg = _('must be different from the value of {}') + msg = _('must be different from the value of "{}"') else: if warnings_only: msg = _('value for {} should be different') diff --git a/tiramisu/option/syndynoptiondescription.py b/tiramisu/option/syndynoptiondescription.py index 004b2a4..f2fda71 100644 --- a/tiramisu/option/syndynoptiondescription.py +++ b/tiramisu/option/syndynoptiondescription.py @@ -103,7 +103,8 @@ class SynDynOptionDescription(object): if self.impl_get_group_type() == groups.master: master = self.getmaster() slaves = self.getslaves() - self._reset_cache(master, + self._reset_cache(path, + master, slaves, values, settings,