fix: better error message

This commit is contained in:
egarette@silique.fr 2025-05-12 08:53:39 +02:00
parent c14a34f232
commit fcbb9c6812
16 changed files with 392 additions and 178 deletions

View file

@ -788,6 +788,19 @@ def test_values_with_leader_and_followers_leader_pop():
# assert not list_sessions() # assert not list_sessions()
def test_values_with_leader_and_followers_leader_pop_default_value():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", default=["192.168.230.145", "192.168.230.146"], multi=True, properties=('notunique',))
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", default_multi="255.255.255.0", multi=True)
interface1 = Leadership('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
od1 = OptionDescription('toto', '', [interface1])
cfg = Config(od1)
cfg.property.read_write()
cfg.option('ip_admin_eth0.ip_admin_eth0').value.pop(0)
compare(cfg.value.exportation(), {'ip_admin_eth0.ip_admin_eth0': {None: [['192.168.230.146'], 'user']}})
assert cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() == ["192.168.230.146"]
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() == '255.255.255.0'
def test_follower_unique(): def test_follower_unique():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=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, properties=('unique',)) netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True, properties=('unique',))

View file

@ -945,7 +945,7 @@ def test_consistency_leader_and_followers_leader_mandatory_transitive():
try: try:
cfg.option('val1.val2', 0).value.get() cfg.option('val1.val2', 0).value.get()
except PropertiesOptionError as error: except PropertiesOptionError as error:
assert str(error) == str(_('cannot access to {0} {1} because has {2} {3}').format('option', '"val2"', _('property'), '"disabled"')) assert str(error) == str(_('cannot access to {0} {1} at index "{2}" because has {3} {4}').format('option', '"val2"', 0, _('property'), '"disabled"'))
else: else:
raise Exception('must raises') raise Exception('must raises')
assert list(cfg.value.mandatory()) == [] assert list(cfg.value.mandatory()) == []

View file

@ -271,9 +271,18 @@ class _TiramisuOptionWalk:
class _TiramisuOptionOptionDescription: class _TiramisuOptionOptionDescription:
"""Manage option""" """Manage option"""
_validate_properties = False _validate_properties = False
@option_type(["optiondescription", "option", "with_or_without_index", "symlink", "allow_dynoption"]) @option_type(
[
"optiondescription",
"option",
"with_or_without_index",
"symlink",
"allow_dynoption",
]
)
def get(self): def get(self):
"""Get Tiramisu option""" """Get Tiramisu option"""
return self._subconfig.option return self._subconfig.option
@ -291,11 +300,14 @@ class _TiramisuOptionOptionDescription:
@option_type(["optiondescription", "option", "with_or_without_index", "symlink"]) @option_type(["optiondescription", "option", "with_or_without_index", "symlink"])
def description( def description(
self, self,
with_quote: bool = False,
uncalculated: bool = False, uncalculated: bool = False,
): ):
"""Get option description""" """Get option description"""
if not uncalculated: if not uncalculated:
return self._subconfig.option.impl_get_display_name(self._subconfig) return self._subconfig.option.impl_get_display_name(
self._subconfig, with_quote=with_quote
)
return self._subconfig.option._get_information( return self._subconfig.option._get_information(
self._subconfig, self._subconfig,
"doc", "doc",
@ -354,7 +366,7 @@ class _TiramisuOptionOptionDescription:
if option.impl_is_optiondescription(): if option.impl_is_optiondescription():
return "optiondescription" return "optiondescription"
if only_self and option.impl_is_symlinkoption(): if only_self and option.impl_is_symlinkoption():
return 'symlink' return "symlink"
return option.get_type() return option.get_type()
@option_type(["option", "symlink", "with_or_without_index"]) @option_type(["option", "symlink", "with_or_without_index"])
@ -808,11 +820,7 @@ class TiramisuOptionValue(CommonTiramisuOption, _TiramisuODGet):
and option.impl_is_leader() and option.impl_is_leader()
and len(value) < self._subconfig.parent.get_length_leadership() and len(value) < self._subconfig.parent.get_length_leadership()
): ):
raise LeadershipError( raise LeadershipError(self._subconfig, "leadership-reduce")
_("cannot reduce length of the leader {}" "").format(
option.impl_get_display_name(self._subconfig, with_quote=True)
)
)
values = self._config_bag.context.get_values() values = self._config_bag.context.get_values()
return values.set_value(self._subconfig, value) return values.set_value(self._subconfig, value)

View file

@ -22,7 +22,15 @@ from typing import Any, Optional, Union, Callable, Dict, List
from itertools import chain from itertools import chain
import weakref import weakref
from .error import PropertiesOptionError, ConfigError, LeadershipError, ValueWarning, CancelParam, display_list, errors from .error import (
PropertiesOptionError,
ConfigError,
LeadershipError,
ValueWarning,
CancelParam,
display_list,
errors,
)
from .i18n import _ from .i18n import _
from .setting import undefined, ConfigBag from .setting import undefined, ConfigBag
from .function import FUNCTION_WAITING_FOR_DICT, FUNCTION_WAITING_FOR_ERROR from .function import FUNCTION_WAITING_FOR_DICT, FUNCTION_WAITING_FOR_ERROR
@ -149,11 +157,15 @@ class ParamDynOption(ParamOption):
) )
if not isinstance(identifiers, list): if not isinstance(identifiers, list):
raise Exception( raise Exception(
_("identifiers in ParamDynOption must be a list, not {0}").format(identifiers) _("identifiers in ParamDynOption must be a list, not {0}").format(
identifiers
)
) )
if not isinstance(optional, bool): if not isinstance(optional, bool):
raise Exception( raise Exception(
_("optional in ParamDynOption must be a boolean, not {0}").format(optional) _("optional in ParamDynOption must be a boolean, not {0}").format(
optional
)
) )
self.identifiers = identifiers self.identifiers = identifiers
self.optional = optional self.optional = optional
@ -204,7 +216,9 @@ class ParamInformation(Param):
def set_option(self, option: "Option" = None) -> None: def set_option(self, option: "Option" = None) -> None:
if not hasattr(self, "self_option"): if not hasattr(self, "self_option"):
raise ConfigError(_("cannot add option in information after creating config")) raise ConfigError(
_("cannot add option in information after creating config")
)
if self.option: if self.option:
raise ConfigError(_("cannot redefine option in information")) raise ConfigError(_("cannot redefine option in information"))
if not option.impl_is_optiondescription(): if not option.impl_is_optiondescription():
@ -464,7 +478,9 @@ def manager_callback(
["configerror"], ["configerror"],
config_bag.context.get_settings(), config_bag.context.get_settings(),
) )
errors.raise_carry_out_calculation_error(subconfig, _("unable to get value for calculating {0}, {1}"), err) errors.raise_carry_out_calculation_error(
subconfig, _("unable to get value for calculating {0}, {1}"), err
)
return value return value
def get_option_bag( def get_option_bag(
@ -496,7 +512,12 @@ def manager_callback(
# raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation # raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation
if param.notraisepropertyerror or param.raisepropertyerror: if param.notraisepropertyerror or param.raisepropertyerror:
raise err from err raise err from err
errors.raise_carry_out_calculation_error(subconfig, _("unable to carry out a calculation for {0}, {1}"), err, option=option) errors.raise_carry_out_calculation_error(
subconfig,
_("unable to carry out a calculation for {0}, {1}"),
err,
option=option,
)
except ValueError as err: except ValueError as err:
display_name = option.impl_get_display_name(subconfig, with_quote=True) display_name = option.impl_get_display_name(subconfig, with_quote=True)
raise ValueError( raise ValueError(
@ -512,7 +533,12 @@ def manager_callback(
["configerror"], ["configerror"],
config_bag.context.get_settings(), config_bag.context.get_settings(),
) )
errors.raise_carry_out_calculation_error(subconfig, _("unable to get value for calculating {0}, {1}"), err, option=option) errors.raise_carry_out_calculation_error(
subconfig,
_("unable to get value for calculating {0}, {1}"),
err,
option=option,
)
return subsubconfig return subsubconfig
if isinstance(param, ParamValue): if isinstance(param, ParamValue):
@ -532,7 +558,13 @@ def manager_callback(
search_name = search_option.impl_get_display_name( search_name = search_option.impl_get_display_name(
None, with_quote=True None, with_quote=True
) )
errors.raise_carry_out_calculation_error(subconfig, _("cannot find information for {0}, {1} is a dynamic option"), None, option=option, extra_keys=[search_name]) errors.raise_carry_out_calculation_error(
subconfig,
_("cannot find information for {0}, {1} is a dynamic option"),
None,
option=option,
extra_keys=[search_name],
)
else: else:
isubconfig = get_option_bag( isubconfig = get_option_bag(
config_bag, config_bag,
@ -551,7 +583,12 @@ def manager_callback(
param.default_value, param.default_value,
) )
except ValueError as err: except ValueError as err:
errors.raise_carry_out_calculation_error(subconfig, _("unable to get value for calculating {0}, {1}"), err, option=option) errors.raise_carry_out_calculation_error(
subconfig,
_("unable to get value for calculating {0}, {1}"),
err,
option=option,
)
if isinstance(param, ParamIndex): if isinstance(param, ParamIndex):
return index return index
@ -561,7 +598,14 @@ def manager_callback(
not option.impl_is_optiondescription() not option.impl_is_optiondescription()
or not option.impl_is_dynoptiondescription() or not option.impl_is_dynoptiondescription()
): ):
errors.raise_carry_out_calculation_error(subconfig, _("option {0} is not a dynoptiondescription or in a dynoptiondescription"), None, option=option) errors.raise_carry_out_calculation_error(
subconfig,
_(
"option {0} is not a dynoptiondescription or in a dynoptiondescription"
),
None,
option=option,
)
if subconfig.identifiers is None: if subconfig.identifiers is None:
# if uncalculated # if uncalculated
return return
@ -637,18 +681,36 @@ def manager_callback(
) )
except AttributeError as err: except AttributeError as err:
if parent.path: if parent.path:
child_path = parent.path + '.' + name child_path = parent.path + "." + name
else: else:
child_path = name child_path = name
if param.optional: if param.optional:
raise CancelParam(callbk_option.impl_getpath(), child_path) raise CancelParam(
callbk_option.impl_getpath(), child_path
)
identifiers = doption.get_identifiers(parent) identifiers = doption.get_identifiers(parent)
if not identifiers: if not identifiers:
errors.raise_carry_out_calculation_error(subconfig, _('cannot calculate arguments for {0}, {1} with identifier "{2}", there is no identifiers'), err, extra_keys=[identifier]) errors.raise_carry_out_calculation_error(
subconfig,
_(
'cannot calculate arguments for {0}, {1} with identifier "{2}", there is no identifiers'
),
err,
extra_keys=[identifier],
)
else: else:
identifiers_list = display_list(identifiers, add_quote=True) identifiers_list = display_list(
errors.raise_carry_out_calculation_error(subconfig, _('cannot calculate arguments for {0}, {1} with identifier "{2}", list of valid identifiers: {3}'), err, extra_keys=[identifier, identifiers_list]) identifiers, add_quote=True
)
errors.raise_carry_out_calculation_error(
subconfig,
_(
'cannot calculate arguments for {0}, {1} with identifier "{2}", list of valid identifiers: {3}'
),
err,
extra_keys=[identifier, identifiers_list],
)
new_parents.append( new_parents.append(
parent.get_child( parent.get_child(
doption, doption,
@ -752,7 +814,12 @@ def carry_out_calculation(
and option.impl_is_follower() and option.impl_is_follower()
and index is None and index is None
): ):
errors.raise_carry_out_calculation_error(subconfig, _("the follower {0} must have index in carry_out_calculation!"), None, option=option) errors.raise_carry_out_calculation_error(
subconfig,
_("the follower {0} must have index in carry_out_calculation!"),
None,
option=option,
)
def fake_items(iterator): def fake_items(iterator):
return ((None, i) for i in iterator) return ((None, i) for i in iterator)
@ -822,33 +889,14 @@ def carry_out_calculation(
and option.impl_is_follower() and option.impl_is_follower()
and not option.impl_is_submulti() and not option.impl_is_submulti()
): ):
if args or kwargs: raise LeadershipError(
raise LeadershipError( subconfig,
_( "leadership-follower-callback-list",
'the "{}" function with positional arguments "{}" ' callback=callback.__name__,
'and keyword arguments "{}" must not return ' args=args,
'a list ("{}") for the follower option {}' kwargs=kwargs,
"" ret=ret,
).format( )
callback.__name__,
args,
kwargs,
ret,
option.impl_get_display_name(subconfig, with_quote=True),
)
)
else:
raise LeadershipError(
_(
'the "{}" function must not return a list ("{}") '
"for the follower option {}"
""
).format(
callback.__name__,
ret,
option.impl_get_display_name(subconfig, with_quote=True),
)
)
return ret return ret
@ -884,11 +932,14 @@ def calculate(
'unexpected error "{1}" in function "{2}" with arguments "{3}" and "{4}" ' 'unexpected error "{1}" in function "{2}" with arguments "{3}" and "{4}" '
"for option {0}" "for option {0}"
) )
extra_keys = [callback.__name__, extra_keys = [
args, callback.__name__,
kwargs, args,
] kwargs,
]
else: else:
msg = _('unexpected error "{1}" in function "{2}" for option {0}') msg = _('unexpected error "{1}" in function "{2}" for option {0}')
extra_keys = [callback.__name__] extra_keys = [callback.__name__]
errors.raise_carry_out_calculation_error(subconfig, msg, error, extra_keys=extra_keys) errors.raise_carry_out_calculation_error(
subconfig, msg, error, extra_keys=extra_keys
)

View file

@ -228,7 +228,7 @@ class SubConfig:
*, *,
true_path: Optional[str] = None, true_path: Optional[str] = None,
# for python 3.9 properties: Union[list[str], undefined] = undefined, # for python 3.9 properties: Union[list[str], undefined] = undefined,
properties = undefined, properties=undefined,
validate_properties: bool = True, validate_properties: bool = True,
) -> None: ) -> None:
self.index = index self.index = index
@ -417,13 +417,7 @@ class SubConfig:
length = self.get_length_leadership() length = self.get_length_leadership()
if index >= length: if index >= length:
raise LeadershipError( raise LeadershipError(
_( subsubconfig, "leadership-greater", index=index, length=length
'index "{0}" is greater than the leadership length "{1}" for option {2}'
).format(
index,
length,
option.impl_get_display_name(subsubconfig, with_quote=True),
)
) )
return subsubconfig return subsubconfig
@ -869,9 +863,10 @@ class _Config(CCache):
subconfig, with_quote=True subconfig, with_quote=True
) )
raise LeadershipError( raise LeadershipError(
_( subconfig,
"the follower option {0} has greater length ({1}) than the leader length ({2})" "leadership-follower-greater",
).format(option_name, follower_len, length) index=follower_len,
length=length,
) )
self.get_settings().validate_mandatory( self.get_settings().validate_mandatory(
subconfig, subconfig,

View file

@ -18,6 +18,23 @@
import weakref import weakref
from .i18n import _ from .i18n import _
from typing import Literal, Union
TiramisuErrorCode = Literal[
"option-dynamic",
"option-not-found",
"property-frozen",
"property-error",
"property-mandatory",
"leadership-group_type",
"leadership-wrong_property",
"leadership-force_default_on_freeze",
"leadership-greater",
"leadership-follower-greater",
"leadership-follower-callback-list",
]
def display_list( def display_list(
lst, lst,
@ -69,6 +86,8 @@ class PropertiesOptionError(AttributeError):
orig_opt=None, orig_opt=None,
help_properties=None, help_properties=None,
): ):
if orig_opt:
raise Exception("a la")
if opt_type: if opt_type:
self._opt_type = opt_type self._opt_type = opt_type
self._name = name self._name = name
@ -87,10 +106,23 @@ class PropertiesOptionError(AttributeError):
self.help_properties = help_properties self.help_properties = help_properties
self._settings = settings self._settings = settings
self.msg = None self.msg = None
if not self.help_properties:
self.help_properties = self.proptype
properties = list(self.help_properties)
if properties == ["frozen"]:
self.code = "property-frozen"
elif properties == ["mandatory"]:
self.code = "property-mandatory"
else:
self.code = "property-error"
super().__init__(None) super().__init__(None)
def set_orig_opt(self, opt): def display_properties(self, force_property=False, add_quote=True):
self._orig_opt = opt if force_property:
properties = self.proptype
else:
properties = self.help_properties
return display_list(list(properties), add_quote=add_quote)
def __str__(self): def __str__(self):
# this part is a bit slow, so only execute when display # this part is a bit slow, so only execute when display
@ -98,47 +130,81 @@ class PropertiesOptionError(AttributeError):
return self.msg return self.msg
if self._settings is None: if self._settings is None:
return "error" return "error"
if self.help_properties: arguments = [self._opt_type]
properties = list(self.help_properties)
else:
properties = list(self.proptype)
only_one = len(properties) == 1
properties_msg = display_list(properties, add_quote=True)
if only_one:
prop_msg = _("property")
else:
prop_msg = _("properties")
if properties == ["frozen"]:
if self._orig_opt:
msg = _('cannot modify the {0} {1} because "{2}" has {3} {4}')
else:
msg = _("cannot modify the {0} {1} because has {2} {3}")
elif properties == ["mandatory"]:
if self._orig_opt:
msg = _("cannot access to {0} {1} because \"{2}\" hasn't value")
else:
msg = _("{0} {1} is mandatory but hasn't value")
else:
if self._orig_opt:
msg = _('cannot access to {0} {1} because "{2}" has {3} {4}')
else:
msg = _("cannot access to {0} {1} because has {2} {3}")
if self._orig_opt: if self._orig_opt:
# FIXME _orig_opt ? arguments.append(
self.msg = msg.format( self._orig_opt.impl_get_display_name(subconfig, with_quote=True)
self._opt_type,
self._orig_opt.impl_get_display_name(subconfig, with_quote=True),
self._name,
prop_msg,
properties_msg,
) )
arguments.append(self._name)
index = self._subconfig.index
if index is not None:
arguments.append(index)
if self.code == "property-frozen":
if index is not None:
if self._orig_opt:
msg = _(
'cannot modify the {0} {1} at index "{2}" because {3} is frozen'
)
else:
msg = _(
'cannot modify the {0} {1} at index "{2}" because is frozen'
)
else:
if self._orig_opt:
msg = _("cannot modify the {0} {1} because {2} is frozen")
else:
msg = _("cannot modify the {0} {1} because is frozen")
elif self.code == "property-mandatory":
if index is not None:
if self._orig_opt:
msg = _(
'cannot access to {0} {1} at index "{2}" because {2} hasn\'t value'
)
else:
msg = _('{0} {1} at index "{2}" is mandatory but hasn\'t value')
else:
if self._orig_opt:
msg = _("cannot access to {0} {1} because {2} hasn't value")
else:
msg = _("{0} {1} is mandatory but hasn't value")
else: else:
self.msg = msg.format(self._opt_type, self._name, prop_msg, properties_msg) if index is not None:
if self._orig_opt:
msg = _(
'cannot access to {0} {1} at index "{2}" because {3} has {4} {5}'
)
else:
msg = _(
'cannot access to {0} {1} at index "{2}" because has {3} {4}'
)
else:
if self._orig_opt:
msg = _("cannot access to {0} {1} because {2} has {3} {4}")
else:
msg = _("cannot access to {0} {1} because has {2} {3}")
only_one = len(self.help_properties) == 1
if only_one:
arguments.append(_("property"))
else:
arguments.append(_("properties"))
arguments.append(self.display_properties())
self.msg = msg.format(*arguments)
del self._opt_type, self._name del self._opt_type, self._name
del self._settings, self._orig_opt del self._settings, self._orig_opt
return self.msg return self.msg
class AttributeOptionError(AttributeError):
def __init__(self, path: str, code: TiramisuErrorCode) -> None:
self.path = path
self.code = code
def __str__(self) -> str:
if self.code == "option-dynamic":
return _('cannot access to "{0}" it\'s a dynamic option').format(self.path)
return _('"{0}" is not an option').format(self.path)
# ____________________________________________________________ # ____________________________________________________________
# Exceptions for a Config # Exceptions for a Config
class ConfigError(Exception): class ConfigError(Exception):
@ -163,8 +229,74 @@ class ConflictError(Exception):
# ____________________________________________________________ # ____________________________________________________________
# miscellaneous exceptions # miscellaneous exceptions
class LeadershipError(Exception): class LeadershipError(Exception):
"problem with a leadership's value length" def __init__(
pass self,
subconfig: Union[str, "SubConfig"],
code,
*,
prop=None,
index=None,
length=None,
callback=None,
args=None,
kwargs=None,
ret=None,
):
if isinstance(subconfig, str):
self.path = self.display_name = subconfig
else:
self.path = subconfig.path
option = subconfig.option
self.display_name = option.impl_get_display_name(subconfig, with_quote=True)
self.code = code
if prop:
self.prop = prop
if index:
self.index = index
if length:
self.length = length
if callback:
self.callback = callback
if args:
self.args = args
if kwargs:
self.kwargs = kwargs
if ret:
self.ret = ret
def __str__(self):
if self.code == "leadership-group_type":
return _('cannot set "group_type" attribute for the Leadership {0}').format(
self.display_name
)
if self.code == "leadership-wrong_property":
return _('the leader {0} cannot have "{1}" property').format(
self.display_name, self.prop
)
if self.code == "leadership-force_default_on_freeze":
return _(
'the leader {0} cannot have "force_default_on_freeze" or "force_metaconfig_on_freeze" property without "frozen"'
).format(self.display_name)
if self.code == "leadership-reduce":
return _("cannot reduce length of the leader {0}").format(self.display_name)
if self.code == "leadership-greater":
return _(
'index "{0}" is greater than the leadership length "{1}" for option {2}'
).format(self.index, self.length, self.display_name)
if self.code == "leadership-follower-greater":
return _(
"the follower option {0} has greater length ({1}) than the leader length ({2})"
).format(self.display_name, self.index, self.length)
if self.code == "leadership-follower-callback-list":
if self.args or self.kwargs:
return _(
'the "{0}" function with positional arguments "{1}" and keyword arguments "{2}" must not return a list ("{3}") for the follower option {4}'
).format(
self.callback, self.args, self.kwargs, self.ret, self.display_name
)
return _(
'the "{0}" function must not return a list ("{1}") for the follower option {2}'
).format(self.callback, self.ret, self.display_name)
class ConstError(TypeError): class ConstError(TypeError):
@ -186,7 +318,9 @@ class _CommonError:
try: try:
msg = self.prefix msg = self.prefix
except AttributeError: except AttributeError:
self.prefix = self.tmpl.format(self.val, _(self.display_type), self.name, self.index) self.prefix = self.tmpl.format(
self.val, _(self.display_type), self.name, self.index
)
msg = self.prefix msg = self.prefix
if self.err_msg: if self.err_msg:
if msg: if msg:
@ -203,12 +337,16 @@ class ValueWarning(_CommonError, UserWarning):
def __init__(self, **kwargs): def __init__(self, **kwargs):
if ValueWarning.tmpl is None: if ValueWarning.tmpl is None:
if kwargs.get('index') is None: if kwargs.get("index") is None:
ValueWarning.tmpl = _('attention, "{0}" could be an invalid {1} for {2}') ValueWarning.tmpl = _(
'attention, "{0}" could be an invalid {1} for {2}'
)
else: else:
ValueWarning.tmpl = _('attention, "{0}" could be an invalid {1} for {2} at index "{3}"') ValueWarning.tmpl = _(
if list(kwargs) == ['msg']: 'attention, "{0}" could be an invalid {1} for {2} at index "{3}"'
self.msg = kwargs['msg'] )
if list(kwargs) == ["msg"]:
self.msg = kwargs["msg"]
else: else:
super().__init__(**kwargs) super().__init__(**kwargs)
self.msg = None self.msg = None
@ -224,7 +362,7 @@ class ValueOptionError(_CommonError, ValueError):
def __init__(self, **kwargs): def __init__(self, **kwargs):
if ValueOptionError.tmpl is None: if ValueOptionError.tmpl is None:
if kwargs.get('index') is None: if kwargs.get("index") is None:
self.tmpl = _('"{0}" is an invalid {1} for {2}') self.tmpl = _('"{0}" is an invalid {1} for {2}')
else: else:
self.tmpl = _('"{0}" is an invalid {1} for {2} at index "{3}"') self.tmpl = _('"{0}" is an invalid {1} for {2} at index "{3}"')
@ -258,12 +396,12 @@ class CancelParam(Exception):
class Errors: class Errors:
@staticmethod @staticmethod
def raise_carry_out_calculation_error(subconfig, message, original_error, option=None, extra_keys=[]): def raise_carry_out_calculation_error(
subconfig, message, original_error, option=None, extra_keys=[]
):
if option is None: if option is None:
option = subconfig.option option = subconfig.option
display_name = option.impl_get_display_name( display_name = option.impl_get_display_name(subconfig, with_quote=True)
subconfig, with_quote=True
)
if original_error: if original_error:
raise ConfigError( raise ConfigError(
message.format(display_name, original_error, *extra_keys) message.format(display_name, original_error, *extra_keys)

View file

@ -176,9 +176,7 @@ class DomainnameOption(StrOption):
try: try:
socket.gethostbyname(value) socket.gethostbyname(value)
except socket.gaierror as err: except socket.gaierror as err:
raise ValueError( raise ValueError(_("DNS resolution failed").format(value)) from err
_("DNS resolution failed").format(value)
) from err
except Exception as err: except Exception as err:
raise ValueError( raise ValueError(
_("error resolving DNS: {1}").format(value, err) _("error resolving DNS: {1}").format(value, err)

View file

@ -74,9 +74,12 @@ class FilenameOption(StrOption):
if not found and "directory" in types and file.is_dir(): if not found and "directory" in types and file.is_dir():
found = True found = True
if not found: if not found:
translated_types = [{"file": _("file"), "directory": _("directory")}.get(typ) for typ in types] translated_types = [
{"file": _("file"), "directory": _("directory")}.get(typ)
for typ in types
]
raise ValueError( raise ValueError(
_('cannot find this {0}').format( _("cannot find this {0}").format(
display_list(translated_types, separator="or"), value display_list(translated_types, separator="or"), value
) )
) )

View file

@ -43,7 +43,7 @@ class IntOption(Option):
value: int, value: int,
) -> None: ) -> None:
if not isinstance(value, int): if not isinstance(value, int):
raise ValueError(_('which is not an integer')) raise ValueError(_("which is not an integer"))
def second_level_validation(self, value, warnings_only): def second_level_validation(self, value, warnings_only):
min_number = self.impl_get_extra("min_number") min_number = self.impl_get_extra("min_number")

View file

@ -51,9 +51,7 @@ class Leadership(OptionDescription):
**kwargs, **kwargs,
) -> None: ) -> None:
if "group_type" in kwargs: if "group_type" in kwargs:
raise LeadershipError( raise LeadershipError(name, "leadership-group_type")
_('cannot set "group_type" attribute for a Leadership')
)
super().__init__( super().__init__(
name, name,
doc, doc,
@ -85,9 +83,7 @@ class Leadership(OptionDescription):
if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance( if prop not in ALLOWED_LEADER_PROPERTIES and not isinstance(
prop, Calculation prop, Calculation
): ):
raise LeadershipError( raise LeadershipError(name, "leadership-wrong_property", prop=prop)
_('leader cannot have "{}" property').format(prop)
)
def _check_child_is_valid( def _check_child_is_valid(
self, self,

View file

@ -259,7 +259,7 @@ class Option(BaseOption):
*, *,
check_error: bool = True, check_error: bool = True,
loaded: bool = False, loaded: bool = False,
self_properties: frozenset=frozenset(), self_properties: frozenset = frozenset(),
) -> bool: ) -> bool:
"""Return True if value is really valid """Return True if value is really valid
If not validate or invalid return it returns False If not validate or invalid return it returns False
@ -269,8 +269,11 @@ class Option(BaseOption):
config_properties = subconfig.config_bag.properties config_properties = subconfig.config_bag.properties
self_properties = subconfig.properties self_properties = subconfig.properties
else: else:
config_properties = {'validator'} config_properties = {"validator"}
if "validator" not in config_properties or "validator" not in self_properties: if (
"validator" not in config_properties
or "validator" not in self_properties
):
return False return False
if subconfig: if subconfig:
force_index = subconfig.index force_index = subconfig.index
@ -443,11 +446,21 @@ class Option(BaseOption):
or "demoting_error_warning" not in subconfig.config_bag.properties or "demoting_error_warning" not in subconfig.config_bag.properties
): ):
raise ValueOptionError( raise ValueOptionError(
subconfig=subconfig, val=val, display_type=_(self.get_type()), opt=self, err_msg=str(err), index=err_index subconfig=subconfig,
val=val,
display_type=_(self.get_type()),
opt=self,
err_msg=str(err),
index=err_index,
) from err ) from err
warnings.warn_explicit( warnings.warn_explicit(
ValueErrorWarning( ValueErrorWarning(
subconfig=subconfig, val=val, display_type=_(self.get_type()), opt=self, err_msg=str(err), index=err_index subconfig=subconfig,
val=val,
display_type=_(self.get_type()),
opt=self,
err_msg=str(err),
index=err_index,
), ),
ValueErrorWarning, ValueErrorWarning,
self.__class__.__name__, self.__class__.__name__,

View file

@ -29,7 +29,7 @@ from ..setting import ConfigBag, groups, undefined, owners, Undefined
from .baseoption import BaseOption from .baseoption import BaseOption
# from .syndynoption import SubDynOptionDescription, SynDynOptionDescription # from .syndynoption import SubDynOptionDescription, SynDynOptionDescription
from ..error import ConfigError, ConflictError from ..error import ConfigError, ConflictError, AttributeOptionError
class CacheOptionDescription(BaseOption): class CacheOptionDescription(BaseOption):
@ -242,12 +242,10 @@ class OptionDescriptionWalk(CacheOptionDescription):
] # pylint: disable=no-member ] # pylint: disable=no-member
if option.impl_is_dynoptiondescription() and not allow_dynoption: if option.impl_is_dynoptiondescription() and not allow_dynoption:
if parent.path: if parent.path:
path = parent.path + '.' + name path = parent.path + "." + name
else: else:
path = name path = name
raise AttributeError( raise AttributeOptionError(path, "option-dynamic")
_('cannot access to "{0}" it\'s a dynamic option').format(path)
)
return option return option
def get_child( def get_child(
@ -279,14 +277,10 @@ class OptionDescriptionWalk(CacheOptionDescription):
return child return child
return identifier, child return identifier, child
if parent.path is None: if parent.path is None:
raise AttributeError( path = name
_('cannot find "{0}"').format(name) else:
) path = parent.path + "." + name
raise AttributeError( raise AttributeOptionError(path, "option-not-found")
_('cannot find "{0}" in "{1}"').format(
name, parent.path
)
)
def get_children(self) -> List[BaseOption]: def get_children(self) -> List[BaseOption]:
"""get children""" """get children"""

View file

@ -38,7 +38,7 @@ class StrOption(Option):
) -> None: ) -> None:
"""validation""" """validation"""
if not isinstance(value, str): if not isinstance(value, str):
raise ValueError(_('which is not a string')) raise ValueError(_("which is not a string"))
class RegexpOption(StrOption): class RegexpOption(StrOption):

View file

@ -56,23 +56,23 @@ class URLOption(StrOption):
# pylint: disable=too-many-arguments,too-many-locals,redefined-builtin # pylint: disable=too-many-arguments,too-many-locals,redefined-builtin
extra = {} extra = {}
extra["_domainname"] = DomainnameOption( extra["_domainname"] = DomainnameOption(
name, name,
doc, doc,
allow_ip=allow_ip, allow_ip=allow_ip,
type=type, type=type,
allow_without_dot=allow_without_dot, allow_without_dot=allow_without_dot,
_extra=extra, _extra=extra,
) )
extra["_port"] = PortOption( extra["_port"] = PortOption(
name, name,
doc, doc,
allow_range=allow_range, allow_range=allow_range,
allow_zero=allow_zero, allow_zero=allow_zero,
allow_wellknown=allow_wellknown, allow_wellknown=allow_wellknown,
allow_registred=allow_registred, allow_registred=allow_registred,
allow_private=allow_private, allow_private=allow_private,
_extra=extra, _extra=extra,
) )
super().__init__( super().__init__(
name, name,
doc, doc,

View file

@ -476,7 +476,7 @@ class Settings:
and new_prop not in ALLOWED_LEADER_PROPERTIES and new_prop not in ALLOWED_LEADER_PROPERTIES
): ):
raise LeadershipError( raise LeadershipError(
_('leader cannot have "{new_prop}" property') subconfig, "leadership-wrong_property", prop=new_prop
) )
props.add(new_prop) props.add(new_prop)
props -= self.getpermissives(subconfig) props -= self.getpermissives(subconfig)
@ -564,19 +564,15 @@ class Settings:
not_allowed_properties = properties - ALLOWED_LEADER_PROPERTIES not_allowed_properties = properties - ALLOWED_LEADER_PROPERTIES
if not_allowed_properties: if not_allowed_properties:
raise LeadershipError( raise LeadershipError(
_('leader cannot have "{0}" property').format( subconfig,
display_list(not_allowed_properties) "leadership-wrong_property",
) prop=display_list(not_allowed_properties),
) )
if ( if (
"force_default_on_freeze" in properties "force_default_on_freeze" in properties
or "force_metaconfig_on_freeze" in properties or "force_metaconfig_on_freeze" in properties
) and "frozen" not in properties: ) and "frozen" not in properties:
raise LeadershipError( raise LeadershipError(subconfig, "leadership-force_default_on_freeze")
_(
'a leader ({0}) cannot have "force_default_on_freeze" or "force_metaconfig_on_freeze" property without "frozen"'
).format(opt.impl_get_display_name())
)
self._properties.setdefault(subconfig.path, {})[subconfig.index] = properties self._properties.setdefault(subconfig.path, {})[subconfig.index] = properties
# values too because of follower values could have a PropertiesOptionError has value # values too because of follower values could have a PropertiesOptionError has value
subconfig.config_bag.context.reset_cache(subconfig) subconfig.config_bag.context.reset_cache(subconfig)

View file

@ -295,9 +295,9 @@ class Values:
owners.forced, owners.forced,
) )
validator = ( validator = (
"validator" in setting_properties and "validator" in setting_properties
"validator" in self_properties and and "validator" in self_properties
"demoting_error_warning" not in setting_properties and "demoting_error_warning" not in setting_properties
) )
if validator and not has_calculation: if validator and not has_calculation:
cache = subconfig.config_bag.context.get_values_cache() cache = subconfig.config_bag.context.get_values_cache()
@ -306,7 +306,11 @@ class Values:
value, value,
validated=validator, validated=validator,
) )
elif "validator" in setting_properties and "validator" in self_properties and has_calculation: elif (
"validator" in setting_properties
and "validator" in self_properties
and has_calculation
):
cache = subconfig.config_bag.context.get_values_cache() cache = subconfig.config_bag.context.get_values_cache()
cache.delcache(subconfig.path) cache.delcache(subconfig.path)
@ -592,7 +596,12 @@ class Values:
self_properties = subconfig.properties self_properties = subconfig.properties
context = config_bag.context context = config_bag.context
setting_properties = config_bag.properties setting_properties = config_bag.properties
if validate and hasvalue and "validator" in setting_properties and "validator" in self_properties: if (
validate
and hasvalue
and "validator" in setting_properties
and "validator" in self_properties
):
fake_context = context.gen_fake_context() fake_context = context.gen_fake_context()
fake_config_bag = config_bag.copy() fake_config_bag = config_bag.copy()
fake_config_bag.remove_validation() fake_config_bag.remove_validation()