add demoting_error_warning properties

This commit is contained in:
Emmanuel Garette 2018-12-24 09:30:58 +01:00
parent f9b9ccacf1
commit ca4d5e3e97
10 changed files with 87 additions and 32 deletions

View File

@ -350,7 +350,7 @@ def test_help():
cfg.help(_display=False) cfg.help(_display=False)
cfg.config.help(_display=False) cfg.config.help(_display=False)
cfg.option.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) cfg.option('o.s').help(_display=False)

View File

@ -326,6 +326,25 @@ def test_requires_transitive():
assert frozenset(props) == frozenset(['disabled']) 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(): def test_requires_transitive_owner():
a = BoolOption('activate_service', '', True) a = BoolOption('activate_service', '', True)
b = BoolOption('activate_service_web', '', True, b = BoolOption('activate_service_web', '', True,

View File

@ -12,7 +12,7 @@
# #
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from .function import Params, ParamOption, ParamValue, ParamContext from .function import Params, ParamOption, ParamValue, ParamContext, tiramisu_copy
from .option import * from .option import *
from .error import APIError from .error import APIError
from .api import Config, MetaConfig, GroupConfig, MixConfig from .api import Config, MetaConfig, GroupConfig, MixConfig
@ -29,7 +29,8 @@ allfuncs = ['Params',
'GroupConfig', 'GroupConfig',
'Config', 'Config',
'APIError', 'APIError',
'undefined'] 'undefined',
'tiramisu_copy']
allfuncs.extend(all_options) allfuncs.extend(all_options)
del(all_options) del(all_options)
__all__ = tuple(allfuncs) __all__ = tuple(allfuncs)

View File

@ -77,6 +77,7 @@ class TiramisuHelp:
class CommonTiramisu(TiramisuHelp): class CommonTiramisu(TiramisuHelp):
_allow_optiondescription = True _allow_optiondescription = True
_validate_properties = True
def _get_option(self) -> Any: def _get_option(self) -> Any:
option = self._option_bag.option option = self._option_bag.option
@ -84,11 +85,14 @@ class CommonTiramisu(TiramisuHelp):
option = self._subconfig.cfgimpl_get_description().get_child(self._name, option = self._subconfig.cfgimpl_get_description().get_child(self._name,
self._option_bag.config_bag, self._option_bag.config_bag,
self._subconfig.cfgimpl_get_path()) self._subconfig.cfgimpl_get_path())
self._option_bag.set_option(option, option_bag = OptionBag()
option_bag.set_option(option,
self._option_bag.path, self._option_bag.path,
self._option_bag.index, self._option_bag.index,
self._option_bag.config_bag) self._option_bag.config_bag)
self._option_bag.config_bag.context.cfgimpl_get_settings().validate_properties(self._option_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 index = self._option_bag.index
if index is not None: if index is not None:
if option.impl_is_optiondescription() or not option.impl_is_master_slaves('slave'): if option.impl_is_optiondescription() or not option.impl_is_master_slaves('slave'):
@ -107,6 +111,7 @@ class CommonTiramisu(TiramisuHelp):
class CommonTiramisuOption(CommonTiramisu): class CommonTiramisuOption(CommonTiramisu):
_allow_optiondescription = False _allow_optiondescription = False
_slave_need_index = True _slave_need_index = True
_validate_properties = False
def __init__(self, def __init__(self,
name: str, name: str,
@ -141,6 +146,13 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption):
"""Get Tiramisu option""" """Get Tiramisu option"""
return self._option_bag.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): def ismasterslaves(self):
"""Test if option is a master or a slave""" """Test if option is a master or a slave"""
option = self._option_bag.option option = self._option_bag.option
@ -210,6 +222,10 @@ class _TiramisuOptionOption(_TiramisuOptionOptionDescription):
option = self._option_bag.option option = self._option_bag.option
return option.impl_is_master_slaves('slave') return option.impl_is_master_slaves('slave')
def issymlinkoption(self) -> bool:
option = self._option_bag.option
return option.impl_is_symlinkoption()
def default(self): def default(self):
"""Get default value for an option (not for optiondescription)""" """Get default value for an option (not for optiondescription)"""
option = self._option_bag.option option = self._option_bag.option
@ -254,6 +270,7 @@ class TiramisuOptionOption(CommonTiramisuOption):
class TiramisuOptionOwner(CommonTiramisuOption): class TiramisuOptionOwner(CommonTiramisuOption):
#FIXME optiondescription must not have Owner!
"""Manage option's owner""" """Manage option's owner"""
def __init__(self, def __init__(self,
@ -505,13 +522,14 @@ class _TiramisuOptionValueOptionDescription:
flatten=False, flatten=False,
withvalue=undefined, withvalue=undefined,
withoption=None, withoption=None,
withwarning: bool=False,
fullpath=False): fullpath=False):
"""Dict with path as key and value""" """Dict with path as key and value"""
self._get_option() self._get_option()
name = self._option_bag.option.impl_getname() name = self._option_bag.option.impl_getname()
subconfig = self._subconfig.get_subconfig(self._option_bag) subconfig = self._subconfig.get_subconfig(self._option_bag)
config_bag = self._option_bag.config_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 = config_bag.copy()
config_bag.remove_warnings() config_bag.remove_warnings()
return subconfig.make_dict(config_bag=config_bag, return subconfig.make_dict(config_bag=config_bag,
@ -817,11 +835,12 @@ class TiramisuContextValue(TiramisuContext):
flatten=False, flatten=False,
withvalue=undefined, withvalue=undefined,
withoption=None, withoption=None,
withwarning: bool=False,
fullpath=False): fullpath=False):
"""Dict with path as key and value""" """Dict with path as key and value"""
config_bag = self._config_bag config_bag = self._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 = self._config_bag.copy() config_bag = config_bag.copy()
config_bag.remove_warnings() config_bag.remove_warnings()
return config_bag.context.make_dict(config_bag, return config_bag.context.make_dict(config_bag,
flatten=flatten, flatten=flatten,
@ -1218,7 +1237,7 @@ class TiramisuAPI(TiramisuHelp):
return TiramisuAPI(config_bag) return TiramisuAPI(config_bag)
elif subfunc == 'unrestraint': elif subfunc == 'unrestraint':
config_bag = self._config_bag.copy() config_bag = self._config_bag.copy()
config_bag.properties = frozenset(['cache']) config_bag.unrestraint()
return TiramisuAPI(config_bag) return TiramisuAPI(config_bag)
elif subfunc == 'config': elif subfunc == 'config':
config_type = self._config_bag.context.impl_type config_type = self._config_bag.context.impl_type
@ -1235,7 +1254,6 @@ class TiramisuAPI(TiramisuHelp):
config_bag = self._config_bag config_bag = self._config_bag
del config_bag.permissives del config_bag.permissives
return self._registers[subfunc](config_bag) 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): def __dir__(self):

View File

@ -80,7 +80,7 @@ def manager_callback(callbk: Union[ParamOption, ParamValue],
if fromconsistency: if fromconsistency:
option_bag.fromconsistency = fromconsistency.copy() option_bag.fromconsistency = fromconsistency.copy()
if opt == option: if opt == option:
option_bag.config_bag.properties = frozenset() option_bag.config_bag.unrestraint()
option_bag.config_bag.remove_validation() option_bag.config_bag.remove_validation()
try: try:
# get value # get value

View File

@ -192,7 +192,7 @@ class ValueWarning(UserWarning):
class ValueErrorWarning(ValueWarning): class ValueErrorWarning(ValueWarning):
def __init__(self, def __init__(self,
value_error): value_error):
super(ValueWarning, self).__init__(value_error, value_error.opt()) super(ValueErrorWarning, self).__init__(value_error, value_error.opt)
class ValueOptionError(ValueError): class ValueOptionError(ValueError):

View File

@ -75,3 +75,7 @@ class ParamContext(Param):
class ParamIndex(Param): class ParamIndex(Param):
__slots__ = tuple() __slots__ = tuple()
def tiramisu_copy(val):
return val

View File

@ -179,6 +179,9 @@ class Option(BaseOption):
def impl_is_dynsymlinkoption(self) -> bool: def impl_is_dynsymlinkoption(self) -> bool:
return False return False
def get_display_type(self) -> str:
return self._display_name
def impl_getdefault(self) -> Any: def impl_getdefault(self) -> Any:
"accessing the default value" "accessing the default value"
is_multi = self.impl_is_multi() is_multi = self.impl_is_multi()
@ -347,7 +350,7 @@ class Option(BaseOption):
is_warnings_only) is_warnings_only)
except ValueError as err: except ValueError as err:
if config_bag is undefined or \ if config_bag is undefined or \
'demoting_error' not in config_bag.properties: 'demoting_error_warning' not in config_bag.properties:
raise ValueOptionError(val, raise ValueOptionError(val,
self._display_name, self._display_name,
option_bag.ori_option, option_bag.ori_option,
@ -638,7 +641,7 @@ class Option(BaseOption):
self._display_name, self._display_name,
current_opt.impl_get_display_name(), current_opt.impl_get_display_name(),
err) err)
warnings.warn_explicit(ValueWarning(msg, weakref.ref(self)), warnings.warn_explicit(ValueWarning(msg, weakref.ref(current_opt)),
ValueWarning, ValueWarning,
self.__class__.__name__, 0) self.__class__.__name__, 0)
else: else:

View File

@ -39,10 +39,8 @@ class StrOption(Option):
raise ValueError() raise ValueError()
#UnicodeOption is same as StrOption in python 3+ #UnicodeOption is same as StrOption
class UnicodeOption(StrOption): UnicodeOption = StrOption
__slots__ = tuple()
_display_name = _('unicode')
class RegexpOption(StrOption): class RegexpOption(StrOption):
@ -52,8 +50,7 @@ class RegexpOption(StrOption):
value: Any, value: Any,
option_bag: OptionBag, option_bag: OptionBag,
current_opt: Option=Undefined) -> None: current_opt: Option=Undefined) -> None:
if not isinstance(value, str): super()._validate(value, option_bag, current_opt)
raise ValueError(_('invalid string'))
match = self._regexp.search(value) match = self._regexp.search(value)
if not match: if not match:
raise ValueError() raise ValueError()

View File

@ -176,6 +176,7 @@ class OptionBag:
class ConfigBag: class ConfigBag:
__slots__ = ('context', # link to the current context __slots__ = ('context', # link to the current context
'properties', # properties for current context 'properties', # properties for current context
'true_properties', # properties for current context
'permissives', # permissives for current context 'permissives', # permissives for current context
) )
def __init__(self, context, **kwargs): def __init__(self, context, **kwargs):
@ -190,6 +191,8 @@ class ConfigBag:
if key == 'permissives': if key == 'permissives':
self.permissives = self.context.cfgimpl_get_settings().get_context_permissives() self.permissives = self.context.cfgimpl_get_settings().get_context_permissives()
return self.permissives return self.permissives
if key == 'true_properties':
return self.properties
raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover
def remove_warnings(self): def remove_warnings(self):
@ -198,6 +201,10 @@ class ConfigBag:
def remove_validation(self): def remove_validation(self):
self.properties = frozenset(self.properties - {'validator'}) self.properties = frozenset(self.properties - {'validator'})
def unrestraint(self):
self.true_properties = self.properties
self.properties = frozenset(['cache'])
def set_permissive(self): def set_permissive(self):
self.properties = frozenset(self.properties | {'permissive'}) self.properties = frozenset(self.properties | {'permissive'})
@ -216,8 +223,9 @@ class ConfigBag:
def copy(self): def copy(self):
kwargs = {} kwargs = {}
for key in self.__slots__: 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 hasattr(self.context, '_impl_settings'):
# not for GroupConfig
continue continue
kwargs[key] = getattr(self, key) kwargs[key] = getattr(self, key)
return ConfigBag(**kwargs) return ConfigBag(**kwargs)
@ -503,23 +511,26 @@ class Settings(object):
elif option.impl_is_multi() and option_bag.index is not None: elif option.impl_is_multi() and option_bag.index is not None:
is_indexed = True is_indexed = True
config_bag = option_bag.config_bag.copy() config_bag = option_bag.config_bag.copy()
config_bag.set_permissive()
soption_bag = OptionBag() soption_bag = OptionBag()
soption_bag.set_option(option, soption_bag.set_option(option,
reqpath, reqpath,
idx, idx,
config_bag) config_bag)
if option_bag.option == option: if option_bag.option == option:
soption_bag.config_bag.properties = frozenset() soption_bag.config_bag.unrestraint()
soption_bag.config_bag.remove_validation() soption_bag.config_bag.remove_validation()
soption_bag.apply_requires = False soption_bag.apply_requires = False
else:
soption_bag.config_bag.properties = soption_bag.config_bag.true_properties
soption_bag.config_bag.set_permissive()
try: try:
value = context.getattr(reqpath, value = context.getattr(reqpath,
soption_bag) soption_bag)
if is_indexed:
value = value[option_bag.index]
except PropertiesOptionError as err: except PropertiesOptionError as err:
properties = err.proptype 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 not transitive:
if all_properties is None: if all_properties is None:
all_properties = [] all_properties = []
@ -550,6 +561,8 @@ class Settings(object):
breaked = True breaked = True
break break
else: else:
if is_indexed:
value = value[option_bag.index]
if (not inverse and value in expected or if (not inverse and value in expected or
inverse and value not in expected): inverse and value not in expected):
if operator != 'and': if operator != 'and':