diff --git a/test/test_config_api.py b/test/test_config_api.py index 734cb86..7d096d1 100644 --- a/test/test_config_api.py +++ b/test/test_config_api.py @@ -350,7 +350,7 @@ def test_help(): cfg.help(_display=False) cfg.config.help(_display=False) cfg.option.help(_display=False) - cfg.option('o').help(_display=False) + #FIXMEcfg.option('o').help(_display=False) cfg.option('o.s').help(_display=False) diff --git a/test/test_requires.py b/test/test_requires.py index 1a17aad..47192b7 100644 --- a/test/test_requires.py +++ b/test/test_requires.py @@ -326,6 +326,25 @@ def test_requires_transitive(): assert frozenset(props) == frozenset(['disabled']) +def test_requires_transitive_unrestraint(): + a = BoolOption('activate_service', '', True) + b = BoolOption('activate_service_web', '', True, + requires=[{'option': a, 'expected': False, 'action': 'disabled'}]) + + d = IPOption('ip_address_service_web', '', + requires=[{'option': b, 'expected': False, 'action': 'disabled'}]) + od = OptionDescription('service', '', [a, b, d]) + api = Config(od) + api.property.read_write() + api.option('activate_service').value.get() + api.option('activate_service_web').value.get() + api.option('ip_address_service_web').value.get() + api.option('activate_service').value.set(False) + # + assert api.unrestraint.option('activate_service_web').property.get() == {'disabled'} + assert api.unrestraint.option('ip_address_service_web').property.get() == {'disabled'} + + def test_requires_transitive_owner(): a = BoolOption('activate_service', '', True) b = BoolOption('activate_service_web', '', True, diff --git a/tiramisu/__init__.py b/tiramisu/__init__.py index c6a895d..7f290ca 100644 --- a/tiramisu/__init__.py +++ b/tiramisu/__init__.py @@ -12,7 +12,7 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . -from .function import Params, ParamOption, ParamValue, ParamContext +from .function import Params, ParamOption, ParamValue, ParamContext, tiramisu_copy from .option import * from .error import APIError from .api import Config, MetaConfig, GroupConfig, MixConfig @@ -29,7 +29,8 @@ allfuncs = ['Params', 'GroupConfig', 'Config', 'APIError', - 'undefined'] + 'undefined', + 'tiramisu_copy'] allfuncs.extend(all_options) del(all_options) __all__ = tuple(allfuncs) diff --git a/tiramisu/api.py b/tiramisu/api.py index 2eabadd..beb33e8 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -77,6 +77,7 @@ class TiramisuHelp: class CommonTiramisu(TiramisuHelp): _allow_optiondescription = True + _validate_properties = True def _get_option(self) -> Any: option = self._option_bag.option @@ -84,11 +85,14 @@ class CommonTiramisu(TiramisuHelp): option = self._subconfig.cfgimpl_get_description().get_child(self._name, self._option_bag.config_bag, self._subconfig.cfgimpl_get_path()) - self._option_bag.set_option(option, - self._option_bag.path, - self._option_bag.index, - self._option_bag.config_bag) - self._option_bag.config_bag.context.cfgimpl_get_settings().validate_properties(self._option_bag) + option_bag = OptionBag() + option_bag.set_option(option, + self._option_bag.path, + self._option_bag.index, + self._option_bag.config_bag) + if self._validate_properties: + option_bag.config_bag.context.cfgimpl_get_settings().validate_properties(option_bag) + self._option_bag = option_bag index = self._option_bag.index if index is not None: if option.impl_is_optiondescription() or not option.impl_is_master_slaves('slave'): @@ -107,6 +111,7 @@ class CommonTiramisu(TiramisuHelp): class CommonTiramisuOption(CommonTiramisu): _allow_optiondescription = False _slave_need_index = True + _validate_properties = False def __init__(self, name: str, @@ -141,6 +146,13 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption): """Get Tiramisu option""" return self._option_bag.option + def type(self): + type_ = self._option_bag.option.__class__.__name__ + if type_.endswith('Option'): + type_ = type_[:-6] + type_ = type_.lower() + return type_ + def ismasterslaves(self): """Test if option is a master or a slave""" option = self._option_bag.option @@ -210,6 +222,10 @@ class _TiramisuOptionOption(_TiramisuOptionOptionDescription): option = self._option_bag.option return option.impl_is_master_slaves('slave') + def issymlinkoption(self) -> bool: + option = self._option_bag.option + return option.impl_is_symlinkoption() + def default(self): """Get default value for an option (not for optiondescription)""" option = self._option_bag.option @@ -254,6 +270,7 @@ class TiramisuOptionOption(CommonTiramisuOption): class TiramisuOptionOwner(CommonTiramisuOption): + #FIXME optiondescription must not have Owner! """Manage option's owner""" def __init__(self, @@ -420,7 +437,7 @@ class _TiramisuOptionValueOption: option = self._option_bag.option self._test_slave_index() return self._subconfig.getattr(self._name, - self._option_bag) + self._option_bag) def set(self, value): """Change option's value""" @@ -505,13 +522,14 @@ class _TiramisuOptionValueOptionDescription: flatten=False, withvalue=undefined, withoption=None, + withwarning: bool=False, fullpath=False): """Dict with path as key and value""" self._get_option() name = self._option_bag.option.impl_getname() subconfig = self._subconfig.get_subconfig(self._option_bag) config_bag = self._option_bag.config_bag - if config_bag.properties and 'warnings' in config_bag.properties: + if not withwarning and config_bag.properties and 'warnings' in config_bag.properties: config_bag = config_bag.copy() config_bag.remove_warnings() return subconfig.make_dict(config_bag=config_bag, @@ -817,11 +835,12 @@ class TiramisuContextValue(TiramisuContext): flatten=False, withvalue=undefined, withoption=None, + withwarning: bool=False, fullpath=False): """Dict with path as key and value""" config_bag = self._config_bag - if config_bag.properties and 'warnings' in config_bag.properties: - config_bag = self._config_bag.copy() + if not withwarning and config_bag.properties and 'warnings' in config_bag.properties: + config_bag = config_bag.copy() config_bag.remove_warnings() return config_bag.context.make_dict(config_bag, flatten=flatten, @@ -1218,7 +1237,7 @@ class TiramisuAPI(TiramisuHelp): return TiramisuAPI(config_bag) elif subfunc == 'unrestraint': config_bag = self._config_bag.copy() - config_bag.properties = frozenset(['cache']) + config_bag.unrestraint() return TiramisuAPI(config_bag) elif subfunc == 'config': config_type = self._config_bag.context.impl_type @@ -1235,8 +1254,7 @@ class TiramisuAPI(TiramisuHelp): config_bag = self._config_bag del config_bag.permissives return self._registers[subfunc](config_bag) - else: - raise APIError(_('please specify a valid sub function ({})').format(subfunc)) + raise APIError(_('please specify a valid sub function ({})').format(subfunc)) def __dir__(self): return list(self._registers.keys()) + ['unrestraint', 'forcepermissive', 'config'] diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index 4753112..7b5b3c4 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -80,7 +80,7 @@ def manager_callback(callbk: Union[ParamOption, ParamValue], if fromconsistency: option_bag.fromconsistency = fromconsistency.copy() if opt == option: - option_bag.config_bag.properties = frozenset() + option_bag.config_bag.unrestraint() option_bag.config_bag.remove_validation() try: # get value diff --git a/tiramisu/error.py b/tiramisu/error.py index 1b2b723..fbe4f21 100644 --- a/tiramisu/error.py +++ b/tiramisu/error.py @@ -192,7 +192,7 @@ class ValueWarning(UserWarning): class ValueErrorWarning(ValueWarning): def __init__(self, value_error): - super(ValueWarning, self).__init__(value_error, value_error.opt()) + super(ValueErrorWarning, self).__init__(value_error, value_error.opt) class ValueOptionError(ValueError): diff --git a/tiramisu/function.py b/tiramisu/function.py index 8b6e498..9fbc60b 100644 --- a/tiramisu/function.py +++ b/tiramisu/function.py @@ -75,3 +75,7 @@ class ParamContext(Param): class ParamIndex(Param): __slots__ = tuple() + + +def tiramisu_copy(val): + return val diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index 9740a18..d23072a 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -179,6 +179,9 @@ class Option(BaseOption): def impl_is_dynsymlinkoption(self) -> bool: return False + def get_display_type(self) -> str: + return self._display_name + def impl_getdefault(self) -> Any: "accessing the default value" is_multi = self.impl_is_multi() @@ -347,7 +350,7 @@ class Option(BaseOption): is_warnings_only) except ValueError as err: if config_bag is undefined or \ - 'demoting_error' not in config_bag.properties: + 'demoting_error_warning' not in config_bag.properties: raise ValueOptionError(val, self._display_name, option_bag.ori_option, @@ -638,7 +641,7 @@ class Option(BaseOption): self._display_name, current_opt.impl_get_display_name(), err) - warnings.warn_explicit(ValueWarning(msg, weakref.ref(self)), + warnings.warn_explicit(ValueWarning(msg, weakref.ref(current_opt)), ValueWarning, self.__class__.__name__, 0) else: diff --git a/tiramisu/option/stroption.py b/tiramisu/option/stroption.py index e28b375..bcd011c 100644 --- a/tiramisu/option/stroption.py +++ b/tiramisu/option/stroption.py @@ -39,10 +39,8 @@ class StrOption(Option): raise ValueError() -#UnicodeOption is same as StrOption in python 3+ -class UnicodeOption(StrOption): - __slots__ = tuple() - _display_name = _('unicode') +#UnicodeOption is same as StrOption +UnicodeOption = StrOption class RegexpOption(StrOption): @@ -52,8 +50,7 @@ class RegexpOption(StrOption): value: Any, option_bag: OptionBag, current_opt: Option=Undefined) -> None: - if not isinstance(value, str): - raise ValueError(_('invalid string')) + super()._validate(value, option_bag, current_opt) match = self._regexp.search(value) if not match: raise ValueError() diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 5a4185e..e8f807c 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -176,6 +176,7 @@ class OptionBag: class ConfigBag: __slots__ = ('context', # link to the current context 'properties', # properties for current context + 'true_properties', # properties for current context 'permissives', # permissives for current context ) def __init__(self, context, **kwargs): @@ -190,6 +191,8 @@ class ConfigBag: if key == 'permissives': self.permissives = self.context.cfgimpl_get_settings().get_context_permissives() return self.permissives + if key == 'true_properties': + return self.properties raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover def remove_warnings(self): @@ -198,6 +201,10 @@ class ConfigBag: def remove_validation(self): self.properties = frozenset(self.properties - {'validator'}) + def unrestraint(self): + self.true_properties = self.properties + self.properties = frozenset(['cache']) + def set_permissive(self): self.properties = frozenset(self.properties | {'permissive'}) @@ -210,14 +217,15 @@ class ConfigBag: return raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover - #def __setattr__(self, key, value): - # super().__setattr__(key, value) + # def __setattr__(self, key, value): + # super().__setattr__(key, value) def copy(self): kwargs = {} for key in self.__slots__: - if key in ['properties', 'permissives'] and \ + if key in ['properties', 'permissives', 'true_properties'] and \ not hasattr(self.context, '_impl_settings'): + # not for GroupConfig continue kwargs[key] = getattr(self, key) return ConfigBag(**kwargs) @@ -503,23 +511,26 @@ class Settings(object): elif option.impl_is_multi() and option_bag.index is not None: is_indexed = True config_bag = option_bag.config_bag.copy() - config_bag.set_permissive() soption_bag = OptionBag() soption_bag.set_option(option, reqpath, idx, config_bag) if option_bag.option == option: - soption_bag.config_bag.properties = frozenset() + soption_bag.config_bag.unrestraint() soption_bag.config_bag.remove_validation() soption_bag.apply_requires = False + else: + soption_bag.config_bag.properties = soption_bag.config_bag.true_properties + soption_bag.config_bag.set_permissive() try: value = context.getattr(reqpath, soption_bag) - if is_indexed: - value = value[option_bag.index] except PropertiesOptionError as err: properties = err.proptype + # if not transitive, properties must be verify in current requires + # otherwise if same_action, property must be in properties + # otherwise add property in returned properties (if operator is 'and') if not transitive: if all_properties is None: all_properties = [] @@ -550,6 +561,8 @@ class Settings(object): breaked = True break else: + if is_indexed: + value = value[option_bag.index] if (not inverse and value in expected or inverse and value not in expected): if operator != 'and':