tiramisu/tiramisu/config.py

1580 lines
67 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
2024-07-06 14:33:25 +02:00
# Copyright (C) 2012-2024 Team tiramisu (see AUTHORS for all contributors)
#
2013-09-22 22:33:09 +02:00
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
2013-09-22 22:33:09 +02:00
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
2013-09-22 22:33:09 +02:00
# 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/>.
#
2012-10-05 16:00:07 +02:00
# The original `Config` design model is unproudly borrowed from
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence
# ____________________________________________________________
2023-05-11 15:44:48 +02:00
"""options handler global entry point
"""
import weakref
from copy import copy, deepcopy
2023-04-27 11:34:35 +02:00
from typing import Optional, List, Any, Union
2024-06-20 12:56:27 +02:00
from os.path import commonprefix
2015-04-18 22:53:45 +02:00
2019-02-23 19:06:23 +01:00
from .error import PropertiesOptionError, ConfigError, ConflictError, \
2023-04-27 11:44:52 +02:00
LeadershipError
2023-05-11 15:44:48 +02:00
from .option import DynOptionDescription, Leadership, Option
2024-04-24 15:39:17 +02:00
from .setting import ConfigBag, Settings, undefined, groups
2023-04-27 11:34:35 +02:00
from .value import Values, owners
from .i18n import _
from .cacheobj import Cache
from .autolib import Calculation
2024-04-24 15:39:17 +02:00
from . import autolib
2013-02-22 11:09:17 +01:00
2024-06-20 12:56:27 +02:00
def get_common_path(path1, path2):
common_path = commonprefix([path1, path2])
if common_path in [path1, path2]:
return common_path
if common_path.endswith('.'):
return common_path[:-1]
if '.' in common_path:
return common_path.rsplit('.', 1)[0]
return None
2024-04-24 15:39:17 +02:00
class CCache:
__slots__ = tuple()
# =============================================================================
# CACHE
def reset_cache(self,
subconfig,
resetted_opts=None,
):
"""reset all settings in cache
"""
if resetted_opts is None:
resetted_opts = []
if subconfig is not None:
if 'cache' not in subconfig.config_bag.properties:
return
subconfig.config_bag.properties = subconfig.config_bag.properties - {'cache'}
self.reset_one_option_cache(subconfig,
resetted_opts,
)
subconfig.config_bag.properties = subconfig.config_bag.properties | {'cache'}
else:
self._impl_values_cache.reset_all_cache() # pylint: disable=no-member
self.properties_cache.reset_all_cache() # pylint: disable=no-member
def reset_one_option_cache(self,
subconfig,
resetted_opts,
):
"""reset cache for one option
"""
if subconfig.path in resetted_opts:
return
resetted_opts.append(subconfig.path)
config_bag = subconfig.config_bag
for woption in subconfig.option.get_dependencies(subconfig.option):
option = woption()
if option.issubdyn():
# it's an option in dynoptiondescription, remove cache for all generated option
self.reset_cache_dyn_option(config_bag,
option,
resetted_opts,
)
elif option.impl_is_dynoptiondescription():
self._reset_cache_dyn_optiondescription(option,
config_bag,
resetted_opts,
)
else:
option_subconfig = self.get_sub_config(config_bag,
option.impl_getpath(),
None,
properties=None,
validate_properties=False,
)
self.reset_one_option_cache(option_subconfig,
resetted_opts,
)
del option
subconfig.option.reset_cache(subconfig.path,
config_bag,
resetted_opts,
)
def _reset_cache_dyn_optiondescription(self,
option,
config_bag,
resetted_opts,
):
# reset cache for all chidren
path = option.impl_getpath()
if '.' in path:
parent_path = path.rsplit('.', 1)[0]
parent_subconfig = self.get_sub_config(config_bag,
parent_path,
None,
properties=None,
validate_properties=False,
)
else:
parent_subconfig = self.get_root(config_bag)
for subconfig in parent_subconfig.dyn_to_subconfig(option,
False,
):
self.reset_one_option_cache(subconfig,
resetted_opts,
)
for walk_subconfig in self.walk(subconfig,
no_value=True,
validate_properties=False,
):
self.reset_one_option_cache(walk_subconfig,
resetted_opts,
)
def reset_cache_dyn_option(self,
config_bag,
option,
resetted_opts,
):
currents = [self.get_root(config_bag)]
sub_paths = option.impl_getpath()
for sub_path in sub_paths.split('.'):
new_currents = []
for current in currents:
sub_option = current.option.get_child(sub_path,
config_bag,
current,
allow_dynoption=True,
)
if sub_option.impl_is_dynoptiondescription():
new_currents.extend(list(current.dyn_to_subconfig(sub_option,
False,
)))
else:
new_currents.append(current.get_child(sub_option,
None,
False,
properties=None,
),
)
currents = new_currents
for dyn_option_subconfig in currents:
self.reset_one_option_cache(dyn_option_subconfig,
resetted_opts,
)
class SubConfig:
__slots__ = ('config_bag',
'option',
'parent',
'index',
'path',
'true_path',
'properties',
'transitive_properties',
2024-04-24 15:39:17 +02:00
'is_dynamic',
'suffixes',
'_length',
)
def __init__(self,
option: Option,
index: Optional[int],
path: str,
config_bag: ConfigBag,
parent: Optional['SubConfig'],
suffixes: Optional[list[str]],
*,
true_path: Optional[str]=None,
properties: Union[list[str], undefined]=undefined,
validate_properties: bool=True,
) -> None:
self.index = index
self.suffixes = suffixes
self.option = option
self.config_bag = config_bag
self.parent = parent
self._length = None
self.path = path
if true_path is None:
true_path = path
2024-06-20 12:56:27 +02:00
is_follower = not option.impl_is_optiondescription() and option.impl_is_follower()
apply_requires = not is_follower or index is not None
2024-04-24 15:39:17 +02:00
self.true_path = true_path
2024-06-20 12:56:27 +02:00
settings = config_bag.context.get_settings()
if parent and parent.is_dynamic or self.option.impl_is_dynoptiondescription():
self.is_dynamic = True
else:
self.is_dynamic = False
2024-04-24 15:39:17 +02:00
if properties is undefined:
if path is None:
self.properties = frozenset()
else:
self.properties = frozenset()
if validate_properties:
self.properties = settings.getproperties(self,
apply_requires=False,
)
self.config_bag.context.get_settings().validate_properties(self)
self.properties = settings.getproperties(self,
2024-06-20 12:56:27 +02:00
apply_requires=apply_requires,
2024-04-24 15:39:17 +02:00
)
else:
self.properties = properties
if validate_properties:
self.config_bag.context.get_settings().validate_properties(self)
if apply_requires and self.option.impl_is_optiondescription():
if self.path and self.properties is not None:
self.transitive_properties = settings.calc_transitive_properties(self,
self.properties,
)
2024-06-20 12:56:27 +02:00
else:
self.transitive_properties = frozenset()
2024-06-20 12:56:27 +02:00
2024-04-24 15:39:17 +02:00
def __repr__(self):
return f'<SubConfig path={self.path}, index={self.index}>'
def dyn_to_subconfig(self,
child: Option,
validate_properties: bool,
2024-06-20 12:56:27 +02:00
*,
true_path: Optional[str]=None,
2024-04-24 15:39:17 +02:00
) -> List['SubConfig']:
config_bag = self.config_bag
for suffix in child.get_suffixes(self):
try:
name = child.impl_getname(suffix)
if not validate_properties:
properties = None
else:
properties = undefined
yield self.get_child(child,
None,
validate_properties,
suffix=suffix,
name=name,
properties=properties,
2024-06-20 12:56:27 +02:00
true_path=true_path,
2024-04-24 15:39:17 +02:00
)
except PropertiesOptionError as err:
if err.proptype in (['mandatory'], ['empty']):
raise err
def get_leadership_children(self,
validate_properties,
):
# it's a leadership so walk to leader and follower
# followers has specific length
leader, *followers = self.option.get_children()
yield self.get_child(leader,
None,
validate_properties,
)
for idx in range(self.get_length_leadership()):
for follower in followers:
try:
yield self.get_child(follower,
idx,
validate_properties,
)
except PropertiesOptionError as err:
if err.proptype in (['mandatory'], ['empty']):
raise err from err
def get_children(self,
validate_properties,
*,
uncalculated: bool=False,
2024-04-24 15:39:17 +02:00
):
if self.option.impl_is_leadership():
yield from self.get_leadership_children(validate_properties)
else:
for child in self.option.get_children():
if child.impl_is_dynoptiondescription() and not uncalculated:
2024-04-24 15:39:17 +02:00
yield from self.dyn_to_subconfig(child,
validate_properties,
)
else:
try:
yield self.get_child(child,
None,
validate_properties,
)
except PropertiesOptionError as err:
if err.proptype in (['mandatory'], ['empty']):
raise err
def get_child(self,
option: Option,
index: Optional[int],
validate_properties: bool,
*,
properties=undefined,
allow_dynoption: bool=False,
suffix: Optional[str]=None,
name: Optional[str]=None,
check_index: bool=True,
config_bag: ConfigBag=None,
true_path: Optional[str]=None,
) -> 'SubConfig':
# pylint: disable=too-many-branches,too-many-locals,too-many-arguments
if config_bag is None:
config_bag = self.config_bag
if not self.option.impl_is_optiondescription():
raise TypeError(f'"{self.path}" is not an optiondescription')
path = self.get_path(name,
option,
)
if suffix is None:
suffixes = self.suffixes
else:
if self.suffixes:
suffixes = self.suffixes + [suffix]
else:
suffixes = [suffix]
2024-06-20 12:56:27 +02:00
subsubconfig = SubConfig(option,
index,
path,
self.config_bag,
self,
suffixes,
properties=properties,
validate_properties=validate_properties,
true_path=true_path,
)
if check_index and index is not None:
if option.impl_is_optiondescription() or \
option.impl_is_symlinkoption() or \
not option.impl_is_follower():
raise ConfigError('index must be set only with a follower option')
length = self.get_length_leadership()
if index >= length:
raise LeadershipError(_(f'index "{index}" is greater than the leadership '
f'length "{length}" for option '
f'"{option.impl_get_display_name(subsubconfig)}"'))
return subsubconfig
2024-04-24 15:39:17 +02:00
def get_path(self,
name: str,
option: Option,
) -> str:
if name is None:
name = option.impl_getname()
if self.path is None:
path = name
else:
path = self.path + '.' + name
return path
def get_length_leadership(self):
"""Get the length of leader option (useful to know follower's length)
"""
if self._length is None:
cconfig_bag = self.config_bag.copy()
cconfig_bag.remove_validation()
leader = self.option.get_leader()
path = self.get_path(None,
leader,
)
subconfig = SubConfig(leader,
None,
path,
cconfig_bag,
self,
self.suffixes,
2024-06-20 12:56:27 +02:00
validate_properties=False,
2024-04-24 15:39:17 +02:00
)
self._length = len(cconfig_bag.context.get_value(subconfig))
return self._length
2024-06-20 12:56:27 +02:00
def get_common_child(self,
search_option: 'BaseOption',
true_path: Optional[str]=None,
validate_properties: bool=True,
):
current_option_path = self.option.impl_getpath()
search_option_path = search_option.impl_getpath()
common_path = get_common_path(current_option_path, search_option_path)
config_bag = self.config_bag
index = None
if not self.option.impl_is_optiondescription() and \
self.option.impl_is_follower() and search_option.impl_is_follower() and \
self.parent.option == search_option.impl_get_leadership():
index = self.index
search_child_number = 0
parents = [self.parent]
else:
if common_path:
parent = self.parent
common_parent_number = common_path.count('.') + 1
for idx in range(current_option_path.count('.') - common_parent_number):
parent = parent.parent
parents = [parent]
else:
common_parent_number = 0
parents = [config_bag.context.get_root(config_bag)]
search_child_number = search_option_path.count('.') - common_parent_number
subconfigs_is_a_list = False
if search_child_number:
if common_parent_number:
parent_paths = search_option_path.rsplit('.', search_child_number + 1)[1:-1]
else:
parent_paths = search_option_path.split('.')[:-1]
for parent_path in parent_paths:
new_parents = []
for parent in parents:
sub_option = parent.option.get_child(parent_path,
config_bag,
parent,
allow_dynoption=True,
)
if sub_option.impl_is_dynoptiondescription():
new_parents.extend(parent.dyn_to_subconfig(sub_option,
True,
true_path=true_path,
)
)
subconfigs_is_a_list = True
else:
new_parents.append(parent.get_child(sub_option,
None,
validate_properties,
true_path=true_path,
)
)
parents = new_parents
subconfigs = []
for parent in parents:
subconfigs.append(parent.get_child(search_option,
index,
validate_properties,
)
)
if subconfigs_is_a_list:
return subconfigs
return subconfigs[0]
2024-04-24 15:39:17 +02:00
class _Config(CCache):
2013-09-23 22:55:54 +02:00
"""Sub configuration management entry.
Tree if OptionDescription's responsability. SubConfig are generated
on-demand. A Config is also a SubConfig.
Root Config is call context below
"""
2017-11-12 14:33:05 +01:00
__slots__ = ('_impl_context',
'_impl_descr',
'_impl_path',
2023-04-27 11:34:35 +02:00
)
2017-11-12 14:33:05 +01:00
def __init__(self,
descr,
context,
subpath=None,
):
2019-02-23 19:06:23 +01:00
""" Configuration option management class
2013-02-19 11:24:17 +01:00
2012-10-05 16:00:07 +02:00
:param descr: describes the configuration schema
:type descr: an instance of ``option.OptionDescription``
2013-02-07 16:20:21 +01:00
:param context: the current root config
:type context: `Config`
:type subpath: `str` with the path name
2012-10-05 16:00:07 +02:00
"""
# main option description
self._impl_descr = descr
self._impl_context = context
self._impl_path = subpath
def get_description(self):
2023-05-11 15:44:48 +02:00
"""get root description
"""
assert self._impl_descr is not None, _('there is no option description for this config'
' (may be GroupConfig)')
return self._impl_descr
2017-11-13 22:45:53 +01:00
def get_settings(self):
2023-05-11 15:44:48 +02:00
"""get settings object
"""
return self._impl_settings # pylint: disable=no-member
def get_values(self):
2023-05-11 15:44:48 +02:00
"""get values object
"""
return self._impl_values # pylint: disable=no-member
2023-05-11 15:44:48 +02:00
def get_values_cache(self):
"""get cache for values
"""
return self._impl_values_cache # pylint: disable=no-member
2013-05-02 11:34:57 +02:00
# =============================================================================
# WALK
def find(self,
2023-04-27 11:34:35 +02:00
option_bag,
bytype,
byname,
byvalue,
raise_if_not_found=True,
only_path=undefined,
only_option=undefined,
2023-04-27 11:34:35 +02:00
with_option=False,
):
2013-05-02 11:34:57 +02:00
"""
convenience method for finding an option that lives only in the subtree
:param first: return only one option if True, a list otherwise
:return: find list or an exception if nothing has been found
"""
2023-05-11 15:44:48 +02:00
# pylint: disable=too-many-arguments,too-many-locals
def _filter_by_value(soption_bag):
2023-05-11 15:44:48 +02:00
value = self.get_value(soption_bag)
2017-11-13 22:45:53 +01:00
if isinstance(value, list):
2016-01-03 21:18:52 +01:00
return byvalue in value
2023-05-11 15:44:48 +02:00
return value == byvalue
2013-05-02 11:34:57 +02:00
2018-04-10 12:33:51 +02:00
found = False
if only_path is not undefined:
def _fake_iter():
2019-12-24 15:24:20 +01:00
yield only_option
options = _fake_iter()
else:
2023-04-27 11:34:35 +02:00
options = option_bag.option.get_children_recursively(bytype,
byname,
option_bag.config_bag,
)
for option in options:
2018-09-30 20:32:00 +02:00
path = option.impl_getpath()
2023-04-27 11:34:35 +02:00
soption_bag = OptionBag(option,
None,
option_bag.config_bag,
)
if byvalue is not undefined and not _filter_by_value(soption_bag):
2013-05-02 11:34:57 +02:00
continue
2023-05-11 15:44:48 +02:00
if option_bag.config_bag.properties:
#remove option with propertyerror, ...
2017-11-13 22:45:53 +01:00
try:
2024-04-24 15:39:17 +02:00
self.get_sub_config(option_bag.config_bag, # pylint: disable=no-member
path,
None,
validate_properties=True,
)
2017-11-13 22:45:53 +01:00
except PropertiesOptionError:
continue
2018-04-10 12:33:51 +02:00
found = True
if not with_option:
yield path
else:
yield path, option
self._find_return_results(found,
2023-04-27 11:34:35 +02:00
raise_if_not_found,
)
2017-11-23 16:56:14 +01:00
def _find_return_results(self,
2018-04-10 12:33:51 +02:00
found,
2017-11-23 16:56:14 +01:00
raise_if_not_found):
2018-04-10 12:33:51 +02:00
if not found and raise_if_not_found:
raise AttributeError(_("no option found in config"
" with these criteria"))
2013-05-02 11:34:57 +02:00
2023-04-27 11:34:35 +02:00
def _walk_valid_value(self,
2024-04-24 15:39:17 +02:00
subconfig,
only_mandatory,
2023-04-27 11:34:35 +02:00
):
2024-04-24 15:39:17 +02:00
value = self.get_value(subconfig,
need_help=False,
)
ori_config_bag = subconfig.config_bag
config_bag = ori_config_bag.copy()
if only_mandatory:
config_bag.properties |= {'mandatory', 'empty'}
subconfig.config_bag = config_bag
self.get_settings().validate_mandatory(subconfig,
value,
2023-04-27 11:34:35 +02:00
)
2024-04-24 15:39:17 +02:00
subconfig.config_bag = ori_config_bag
2023-04-27 11:34:35 +02:00
return value
2024-04-24 15:39:17 +02:00
def get_root(self,
config_bag: ConfigBag,
) -> SubConfig:
return SubConfig(config_bag.context.get_description(),
None,
None,
config_bag,
None,
None,
)
def get_sub_config(self,
config_bag,
path,
index,
*,
validate_properties: bool=True,
properties=undefined,
true_path: Optional[str]=None,
):
subconfig = self.get_root(config_bag)
if path is None:
paths = []
len_path = 0
else:
if '.' in path:
paths = path.split('.')
else:
paths = [path]
len_path = len(paths) - 1
for idx, name in enumerate(paths):
if idx != len_path:
index_ = None
true_path_ = None
else:
index_ = index
true_path_ = true_path
if not subconfig.option.impl_is_optiondescription():
raise TypeError(f'"{subconfig.true_path}" is not an optiondescription')
option = subconfig.option.get_child(name,
config_bag,
subconfig,
with_suffix=True,
)
if isinstance(option, tuple):
suffix, option = option
else:
suffix = None
subconfig = subconfig.get_child(option,
index_,
validate_properties,
properties=properties,
name=name,
suffix=suffix,
true_path=true_path_,
)
return subconfig
2023-04-27 11:34:35 +02:00
def walk(self,
2024-04-24 15:39:17 +02:00
root_subconfig: SubConfig,
2024-02-08 08:32:24 +01:00
*,
2024-04-24 15:39:17 +02:00
no_value: bool=False,
only_mandatory: bool=False,
2024-02-08 08:32:24 +01:00
validate_properties: bool=True,
2023-04-27 11:34:35 +02:00
):
2024-04-24 15:39:17 +02:00
if only_mandatory or no_value:
ret = []
2018-08-19 15:19:42 +02:00
else:
2024-04-24 15:39:17 +02:00
ret = {}
for subconfig in root_subconfig.get_children(validate_properties):
# pylint: disable=too-many-branches,too-many-locals,too-many-arguments,
if only_mandatory and subconfig.option.impl_is_symlinkoption():
continue
if subconfig.option.impl_is_optiondescription():
values = self.walk(subconfig,
no_value=no_value,
only_mandatory=only_mandatory,
validate_properties=validate_properties,
)
if only_mandatory or no_value:
ret.extend(values)
else:
ret[subconfig] = values
2023-04-27 11:34:35 +02:00
else:
2024-04-24 15:39:17 +02:00
if no_value:
ret.append(subconfig)
else:
option = self.walk_option(subconfig,
only_mandatory,
)
if only_mandatory:
if option:
ret.append(subconfig)
elif option[0]:
ret[subconfig] = option[1]
2023-04-27 11:34:35 +02:00
return ret
2024-04-24 15:39:17 +02:00
def walk_option(self,
subconfig: SubConfig,
only_mandatory: bool,
):
try:
value = self._walk_valid_value(subconfig,
only_mandatory,
)
except PropertiesOptionError as err:
if err.proptype in (['mandatory'], ['empty']):
if only_mandatory:
return True
else:
raise err from err
else:
if not only_mandatory:
return True, value
if only_mandatory:
return False
return False, None
# =============================================================================
# Manage value
2023-04-27 11:34:35 +02:00
def get_value(self,
2024-04-24 15:39:17 +02:00
subconfig,
2023-04-27 11:34:35 +02:00
need_help=True,
):
"""
:return: option's value if name is an option name, OptionDescription
otherwise
"""
2024-04-24 15:39:17 +02:00
subconfig = self._get(subconfig,
need_help,
)
if isinstance(subconfig, list):
2023-04-27 11:34:35 +02:00
value = []
2024-04-24 15:39:17 +02:00
follower_subconfig = None
2024-06-20 12:56:27 +02:00
is_follower = not subconfig or subconfig[0].option.impl_is_follower()
2024-04-24 15:39:17 +02:00
for sconfig in subconfig:
2024-06-20 12:56:27 +02:00
if not is_follower or follower_subconfig is None:
2024-04-24 15:39:17 +02:00
follower_subconfig = self.get_sub_config(sconfig.config_bag,
sconfig.path,
sconfig.index,
)
else:
follower_subconfig = follower_subconfig.parent.get_child(sconfig.option,
sconfig.index,
False,
)
value.append(self.get_value(follower_subconfig,
2023-04-27 11:34:35 +02:00
need_help=need_help,
))
else:
2024-04-24 15:39:17 +02:00
value = self.get_values().get_cached_value(subconfig)
if subconfig.option.impl_is_follower():
length = subconfig.parent.get_length_leadership()
follower_len = self.get_values().get_max_length(subconfig.path)
2023-04-27 11:34:35 +02:00
if follower_len > length:
2024-06-20 12:56:27 +02:00
option_name = subconfig.option.impl_get_display_name(subconfig)
2023-04-27 11:34:35 +02:00
raise LeadershipError(_(f'the follower option "{option_name}" '
f'has greater length ({follower_len}) than the leader '
f'length ({length})'))
2024-04-24 15:39:17 +02:00
self.get_settings().validate_mandatory(subconfig,
value,
2023-04-27 11:34:35 +02:00
)
return value
2023-04-27 11:34:35 +02:00
def _get(self,
2024-04-24 15:39:17 +02:00
subconfig: "SubConfig",
2023-05-11 15:44:48 +02:00
need_help: bool,
2024-06-20 12:56:27 +02:00
validate_properties: bool=True,
2024-04-24 15:39:17 +02:00
) -> "OptionBag":
2023-05-11 15:44:48 +02:00
# pylint: disable=too-many-locals
2024-04-24 15:39:17 +02:00
option = subconfig.option
if not option.impl_is_symlinkoption():
return subconfig
suboption = option.impl_getopt()
if suboption.issubdyn():
dynopt = suboption.getsubdyn()
2024-06-20 12:56:27 +02:00
return subconfig.get_common_child(suboption,
true_path=subconfig.path,
validate_properties=validate_properties,
)
2024-04-24 15:39:17 +02:00
if suboption.impl_is_follower():
subconfig = self.get_sub_config(subconfig.config_bag, # pylint: disable=no-member
suboption.impl_getpath(),
None,
2024-06-20 12:56:27 +02:00
validate_properties=validate_properties,
2024-04-24 15:39:17 +02:00
true_path=subconfig.path,
)
leadership_length = subconfig.parent.get_length_leadership()
ret = []
follower = subconfig.option
parent = subconfig.parent
for idx in range(leadership_length):
ret.append(parent.get_child(follower,
idx,
True,
))
return ret
s_subconfig = self.get_sub_config(subconfig.config_bag, # pylint: disable=no-member
suboption.impl_getpath(),
None,
2024-06-20 12:56:27 +02:00
validate_properties=validate_properties,
2024-04-24 15:39:17 +02:00
true_path=subconfig.path,
)
return self._get(s_subconfig,
need_help,
)
2024-04-24 15:39:17 +02:00
def get_owner(self,
subconfig: "SubConfig",
):
2023-05-11 15:44:48 +02:00
"""get owner
"""
2024-04-24 15:39:17 +02:00
subconfigs = self._get(subconfig,
need_help=True,
)
if isinstance(subconfigs, list):
for sc in subconfigs:
owner = self.get_owner(sc,
)
2023-05-11 15:44:48 +02:00
if owner != owners.default:
break
2023-04-27 11:34:35 +02:00
else:
2023-05-11 15:44:48 +02:00
owner = owners.default
else:
2024-04-24 15:39:17 +02:00
owner = self.get_values().getowner(subconfigs)
2023-04-27 11:34:35 +02:00
return owner
2013-04-03 12:20:26 +02:00
2024-04-24 15:39:17 +02:00
class _CommonConfig(_Config):
2018-08-14 22:15:40 +02:00
"abstract base class for the Config, KernelGroupConfig and the KernelMetaConfig"
2017-12-19 23:11:45 +01:00
__slots__ = ('_impl_values',
'_impl_values_cache',
2017-12-19 23:11:45 +01:00
'_impl_settings',
'properties_cache',
'_impl_permissives_cache',
2019-08-05 22:31:56 +02:00
'parents',
2023-05-11 15:44:48 +02:00
'impl_type',
)
2013-04-03 12:20:26 +02:00
def _impl_build_all_caches(self, descr):
2015-05-03 09:56:03 +02:00
if not descr.impl_already_build_caches():
2023-05-11 15:44:48 +02:00
descr._group_type = groups.root # pylint: disable=protected-access
2024-02-08 08:32:24 +01:00
descr._build_cache(self._display_name) # pylint: disable=no-member,protected-access
2019-12-24 15:24:20 +01:00
if not hasattr(descr, '_cache_force_store_values'):
raise ConfigError(_('option description seems to be part of an other '
'config'))
2019-08-05 22:31:56 +02:00
def get_parents(self):
2023-05-11 15:44:48 +02:00
"""get parents
"""
for parent in self.parents: # pylint: disable=no-member
2019-08-05 22:31:56 +02:00
yield parent()
# information
def impl_set_information(self,
config_bag,
key,
value,
):
"""updates the information's attribute
:param key: information's key (ex: "help", "doc"
:param value: information's value (ex: "the help string")
"""
2023-05-11 15:44:48 +02:00
self._impl_values.set_information(None, # pylint: disable=no-member
key,
value,
)
for option in self.get_description()._cache_dependencies_information.get(key, []): # pylint: disable=protected-access
2024-04-24 15:39:17 +02:00
#option_bag = OptionBag(option,
# None,
# config_bag,
# properties=None,
# )
option_bag = None
2023-05-11 15:44:48 +02:00
self.reset_cache(option_bag)
def impl_get_information(self,
2024-06-20 12:56:27 +02:00
subconfig,
key,
default,
):
"""retrieves one information's item
:param key: the item string (ex: "help")
"""
return self._impl_values.get_information(None, # pylint: disable=no-member
key,
default,
)
def impl_del_information(self,
key,
raises=True,
):
2023-05-11 15:44:48 +02:00
"""delete an information
"""
self._impl_values.del_information(key, # pylint: disable=no-member
raises,
)
def impl_list_information(self):
2023-05-11 15:44:48 +02:00
"""list information keys for context
"""
return self._impl_values.list_information() # pylint: disable=no-member
2012-10-12 11:35:07 +02:00
2024-04-24 15:39:17 +02:00
def gen_fake_context(self) -> 'KernelConfig':
2023-05-11 15:44:48 +02:00
"""generate a fake values to improve validation when assign a new value
"""
export = deepcopy(self.get_values()._values) # pylint: disable=protected-access
2024-04-24 15:39:17 +02:00
fake_context = KernelConfig(self._impl_descr,
force_values=export,
force_settings=self.get_settings(),
name=self._impl_name, # pylint: disable=no-member
)
fake_context.parents = self.parents # pylint: disable=no-member
return fake_context
2015-04-18 22:53:45 +02:00
def duplicate(self,
force_values=None,
force_settings=None,
metaconfig_prefix=None,
child=None,
deep=None,
name=None,
):
2023-05-11 15:44:48 +02:00
"""duplication config
"""
# pylint: disable=too-many-arguments
if name is None:
2023-05-11 15:44:48 +02:00
name = self._impl_name # pylint: disable=no-member
2018-09-05 20:22:16 +02:00
if isinstance(self, KernelConfig):
duplicated_config = KernelConfig(self._impl_descr,
_duplicate=True,
force_values=force_values,
force_settings=force_settings,
name=name,
)
2018-10-31 08:00:19 +01:00
else:
duplicated_config = KernelMetaConfig([],
_duplicate=True,
optiondescription=self._impl_descr,
name=name,
)
duplicated_values = duplicated_config.get_values()
duplicated_settings = duplicated_config.get_settings()
2023-05-11 15:44:48 +02:00
duplicated_values._values = deepcopy(self.get_values()._values) # pylint: disable=protected-access
duplicated_values._informations = deepcopy(self.get_values()._informations) # pylint: disable=protected-access
duplicated_settings._properties = deepcopy(self.get_settings()._properties) # pylint: disable=protected-access
duplicated_settings._permissives = deepcopy(self.get_settings()._permissives) # pylint: disable=protected-access
duplicated_settings.ro_append = self.get_settings().ro_append
duplicated_settings.rw_append = self.get_settings().rw_append
duplicated_settings.ro_remove = self.get_settings().ro_remove
duplicated_settings.rw_remove = self.get_settings().rw_remove
2024-06-20 12:56:27 +02:00
# duplicated_settings.default_properties = self.get_settings().default_properties
duplicated_config.reset_cache(None, None)
2018-09-13 17:00:52 +02:00
if child is not None:
2023-05-11 15:44:48 +02:00
duplicated_config._impl_children.append(child) # pylint: disable=protected-access
2019-08-05 22:31:56 +02:00
child.parents.append(weakref.ref(duplicated_config))
2023-05-11 15:44:48 +02:00
if self.parents: # pylint: disable=no-member
if deep is not None:
2023-05-11 15:44:48 +02:00
for parent in self.parents: # pylint: disable=no-member
wparent = parent()
if wparent not in deep:
deep.append(wparent)
subname = wparent.impl_getname()
if metaconfig_prefix:
subname = metaconfig_prefix + subname
duplicated_config = wparent.duplicate(deep=deep,
metaconfig_prefix=metaconfig_prefix,
child=duplicated_config,
name=subname,
)
2018-09-05 20:22:16 +02:00
else:
2023-05-11 15:44:48 +02:00
duplicated_config.parents = self.parents # pylint: disable=no-member
for parent in self.parents: # pylint: disable=no-member
parent()._impl_children.append(duplicated_config) # pylint: disable=protected-access
2018-09-13 17:00:52 +02:00
return duplicated_config
2015-07-24 17:54:10 +02:00
def get_config_path(self):
2023-05-11 15:44:48 +02:00
"""get config path
"""
path = self.impl_getname()
2023-05-11 15:44:48 +02:00
for parent in self.parents: # pylint: disable=no-member
wparent = parent()
if wparent is None: # pragma: no cover
2023-05-11 15:44:48 +02:00
raise ConfigError(_(f'parent of {self._impl_name} not already exists')) # pylint: disable=no-member
path = parent().get_config_path() + '.' + path
2019-08-05 22:31:56 +02:00
return path
def impl_getname(self):
2023-05-11 15:44:48 +02:00
"""get config name
"""
return self._impl_name # pylint: disable=no-member
2013-09-30 16:22:08 +02:00
# ____________________________________________________________
2018-08-14 22:15:40 +02:00
class KernelConfig(_CommonConfig):
2023-05-11 15:44:48 +02:00
"""main configuration management entry
"""
# pylint: disable=too-many-instance-attributes
2018-09-29 18:52:13 +02:00
__slots__ = ('__weakref__',
'_impl_name',
'_display_name',
2020-01-22 20:46:18 +01:00
'_impl_symlink',
2023-05-11 15:44:48 +02:00
'_storage',
)
2018-09-29 18:52:13 +02:00
impl_type = 'config'
2013-09-30 16:22:08 +02:00
def __init__(self,
descr,
force_values=None,
force_settings=None,
name=None,
display_name=None,
_duplicate=False,
):
2019-02-23 19:06:23 +01:00
""" Configuration option management class
2013-09-30 16:22:08 +02:00
:param descr: describes the configuration schema
:type descr: an instance of ``option.OptionDescription``
:param context: the current root config
:type context: `Config`
"""
2023-05-11 15:44:48 +02:00
# pylint: disable=too-many-arguments,too-many-arguments
self._display_name = display_name
2019-08-05 22:31:56 +02:00
self.parents = []
self._impl_symlink = []
self._impl_name = name
2019-02-23 19:06:23 +01:00
if isinstance(descr, Leadership):
raise ConfigError(_('cannot set leadership object has root optiondescription'))
2018-04-12 23:04:33 +02:00
if isinstance(descr, DynOptionDescription):
msg = _('cannot set dynoptiondescription object has root optiondescription')
raise ConfigError(msg)
if force_settings is not None and force_values is not None:
self._impl_settings = force_settings
self._impl_permissives_cache = Cache()
self.properties_cache = Cache()
self._impl_values = Values(force_values)
self._impl_values_cache = Cache()
else:
self._impl_settings = Settings()
self._impl_permissives_cache = Cache()
self.properties_cache = Cache()
self._impl_values = Values()
self._impl_values_cache = Cache()
2019-12-24 15:24:20 +01:00
self._impl_context = weakref.ref(self)
if None in [force_settings, force_values]:
self._impl_build_all_caches(descr)
super().__init__(descr,
self._impl_context,
None,
)
2020-01-22 20:46:18 +01:00
2018-08-14 22:15:40 +02:00
class KernelGroupConfig(_CommonConfig):
2023-05-11 15:44:48 +02:00
"""Group a config with same optiondescription tree
"""
__slots__ = ('__weakref__',
'_impl_children',
'_impl_name',
'_display_name',
)
2018-09-29 18:52:13 +02:00
impl_type = 'group'
2012-10-12 11:35:07 +02:00
def __init__(self,
children,
display_name=None,
name=None,
2023-05-11 15:44:48 +02:00
_descr=None,
):
# pylint: disable=super-init-not-called
names = []
for child in children:
name_ = child._impl_name
names.append(name_)
if len(names) != len(set(names)):
2023-05-11 15:44:48 +02:00
while range(1, len(names) + 1):
name = names.pop(0)
if name in names:
raise ConflictError(_('config name must be uniq in '
2018-04-11 16:36:15 +02:00
'groupconfig for "{0}"').format(name))
2013-09-17 09:02:10 +02:00
self._impl_children = children
2019-08-05 22:31:56 +02:00
self.parents = []
self._display_name = display_name
if name:
self._impl_name = name
self._impl_context = weakref.ref(self)
self._impl_descr = _descr
self._impl_path = None
2013-09-17 09:02:10 +02:00
def get_children(self):
2023-05-11 15:44:48 +02:00
"""get all children
"""
2013-09-17 09:02:10 +02:00
return self._impl_children
def reset_cache(self,
option_bag,
resetted_opts=None,
):
2017-12-19 23:11:45 +01:00
if resetted_opts is None:
resetted_opts = []
2018-10-31 18:26:20 +01:00
if isinstance(self, KernelMixConfig):
super().reset_cache(option_bag,
resetted_opts=copy(resetted_opts),
)
2013-09-17 09:02:10 +02:00
for child in self._impl_children:
if option_bag is not None:
coption_bag = option_bag.copy()
cconfig_bag = coption_bag.config_bag.copy()
cconfig_bag.context = child
coption_bag.config_bag = cconfig_bag
else:
coption_bag = None
child.reset_cache(coption_bag,
resetted_opts=copy(resetted_opts),
)
def set_value(self,
2023-04-27 11:34:35 +02:00
option_bag,
value,
only_config=False,
):
2018-08-14 22:15:40 +02:00
"""Setattr not in current KernelGroupConfig, but in each children
2013-09-30 16:22:08 +02:00
"""
ret = []
2013-09-17 09:02:10 +02:00
for child in self._impl_children:
2023-04-27 11:34:35 +02:00
cconfig_bag = option_bag.config_bag.copy()
2018-08-03 22:56:04 +02:00
cconfig_bag.context = child
2018-10-31 08:00:19 +01:00
if isinstance(child, KernelGroupConfig):
2023-04-27 11:34:35 +02:00
ret.extend(child.set_value(option_bag,
value,
2023-04-27 11:34:35 +02:00
only_config=only_config,
))
2018-10-31 08:00:19 +01:00
else:
settings = child.get_settings()
properties = settings.get_context_properties(child.properties_cache)
permissives = settings.get_context_permissives()
2019-12-24 15:24:20 +01:00
cconfig_bag.properties = properties
cconfig_bag.permissives = permissives
2018-10-31 08:00:19 +01:00
try:
2024-04-24 15:39:17 +02:00
# GROUP
2023-04-27 11:34:35 +02:00
coption_bag = child.get_sub_option_bag(cconfig_bag,
option_bag.path,
option_bag.index,
False,
2024-04-24 15:39:17 +02:00
)
2023-04-27 11:34:35 +02:00
child.set_value(coption_bag,
value,
)
2018-10-31 08:00:19 +01:00
except PropertiesOptionError as err:
2023-05-11 15:44:48 +02:00
# pylint: disable=protected-access
2018-10-31 08:00:19 +01:00
ret.append(PropertiesOptionError(err._option_bag,
err.proptype,
err._settings,
err._opt_type,
err._name,
err._orig_opt))
2019-02-23 19:06:23 +01:00
except (ValueError, LeadershipError, AttributeError) as err:
2018-10-31 08:00:19 +01:00
ret.append(err)
return ret
2017-07-16 23:11:12 +02:00
def find_group(self,
config_bag,
byname=None,
bypath=undefined,
byoption=undefined,
byvalue=undefined,
raise_if_not_found=True,
2023-04-27 11:34:35 +02:00
_sub=False,
):
2018-08-14 22:15:40 +02:00
"""Find first not in current KernelGroupConfig, but in each children
2013-09-30 16:22:08 +02:00
"""
2023-05-11 15:44:48 +02:00
# pylint: disable=too-many-arguments
# if KernelMetaConfig, all children have same OptionDescription in
# context so search only one time the option for all children
if bypath is undefined and byname is not None and \
2023-04-27 11:34:35 +02:00
self.impl_type == 'meta':
root_option_bag = OptionBag(self.get_description(),
None,
config_bag,
)
2023-05-11 15:44:48 +02:00
next(self.find(root_option_bag,
bytype=None,
byname=byname,
byvalue=undefined,
raise_if_not_found=raise_if_not_found,
with_option=True,
))
byname = None
2018-04-10 12:33:51 +02:00
ret = []
2013-09-17 09:02:10 +02:00
for child in self._impl_children:
2018-08-14 22:15:40 +02:00
if isinstance(child, KernelGroupConfig):
ret.extend(child.find_group(byname=byname,
bypath=bypath,
byoption=byoption,
byvalue=byvalue,
config_bag=config_bag,
raise_if_not_found=False,
_sub=True))
2018-04-10 12:33:51 +02:00
else:
2019-12-24 15:24:20 +01:00
cconfig_bag = config_bag.copy()
cconfig_bag.context = child
2023-04-27 11:34:35 +02:00
if cconfig_bag.properties is None:
settings = child.get_settings()
properties = settings.get_context_properties(child.properties_cache)
permissives = settings.get_context_permissives()
cconfig_bag.properties = properties
cconfig_bag.permissives = permissives
root_option_bag = OptionBag(child.get_description(),
None,
cconfig_bag,
)
2023-05-11 15:44:48 +02:00
try:
next(child.find(root_option_bag,
None,
byname,
byvalue,
raise_if_not_found=False,
only_path=bypath,
only_option=byoption,
))
2018-04-10 12:33:51 +02:00
ret.append(child)
2023-05-11 15:44:48 +02:00
except StopIteration:
pass
if not _sub:
2023-05-11 15:44:48 +02:00
self._find_return_results(ret != [], # pylint: disable=use-implicit-booleaness-not-comparison
raise_if_not_found,
)
return ret
2013-05-02 11:34:57 +02:00
def reset(self,
2023-04-27 11:34:35 +02:00
path: str,
config_bag: ConfigBag,
2023-05-11 15:44:48 +02:00
) -> None:
"""reset value for specified path
"""
2018-09-29 18:52:13 +02:00
for child in self._impl_children:
2023-04-27 11:34:35 +02:00
settings = child.get_settings()
cconfig_bag = config_bag.copy()
cconfig_bag.context = child
settings = child.get_settings()
properties = settings.get_context_properties(child.properties_cache)
permissives = settings.get_context_permissives()
2023-04-27 11:34:35 +02:00
cconfig_bag.properties = properties
cconfig_bag.permissives = permissives
cconfig_bag.remove_validation()
2024-04-24 15:39:17 +02:00
# GROUP
2023-04-27 11:34:35 +02:00
option_bag = child.get_sub_option_bag(cconfig_bag,
path,
None,
False,
)[-1]
child.get_values().reset(option_bag)
2018-09-29 18:52:13 +02:00
2017-11-23 16:56:14 +01:00
def getconfig(self,
2023-05-11 15:44:48 +02:00
name: str,
) -> KernelConfig:
"""get a child from a config name
"""
for child in self._impl_children:
if name == child.impl_getname():
return child
raise ConfigError(_('unknown config "{}"').format(name))
2014-11-10 23:15:08 +01:00
2020-01-22 20:46:18 +01:00
2018-10-31 18:26:20 +01:00
class KernelMixConfig(KernelGroupConfig):
2023-05-11 15:44:48 +02:00
"""Kernel mixconfig: this config can have differents optiondescription tree
"""
# pylint: disable=too-many-instance-attributes
__slots__ = ('_impl_symlink',
'_storage',
)
2018-10-31 18:26:20 +01:00
impl_type = 'mix'
2013-09-30 16:22:08 +02:00
def __init__(self,
optiondescription,
children,
name=None,
display_name=None,
_duplicate=False,
):
self._impl_name = name
self._impl_symlink = []
2013-09-30 16:22:08 +02:00
for child in children:
2018-10-31 18:38:44 +01:00
if not isinstance(child, (KernelConfig, KernelMixConfig)):
raise TypeError(_("child must be a Config, MixConfig or MetaConfig"))
2019-08-05 22:31:56 +02:00
child.parents.append(weakref.ref(self))
self._impl_settings = Settings()
self._impl_settings._properties = deepcopy(self._impl_settings._properties)
self._impl_settings._permissives = deepcopy(self._impl_settings._permissives)
self._impl_permissives_cache = Cache()
self.properties_cache = Cache()
self._impl_values = Values()
self._impl_values._values = deepcopy(self._impl_values._values)
self._impl_values_cache = Cache()
self._display_name = display_name
self._impl_build_all_caches(optiondescription)
super().__init__(children,
_descr=optiondescription,
display_name=display_name,
)
def set_value(self,
2023-04-27 11:34:35 +02:00
option_bag,
value,
only_config=False,
force_default=False,
force_dont_change_value=False,
force_default_if_same=False,
):
"""only_config: could be set if you want modify value in all Config included in
2018-08-14 22:15:40 +02:00
this KernelMetaConfig
"""
2023-05-11 15:44:48 +02:00
# pylint: disable=too-many-branches,too-many-nested-blocks,too-many-locals,too-many-arguments
2023-04-27 11:34:35 +02:00
ret = []
if only_config:
if force_default or force_default_if_same or force_dont_change_value:
raise ValueError(_('force_default, force_default_if_same or '
'force_dont_change_value cannot be set with'
' only_config'))
2023-04-27 11:34:35 +02:00
else:
if force_default or force_default_if_same or force_dont_change_value:
if force_default and force_dont_change_value:
raise ValueError(_('force_default and force_dont_change_value'
' cannot be set together'))
for child in self._impl_children:
cconfig_bag = option_bag.config_bag.copy()
cconfig_bag.context = child
settings = child.get_settings()
properties = settings.get_context_properties(child.properties_cache)
cconfig_bag.properties = properties
cconfig_bag.permissives = settings.get_context_permissives()
try:
if self.impl_type == 'meta':
obj = self
2018-10-31 08:00:19 +01:00
else:
2023-04-27 11:34:35 +02:00
obj = child
2023-05-11 15:44:48 +02:00
validate_properties = not force_default and not force_default_if_same
2024-04-24 15:39:17 +02:00
# MIX
2023-04-27 11:34:35 +02:00
moption_bag = obj.get_sub_option_bag(cconfig_bag,
option_bag.path,
option_bag.index,
2023-05-11 15:44:48 +02:00
validate_properties,
2023-04-27 11:34:35 +02:00
)[-1]
if force_default_if_same:
if not child.get_values().hasvalue(option_bag.path):
child_value = undefined
else:
child_value = child.get_value(moption_bag)
if force_default or (force_default_if_same and value == child_value):
child.get_values().reset(moption_bag)
continue
if force_dont_change_value:
child_value = child.get_value(moption_bag)
if value != child_value:
child.set_value(moption_bag,
child_value,
)
except PropertiesOptionError as err:
2023-05-11 15:44:48 +02:00
# pylint: disable=protected-access
2023-04-27 11:34:35 +02:00
ret.append(PropertiesOptionError(err._option_bag,
err.proptype,
err._settings,
err._opt_type,
err._name,
err._orig_opt,
))
except (ValueError, LeadershipError, AttributeError) as err:
ret.append(err)
2018-01-03 21:07:51 +01:00
try:
2024-04-24 15:39:17 +02:00
# MIX
2023-04-27 11:34:35 +02:00
moption_bag = self.get_sub_option_bag(option_bag.config_bag,
option_bag.path,
option_bag.index,
not only_config,
)[-1]
if only_config:
ret = super().set_value(moption_bag,
value,
only_config=only_config,
)
2019-12-24 15:24:20 +01:00
else:
2023-04-27 11:34:35 +02:00
_CommonConfig.set_value(self,
moption_bag,
value,
)
2019-02-23 19:06:23 +01:00
except (PropertiesOptionError, ValueError, LeadershipError) as err:
2018-01-03 21:07:51 +01:00
ret.append(err)
return ret
2017-07-08 15:59:56 +02:00
def reset(self,
2023-05-11 15:44:48 +02:00
path: str,
only_children: bool,
config_bag: ConfigBag,
) -> None:
"""reset value for a specified path
"""
# pylint: disable=arguments-differ
2018-08-02 22:35:40 +02:00
rconfig_bag = config_bag.copy()
2018-08-17 23:11:25 +02:00
rconfig_bag.remove_validation()
2018-10-31 16:08:22 +01:00
if self.impl_type == 'meta':
2024-04-24 15:39:17 +02:00
# MIX
2023-04-27 11:34:35 +02:00
option_bag = self.get_sub_option_bag(config_bag,
path,
None,
True,
)[-1]
2018-10-31 16:08:22 +01:00
elif not only_children:
try:
2024-04-24 15:39:17 +02:00
# MIX
2023-04-27 11:34:35 +02:00
option_bag = self.get_sub_option_bag(rconfig_bag,
path,
None,
True,
)[-1]
2018-10-31 16:08:22 +01:00
except AttributeError:
only_children = True
for child in self._impl_children:
2018-10-31 16:08:22 +01:00
rconfig_bag.context = child
try:
if self.impl_type == 'meta':
moption_bag = option_bag
moption_bag.config_bag = rconfig_bag
else:
2024-04-24 15:39:17 +02:00
# MIX
2023-04-27 11:34:35 +02:00
moption_bag = child.get_sub_option_bag(rconfig_bag,
path,
None,
True,
)[-1]
child.get_values().reset(moption_bag)
2018-10-31 16:08:22 +01:00
except AttributeError:
pass
2018-10-31 18:26:20 +01:00
if isinstance(child, KernelMixConfig):
child.reset(path,
False,
rconfig_bag,
)
2018-10-31 16:08:22 +01:00
if not only_children:
option_bag.config_bag = config_bag
self.get_values().reset(option_bag)
def new_config(self,
name=None,
type_='config',
):
"""Create a new config/metaconfig/mixconfig and add it to this MixConfig"""
if name:
for child in self._impl_children:
if child.impl_getname() == name:
raise ConflictError(_('config name must be uniq in '
'groupconfig for {0}').format(child))
2020-03-04 15:39:47 +01:00
assert type_ in ('config', 'metaconfig', 'mixconfig'), _('unknown type {}').format(type_)
if type_ == 'config':
config = KernelConfig(self._impl_descr,
name=name)
2020-03-04 15:39:47 +01:00
elif type_ == 'metaconfig':
config = KernelMetaConfig([],
optiondescription=self._impl_descr,
name=name,
)
2020-03-04 15:39:47 +01:00
elif type_ == 'mixconfig':
config = KernelMixConfig(children=[],
optiondescription=self._impl_descr,
name=name,
)
2020-03-04 15:39:47 +01:00
# Copy context properties/permissives
settings = config.get_settings()
2024-06-20 12:56:27 +02:00
properties = settings.get_context_properties()
settings.set_context_properties(properties,
config,
)
settings.set_context_permissives(settings.get_context_permissives())
settings.ro_append = settings.ro_append
settings.rw_append = settings.rw_append
settings.ro_remove = settings.ro_remove
settings.rw_remove = settings.rw_remove
2024-06-20 12:56:27 +02:00
# settings.default_properties = settings.default_properties
2020-03-04 15:39:47 +01:00
config.parents.append(weakref.ref(self))
self._impl_children.append(config)
return config
def add_config(self,
config,
):
"""Add a child config to a mix config"""
if not config.impl_getname():
raise ConfigError(_('config added has no name, the name is mandatory'))
if config.impl_getname() in [child.impl_getname() for child in self._impl_children]:
2020-11-21 19:26:50 +01:00
raise ConflictError(_('config name "{0}" is not uniq in '
'groupconfig "{1}"').format(config.impl_getname(),
self.impl_getname()),
)
2019-08-05 22:31:56 +02:00
config.parents.append(weakref.ref(self))
self._impl_children.append(config)
config.reset_cache(None, None)
def remove_config(self,
name,
):
"""Remove a child config to a mix config by it's name"""
for current_index, child in enumerate(self._impl_children):
if name == child.impl_getname():
child.reset_cache(None, None)
2019-08-05 22:31:56 +02:00
break
else:
raise ConfigError(_(f'cannot find the config {name}'))
for child_index, parent in enumerate(child.parents):
if parent() == self:
break
else: # pragma: no cover
raise ConfigError(_('cannot find the config {}').format(self.impl_getname()))
self._impl_children.pop(current_index)
child.parents.pop(child_index)
2019-08-05 22:31:56 +02:00
return child
2018-10-31 08:00:19 +01:00
2018-10-31 18:26:20 +01:00
class KernelMetaConfig(KernelMixConfig):
"""Meta config
"""
2018-10-31 08:00:19 +01:00
__slots__ = tuple()
impl_type = 'meta'
def __init__(self,
children,
optiondescription=None,
name=None,
display_name=None,
_duplicate=False,
):
2018-10-31 08:00:19 +01:00
descr = None
if optiondescription is not None:
if not _duplicate:
new_children = []
for child_name in children:
assert isinstance(child_name, str), _('MetaConfig with optiondescription'
2023-05-11 15:44:48 +02:00
' must have string has child, '
'not {}').format(child_name)
new_children.append(KernelConfig(optiondescription, name=child_name))
2018-10-31 08:00:19 +01:00
children = new_children
descr = optiondescription
for child in children:
2019-02-25 20:30:20 +01:00
if __debug__ and not isinstance(child, (KernelConfig,
KernelMetaConfig)):
2018-10-31 18:38:44 +01:00
raise TypeError(_("child must be a Config or MetaConfig"))
2018-10-31 08:00:19 +01:00
if descr is None:
descr = child.get_description()
elif descr is not child.get_description():
2018-10-31 08:00:19 +01:00
raise ValueError(_('all config in metaconfig must '
'have the same optiondescription'))
super().__init__(descr,
children,
name=name,
display_name=display_name,
)
def add_config(self,
config,
):
if self._impl_descr is not config.get_description():
raise ValueError(_('metaconfig must '
'have the same optiondescription'))
super().add_config(config)