feat: add subconfig in errors

This commit is contained in:
egarette@silique.fr 2025-12-19 13:30:30 +01:00
parent 050979f6d3
commit fe108b1238
13 changed files with 41 additions and 24 deletions

View file

@ -402,12 +402,12 @@ def test_prefix_error():
try: try:
cfg.option('test1').value.set('yes') cfg.option('test1').value.set('yes')
except Exception as err: except Exception as err:
assert str(err) == _('"{0}" is an invalid {1} for "{2}", which is not an integer').format('yes', _('integer'), 'test1') assert str(err) == _('"{0}" is an invalid {1} for "{2}", it\'s not an integer').format('yes', _('integer'), 'test1')
try: try:
cfg.option('test1').value.set('yes') cfg.option('test1').value.set('yes')
except Exception as err: except Exception as err:
err.prefix = '' err.prefix = ''
assert str(err) == _('which is not an integer') assert str(err) == _('it\'s not an integer')
# assert not list_sessions() # assert not list_sessions()

View file

@ -583,6 +583,7 @@ class _TiramisuOptionOptionDescription:
convert: bool = False, convert: bool = False,
): ):
"""Get identifiers for dynamic option""" """Get identifiers for dynamic option"""
subconfig = self._subconfig
if not only_self: if not only_self:
if self._subconfig.is_dynamic_without_identifiers and not uncalculated: if self._subconfig.is_dynamic_without_identifiers and not uncalculated:
raise AttributeOptionError(self._subconfig.path, "option-dynamic") raise AttributeOptionError(self._subconfig.path, "option-dynamic")
@ -590,7 +591,6 @@ class _TiramisuOptionOptionDescription:
return self._subconfig.identifiers return self._subconfig.identifiers
identifiers = [] identifiers = []
dynconfig = None dynconfig = None
subconfig = self._subconfig
while not dynconfig: while not dynconfig:
if subconfig.option.impl_is_optiondescription() and subconfig.option.impl_is_dynoptiondescription(): if subconfig.option.impl_is_optiondescription() and subconfig.option.impl_is_dynoptiondescription():
dynconfig = subconfig dynconfig = subconfig
@ -607,7 +607,8 @@ class _TiramisuOptionOptionDescription:
raise ConfigError( raise ConfigError(
_( _(
"the option {0} is not a dynamic option, cannot get identifiers with only_self parameter to True" "the option {0} is not a dynamic option, cannot get identifiers with only_self parameter to True"
).format(self._subconfig.path) ).format(self._subconfig.path),
subconfig=subconfig,
) )
return self._subconfig.option.get_identifiers( return self._subconfig.option.get_identifiers(
self._subconfig.parent, self._subconfig.parent,

View file

@ -462,7 +462,7 @@ def manager_callback(
or param.raisepropertyerror or param.raisepropertyerror
): ):
raise err from err raise err from err
raise ConfigError(err) raise ConfigError(err, subconfig=subconfig)
except ValueError as err: except ValueError as err:
display_name = subconfig.option.impl_get_display_name( display_name = subconfig.option.impl_get_display_name(
subconfig, with_quote=True subconfig, with_quote=True
@ -626,7 +626,7 @@ def manager_callback(
properties = config_bag.context.get_settings().getproperties( properties = config_bag.context.get_settings().getproperties(
subconfig, subconfig,
uncalculated=True, uncalculated=True,
) - {'validator'} ) - {'validator', 'mandatory', 'empty'}
for subconfig_ in subconfigs: for subconfig_ in subconfigs:
if subconfig.path == subconfig_.path: if subconfig.path == subconfig_.path:
values.append(orig_value) values.append(orig_value)
@ -958,6 +958,7 @@ def calculate(
raise err from err raise err from err
error = err error = err
except ConfigError as err: except ConfigError as err:
err.subconfig = subconfig
raise err from err raise err from err
except Exception as err: except Exception as err:
error = err error = err

View file

@ -522,7 +522,7 @@ class SubConfig:
) )
if check_index and index is not None: if check_index and index is not None:
if option.impl_is_optiondescription() or not option.impl_is_follower(): if option.impl_is_optiondescription() or not option.impl_is_follower():
raise ConfigError("index must be set only with a follower option") raise ConfigError("index must be set only with a follower option", subconfig=subsubconfig,)
length = self.get_length_leadership() length = self.get_length_leadership()
if index >= length: if index >= length:
raise LeadershipError( raise LeadershipError(
@ -1426,7 +1426,7 @@ class KernelGroupConfig(_CommonConfig):
# pylint: disable=protected-access # pylint: disable=protected-access
ret.append( ret.append(
PropertiesOptionError( PropertiesOptionError(
err._subconfig, err.subconfig,
err.proptype, err.proptype,
err._settings, err._settings,
err._opt_type, err._opt_type,
@ -1674,7 +1674,7 @@ class KernelMixConfig(KernelGroupConfig):
# pylint: disable=protected-access # pylint: disable=protected-access
ret.append( ret.append(
PropertiesOptionError( PropertiesOptionError(
err._subconfig, err.subconfig,
err.proptype, err.proptype,
err._settings, err._settings,
err._opt_type, err._opt_type,

View file

@ -18,7 +18,7 @@
import weakref import weakref
from .i18n import _ from .i18n import _
from typing import Literal, Union from typing import Literal, Union, Optional
TiramisuErrorCode = Literal[ TiramisuErrorCode = Literal[
@ -101,7 +101,7 @@ class PropertiesOptionError(AttributeError):
subconfig, with_quote=True subconfig, with_quote=True
) )
self._orig_opt = None self._orig_opt = None
self._subconfig = subconfig self.subconfig = subconfig
self.proptype = proptype self.proptype = proptype
self.help_properties = help_properties self.help_properties = help_properties
self._settings = settings self._settings = settings
@ -136,7 +136,7 @@ class PropertiesOptionError(AttributeError):
self._orig_opt.impl_get_display_name(subconfig, with_quote=True) self._orig_opt.impl_get_display_name(subconfig, with_quote=True)
) )
arguments.append(self._name) arguments.append(self._name)
index = self._subconfig.index index = self.subconfig.index
if index is not None: if index is not None:
arguments.append(index) arguments.append(index)
if self.code == "property-frozen": if self.code == "property-frozen":
@ -215,10 +215,22 @@ class ConfigError(Exception):
def __init__( def __init__(
self, self,
exp, exp,
ori_err=None, *,
prefix: Optional[str] = None,
subconfig: Optional["Subconfig"]=None,
): ):
super().__init__(exp) super().__init__(exp)
self.ori_err = ori_err self.err_msg = exp
self.subconfig = subconfig
self.prefix = prefix
def __str__(self):
msg = self.prefix
if msg:
msg += ", {}".format(self.err_msg)
else:
msg = self.err_msg
return msg
class ConflictError(Exception): class ConflictError(Exception):
@ -419,9 +431,9 @@ class Errors:
display_name = option.impl_get_display_name(subconfig, with_quote=True) display_name = option.impl_get_display_name(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), subconfig=subconfig,
) from original_error ) from original_error
raise ConfigError(message.format(display_name, extra_keys)) raise ConfigError(message.format(display_name, extra_keys), subconfig=subconfig)
errors = Errors() errors = Errors()

View file

@ -395,6 +395,8 @@ class BaseOption(Base):
is_identifier: bool = False, is_identifier: bool = False,
type_: str = 'default' type_: str = 'default'
) -> Any: ) -> Any:
if not isinstance(is_identifier, bool):
raise Exception()
"""parse dependancy to add dependencies""" """parse dependancy to add dependencies"""
for param in chain(value.params.args, value.params.kwargs.values()): for param in chain(value.params.args, value.params.kwargs.values()):
if isinstance(param, ParamOption): if isinstance(param, ParamOption):

View file

@ -45,7 +45,7 @@ class ChoiceOption(Option):
:param values: is a list of values the option can possibly take :param values: is a list of values the option can possibly take
""" """
if isinstance(values, Calculation): if isinstance(values, Calculation):
self.value_dependency(values, "choice") self.value_dependency(values, type_="choice")
elif not isinstance(values, tuple): elif not isinstance(values, tuple):
raise TypeError( raise TypeError(
_("values must be a tuple or a calculation for {0}").format(name) _("values must be a tuple or a calculation for {0}").format(name)
@ -73,7 +73,8 @@ class ChoiceOption(Option):
raise ConfigError( raise ConfigError(
_('the calculated values "{0}" for "{1}" is not a list' "").format( _('the calculated values "{0}" for "{1}" is not a list' "").format(
values, self.impl_getname() values, self.impl_getname()
) ),
subconfig=subconfig,
) )
return values return values

View file

@ -31,7 +31,6 @@ from ..i18n import _
from .optiondescription import OptionDescription from .optiondescription import OptionDescription
from .baseoption import BaseOption from .baseoption import BaseOption
from ..setting import ConfigBag, undefined from ..setting import ConfigBag, undefined
from ..error import ConfigError
from ..autolib import Calculation, get_calculated_value from ..autolib import Calculation, get_calculated_value

View file

@ -56,7 +56,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(_("it's not an integer"))
def second_level_validation(self, value, warnings_only): def second_level_validation(self, value, warnings_only):
min_integer = self.impl_get_extra("min_integer") min_integer = self.impl_get_extra("min_integer")

View file

@ -39,7 +39,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(_("it's not a string"))
class RegexpOption(StrOption): class RegexpOption(StrOption):

View file

@ -22,7 +22,6 @@
""" """
from typing import Any, Optional, Dict from typing import Any, Optional, Dict
from .baseoption import BaseOption, valid_name from .baseoption import BaseOption, valid_name
from ..error import ConfigError
from ..i18n import _ from ..i18n import _

View file

@ -638,7 +638,8 @@ class Settings:
raise ConfigError( raise ConfigError(
_("cannot add those permissives: {0}").format( _("cannot add those permissives: {0}").format(
" ".join(forbidden_permissives) " ".join(forbidden_permissives)
) ),
subconfig=subconfig,
) )
self._permissives.setdefault(path, {})[index] = permissives self._permissives.setdefault(path, {})[index] = permissives
if subconfig is not None: if subconfig is not None:

View file

@ -615,7 +615,8 @@ class Values:
raise ConfigError( raise ConfigError(
_( _(
'"{0}" is a default value, so we cannot change owner to "{1}"' '"{0}" is a default value, so we cannot change owner to "{1}"'
).format(subconfig.path, owner) ).format(subconfig.path, owner),
subconfig=subconfig,
) )
subconfig.config_bag.context.get_settings().validate_frozen(subconfig) subconfig.config_bag.context.get_settings().validate_frozen(subconfig)
self._values[subconfig.path][subconfig.index][1] = owner self._values[subconfig.path][subconfig.index][1] = owner