# -*- coding: utf-8 -*- # Copyright (C) 2017-2018 Team tiramisu (see AUTHORS for all contributors) # # 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. # # 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. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # ____________________________________________________________ from inspect import ismethod, getdoc, signature from time import time from copy import deepcopy from typing import List, Any, Optional, Callable, Union, Dict from .error import APIError, ConfigError, SlaveError, PropertiesOptionError from .i18n import _ from .setting import ConfigBag, OptionBag, owners, groups, Undefined, undefined, FORBIDDEN_SET_PROPERTIES from .config import KernelConfig, SubConfig, KernelGroupConfig, KernelMetaConfig from .option import ChoiceOption, OptionDescription TIRAMISU_VERSION = 3 EXCLUDE_HELP = ('help', '_get_option', '_test_slave_index') class TiramisuHelp: icon = '\u2937' tmpl_help = '{0}{1} {2}: \n{0} {3}\n' def help(self, init: bool=True, space: str="", root: str='', _display: bool=True, _valid: bool=False) -> List[str]: options = [] if init and isinstance(self, TiramisuAPI): options.append(self.tmpl_help.format(space, self.icon, root + 'unrestraint', _('access to option without property restriction'))) options.append(self.tmpl_help.format(space, self.icon, root + 'forcepermissive', _('access to option without verifying permissive property'))) root = '[unrestraint.|forcepermissive.]' if 'registers' in dir(self): modules = list(self.registers.keys()) modules.sort() for module_name in modules: module = self.registers[module_name] try: instance_module = module(None) except TypeError: instance_module = module(None, None, None) if isinstance(instance_module, TiramisuDispatcher): if _valid and not getdoc(module.__call__): # pragma: no cover raise Exception('unknown doc for {}'.format('__call__')) module_doc = _(getdoc(module.__call__)) module_signature = signature(module.__call__) module_args = [str(module_signature.parameters[key]) for key in list(module_signature.parameters.keys())[1:]] module_args = '(' + ', '.join(module_args) + ')' options.append(self.tmpl_help.format(space, self.icon, root + module_name + module_args, module_doc)) if hasattr(module, 'subhelp'): instance_submodule = module.subhelp(None, None, None, None, None) options.extend(instance_submodule.help(init=False, space=space + ' ', root=root + module_name + module_args + '.')) else: root = root + '[config(path).]' if isinstance(instance_module, CommonTiramisuOption): if _valid and not getdoc(module): # pragma: no cover raise Exception('unknown doc for {}'.format(module.__class__.__name__)) module_doc = _(getdoc(module)) options.append(self.tmpl_help.format(space, self.icon, root + module_name, module_doc)) if isinstance(instance_module, TiramisuContext): if _valid and not getdoc(module): # pragma: no cover raise Exception('unknown doc for {}'.format(module.__class__.__name__)) module_doc = _(getdoc(module)) options.append(self.tmpl_help.format(space, self.icon, root + module_name, module_doc)) options.extend(instance_module.help(init=False, space=space + ' ', root=root + '{}.'.format(module_name))) funcs = dir(self) funcs.sort() for func_name in funcs: if not func_name.startswith('__') and not func_name in EXCLUDE_HELP: func = getattr(self, func_name) if ismethod(func): module_signature = signature(func) module_args = list(module_signature.parameters.keys()) module_args = [str(module_signature.parameters[key]) for key in module_signature.parameters.keys()] module_args = '(' + ', '.join(module_args) + ')' if func_name.startswith('_'): func_name = func_name[1:] if _valid and not getdoc(func): # pragma: no cover raise Exception('unknown doc for {}'.format(func.__name__)) options.append(self.tmpl_help.format(space, self.icon, root + func_name + module_args, _(getdoc(func)))) if init: if _display: # pragma: no cover print('\n'.join(options)) else: return options class CommonTiramisu(TiramisuHelp): allow_optiondescription = True registers = {} def _get_option(self) -> Any: option = self.option_bag.option if option is None: option = self.subconfig.cfgimpl_get_description().impl_getchild(self._name, self.option_bag.config_bag, self.subconfig) self.option_bag.set_option(option, self.option_bag.path, self.option_bag.index, self.option_bag.config_bag) self.option_bag.config_bag.context.cfgimpl_get_settings().validate_properties(self.option_bag) index = self.option_bag.index if index is not None: if option.impl_is_optiondescription() or not option.impl_is_master_slaves('slave'): raise APIError('index must be set only with a slave option') self._length = self.subconfig.cfgimpl_get_length_slave(self.option_bag) if index >= self._length: raise SlaveError(_('index "{}" is higher than the master length "{}" ' 'for option "{}"').format(index, self._length, option.impl_get_display_name())) if not self.allow_optiondescription and option.impl_is_optiondescription(): raise APIError(_('option must not be an optiondescription')) return option class CommonTiramisuOption(CommonTiramisu): allow_optiondescription = False slave_need_index = True def __init__(self, name: str, subconfig: Union[KernelConfig, SubConfig], option_bag: OptionBag) -> None: self.option_bag = option_bag self._name = name self.subconfig = subconfig if option_bag is not None: # for help() self._get_option() if option_bag.config_bag is not None and self.slave_need_index: self._test_slave_index() def _test_slave_index(self) -> None: option = self.option_bag.option if not option.impl_is_optiondescription(): if self.option_bag.index is None and option.impl_is_master_slaves('slave'): raise APIError(_('index must be set with the slave option "{}"').format(self.option_bag.path)) elif self.option_bag.index is not None and not option.impl_is_master_slaves('slave'): raise APIError(_('index must be set only with a slave option, not for "{}"').format(self.option_bag.path)) def __getattr__(self, name): if not hasattr(CommonTiramisuOption, name): raise APIError(_('unknown method {}').format(name)) else: super().__getattribute__(name) class TiramisuOptionOption(CommonTiramisuOption): """manage option""" allow_optiondescription = True slave_need_index = False def get(self): """get Tiramisu option""" return self.option_bag.option def _ismulti(self): """test if option could have multi value""" option = self.option_bag.option return option.impl_is_multi() def _issubmulti(self): """test if option could have submulti value""" option = self.option_bag.option return option.impl_is_submulti() def ismasterslaves(self): """test if option is a master or a slave""" option = self.option_bag.option return option.impl_is_master_slaves() def _ismaster(self): """test if option is a master""" option = self.option_bag.option return option.impl_is_master_slaves('master') def _isslave(self): """test if option is a slave""" option = self.option_bag.option return option.impl_is_master_slaves('slave') def doc(self): """get option document""" option = self.option_bag.option return option.impl_get_display_name() def name(self): """get option name""" return self._name def path(self) -> str: """get option path""" return self.option_bag.path def _default(self): """get default value for an option (not for optiondescription)""" option = self.option_bag.option return option.impl_getdefault() def _defaultmulti(self): """get default value when added a value for a multi option (not for optiondescription)""" option = self.option_bag.option return option.impl_getdefault_multi() def has_dependency(self, self_is_dep=True): """test if option has dependency""" option = self.option_bag.option return option.impl_has_dependency(self_is_dep) def _consistencies(self): """get consistencies for an option (not for optiondescription)""" option = self.option_bag.option return option.get_consistencies() def _callbacks(self): """get callbacks for an option (not for optiondescription)""" option = self.option_bag.option return option.impl_get_callback() def requires(self): """get requires for an option""" option = self.option_bag.option return option.impl_getrequires() def __getattr__(self, name: str) -> Callable: option = self.option_bag.option if not option.impl_is_optiondescription() and not name.startswith('_'): return getattr(self, '_' + name) raise APIError(_('{} is unknown').format(name)) def isoptiondescription(self): """test if option is an optiondescription""" option = self.option_bag.option return option.impl_is_optiondescription() class TiramisuOptionOwner(CommonTiramisuOption): """manager option's owner""" def __init__(self, name: str, subconfig: Union[KernelConfig, SubConfig], option_bag: OptionBag) -> None: super().__init__(name, subconfig, option_bag) if option_bag is not None: # for help() self.values = self.option_bag.config_bag.context.cfgimpl_get_values() def get(self): """get owner for a specified option""" option = self.option_bag.option return self.values.getowner(self.option_bag) def isdefault(self): """is option has defaut value""" option = self.option_bag.option return self.values.is_default_owner(self.option_bag) def set(self, owner): """get owner for a specified option""" option = self.option_bag.option try: obj_owner = getattr(owners, owner) except AttributeError: owners.addowner(owner) obj_owner = getattr(owners, owner) self.values.setowner(obj_owner, self.option_bag) class TiramisuOptionProperty(CommonTiramisuOption): """manager option's property""" allow_optiondescription = True slave_need_index = False def __init__(self, name: str, subconfig: Union[KernelConfig, SubConfig], option_bag: OptionBag) -> None: super().__init__(name, subconfig, option_bag) if option_bag and option_bag.config_bag: self.settings = option_bag.config_bag.context.cfgimpl_get_settings() def get(self, apply_requires=True): """get properties for an option""" option = self.option_bag.option if apply_requires: self._test_slave_index() properties = self.option_bag.properties else: properties = self.settings.getproperties(self.option_bag, apply_requires=False) return set(properties) def add(self, prop): """add new property for an option""" option = self.option_bag.option if prop in FORBIDDEN_SET_PROPERTIES: raise ConfigError(_('cannot add this property: "{0}"').format( ' '.join(prop))) props = self.settings.getproperties(self.option_bag, apply_requires=False) self.settings.setproperties(self.option_bag.path, props | {prop}, self.option_bag) def pop(self, prop): """remove new property for an option""" option = self.option_bag.option props = self.settings.getproperties(self.option_bag, apply_requires=False) self.settings.setproperties(self.option_bag.path, props - {prop}, self.option_bag) def reset(self): """reset all personalised properties""" option = self.option_bag.option self.settings.reset(self.option_bag) class TiramisuOptionPermissive(CommonTiramisuOption): """manager option's property""" allow_optiondescription = True slave_need_index = False # FIXME should have same api than property def __init__(self, name: str, subconfig: Union[KernelConfig, SubConfig], option_bag: OptionBag) -> None: super().__init__(name, subconfig, option_bag) if option_bag and option_bag.config_bag: self.settings = option_bag.config_bag.context.cfgimpl_get_settings() def get(self): """get permissives value""" return self.settings.getpermissives(self.option_bag.option, self.option_bag.path) def set(self, permissives): """set permissives value""" option = self.option_bag.option self.settings.setpermissives(self.option_bag, permissives=permissives) def reset(self): """reset all personalised permissive""" self.set(frozenset()) class TiramisuOptionInformation(CommonTiramisuOption): """manage option informations""" allow_optiondescription = True slave_need_index = False def get(self, name, default=undefined): """get information for a key name""" option = self.option_bag.option return option.impl_get_information(name, default) def set(self, name, value): """set information for a key name""" #FIXME ? self.config_bag.context.impl_set_information(name, value) def reset(self, name): """remove information for a key name""" #FIXME ? self.config_bag.context.impl_del_information(name) class TiramisuOptionValue(CommonTiramisuOption): """manager option's value""" slave_need_index = False def get(self): """get option's value""" option = self.option_bag.option self._test_slave_index() return self.subconfig.getattr(self._name, self.option_bag) def set(self, value): """set a value for a specified option""" option = self.option_bag.option self._test_slave_index() values = self.option_bag.config_bag.context.cfgimpl_get_values() if isinstance(value, list): while undefined in value: idx = value.index(undefined) option_bag = OptionBag() option_bag.set_option(self.option_bag.option, self.option_bag.path, idx, self.option_bag.config_bag) value[idx] = values.getdefaultvalue(option_bag) else: if value == undefined: value = values.getdefaultvalue(self.option_bag) self.subconfig.setattr(value, self.option_bag) def _pop(self, index): """pop value for a master option (only for master option)""" if self.option_bag.option.impl_is_symlinkoption(): raise TypeError(_("can't delete a SymLinkOption")) self.option_bag.config_bag.context.cfgimpl_get_values().reset_master(index, self.option_bag, self.subconfig) def reset(self): """reset value for a value""" self._test_slave_index() self.subconfig.delattr(self.option_bag) def _len_master(self): """length of master option (only for slave option)""" option = self.option_bag.option # for example if index is None if '_length' not in vars(self): self._length = self.subconfig.cfgimpl_get_length() return self._length def _len_slave(self): """length of slave option (only for slave option)""" option = self.option_bag.option # for example if index is None if '_length' not in vars(self): self._length = self.subconfig.cfgimpl_get_length_slave(self.option_bag) return self._length def __getattr__(self, name: str) -> Callable: option = self.option_bag.option if name == 'list' and isinstance(option, ChoiceOption): return self._list elif name == 'pop' and option.impl_is_master_slaves('master'): return self._pop elif name == 'len': if option.impl_is_master_slaves('slave'): return self._len_slave if option.impl_is_master_slaves('master'): return self._len_master raise APIError(_('{} is unknown').format(name)) def _list(self): """all values available for an option (only for choiceoption)""" option = self.option_bag.option return option.impl_get_values(self.option_bag) def registers(registers: Dict[str, type], prefix: str) -> None: for module_name in globals().keys(): if module_name != prefix and module_name.startswith(prefix): module = globals()[module_name] func_name = module_name[len(prefix):].lower() registers[func_name] = module class TiramisuOption(CommonTiramisu): registers = {} def __init__(self, name: Optional[str], path: Optional[str]=None, index: Optional[int]=None, subconfig: Union[None, KernelConfig, SubConfig]=None, config_bag: Optional[ConfigBag]=None, option_bag: Optional[OptionBag]=None) -> None: self._name = name self.subconfig = subconfig self._path = path self.index = index self.config_bag = config_bag if option_bag: self.option_bag = option_bag else: self.option_bag = OptionBag() self.option_bag.path = self._path self.option_bag.index = self.index self.option_bag.config_bag = self.config_bag if not self.registers: registers(self.registers, self.__class__.__name__) def __getattr__(self, subfunc: str) -> Any: if subfunc in self.registers: return self.registers[subfunc](self._name, self.subconfig, self.option_bag) elif self._get_option().impl_is_optiondescription() and not subfunc.startswith('_'): return getattr(self, '_' + subfunc) raise APIError(_('please specify a valid sub function ({})').format(subfunc)) def dict(self, flatten=False, withvalue=undefined, withoption=None, fullpath=False): """return dict with path as key and value (only for optiondescription)""" self._get_option() if not self.config_bag.properties: config_bag = self.config_bag else: config_bag = self.config_bag.copy() config_bag.properties = self.config_bag.properties - {'warnings'} return config_bag.context.get_subconfig(self._path, self.option_bag).make_dict(config_bag=config_bag, flatten=flatten, fullpath=fullpath, withoption=withoption, withvalue=withvalue) def _find(self, name: str, value=undefined, type=None, first: bool=False): """find an option by name (only for optiondescription)""" if not first: ret = [] for path in self.config_bag.context.find(byname=name, byvalue=value, bytype=type, _subpath=self._path, config_bag=self.config_bag): subconfig, name = self.config_bag.context.cfgimpl_get_home_by_path(path, self.config_bag) t_option = TiramisuOption(name, path, None, # index for a slave ? subconfig, self.config_bag) if first: return t_option ret.append(t_option) return ret def _get(self, name): self._get_option() current_option = self.option_bag.option.impl_getchild(name, self.config_bag, self.subconfig) path = self.option_bag.path + '.' + name option_bag= OptionBag() option_bag.set_option(current_option, path, None, self.config_bag) if current_option.impl_is_optiondescription(): subconfig = self.subconfig.getattr(name, option_bag) else: subconfig = self.subconfig return TiramisuOption(name, path, None, subconfig, self.config_bag, option_bag) def _group_type(self): """get type for an optiondescription (only for optiondescription)""" return self._get_option().impl_get_group_type() def _list(self, type='all', group_type=None): """list options in an optiondescription (only for optiondescription)""" if type not in ('all', 'optiondescription'): raise APIError(_('unknown list type {}').format(type)) if group_type is not None and not isinstance(group_type, groups.GroupType): raise TypeError(_("unknown group_type: {0}").format(group_type)) def _filter(opt): if self.config_bag.properties: name = opt.impl_getname() path = subconfig._get_subpath(name) option_bag = OptionBag() option_bag.set_option(opt, path, None, self.config_bag) if opt.impl_is_optiondescription(): self.subconfig.get_subconfig(name, option_bag) else: self.subconfig.getattr(name, option_bag) option = self._get_option() name = option.impl_getname() path = self.subconfig._get_subpath(name) option_bag = OptionBag() option_bag.set_option(option, path, None, self.config_bag) subconfig = self.subconfig.get_subconfig(name, option_bag) for opt in option.impl_getchildren(self.config_bag): try: subsubconfig = _filter(opt) except PropertiesOptionError: continue if opt.impl_is_optiondescription(): if type == 'optiondescription' and \ (group_type and opt.impl_get_group_type() != group_type): continue else: if type == 'optiondescription': continue name = opt.impl_getname() yield TiramisuOption(name, subconfig._get_subpath(name), None, subconfig, self.config_bag) class TiramisuContext(TiramisuHelp): def __init__(self, config_bag: Optional[ConfigBag]) -> None: self.config_bag = config_bag class TiramisuContextInformation(TiramisuContext): """manage configuration informations""" def get(self, name, default=undefined): """get information for a key name""" return self.config_bag.context.impl_get_information(name, default) def set(self, name, value): """set information for a key name""" self.config_bag.context.impl_set_information(name, value) def reset(self, name): """remove information for a key name""" self.config_bag.context.impl_del_information(name) class TiramisuContextValue(TiramisuContext): """manager value""" def mandatory_warnings(self): """return path of options with mandatory property without any value""" return self.config_bag.context.cfgimpl_get_values().mandatory_warnings(self.config_bag) def set(self, path: str, value, index=None, only_config=undefined, force_default=undefined, force_default_if_same=undefined, force_dont_change_value=undefined): """set values for a GroupConfig or a MetaConfig""" kwargs = {} if only_config is not undefined: kwargs['only_config'] = only_config if force_default is not undefined: kwargs['force_default'] = force_default if force_default_if_same is not undefined: kwargs['force_default_if_same'] = force_default_if_same if force_dont_change_value is not undefined: kwargs['force_dont_change_value'] = force_dont_change_value return self.config_bag.context.set_value(path, index, value, self.config_bag, **kwargs) def reset(self, path): """reset value for a GroupConfig or a MetaConfig""" self.config_bag.context.reset(path, self.config_bag) def exportation(self): """export all values""" return self.config_bag.context.cfgimpl_get_values()._p_.exportation() def importation(self, values): """import values""" self.config_bag.context.cfgimpl_get_values()._p_.importation(values) self.config_bag.context.cfgimpl_reset_cache(None, None) class TiramisuContextOwner(TiramisuContext): """manager value""" def get(self): """get default owner""" return self.config_bag.context.cfgimpl_get_settings().getowner() def set(self, owner): """set default owner""" try: obj_owner = getattr(owners, owner) except AttributeError: owners.addowner(owner) obj_owner = getattr(owners, owner) self.config_bag.context.cfgimpl_get_settings().setowner(obj_owner) class TiramisuContextProperty(TiramisuContext): """manage configuration properties""" def read_only(self): """set configuration to read only mode""" settings = self.config_bag.context.cfgimpl_get_settings() settings.read_only() try: del self.config_bag.properties except AttributeError: pass def read_write(self): """set configuration to read and write mode""" settings = self.config_bag.context.cfgimpl_get_settings() settings.read_write() # #FIXME ? permissives = frozenset(settings.get_context_permissives() | frozenset(['hidden'])) settings.set_context_permissives(permissives) try: del self.config_bag.properties except AttributeError: pass #/FIXME ? def add(self, prop): """add a configuration property""" props = self.get() props.add(prop) self.set(frozenset(props)) def pop(self, prop): """remove a configuration property""" props = self.get() if prop in props: props.remove(prop) self.set(frozenset(props)) def get(self): """get all configuration properties""" return set(self.config_bag.properties) def set(self, props): """personalise configuration properties""" self.config_bag.context.cfgimpl_get_settings().set_context_properties(props) def reset(self): """remove configuration properties""" self.config_bag.context.cfgimpl_get_settings().reset(None) def exportation(self): """export configuration properties""" return self.config_bag.context.cfgimpl_get_settings()._p_.exportation() def importation(self, properties): """import configuration properties""" self.config_bag.context.cfgimpl_get_settings()._p_.importation(properties) self.config_bag.context.cfgimpl_reset_cache(None, None) class TiramisuContextPermissive(TiramisuContext): """manage configuration permissives""" def get(self): """get configuration permissives""" return self.config_bag.context.cfgimpl_get_settings().get_context_permissives() def set(self, permissives): """set configuration permissives""" self.config_bag.context.cfgimpl_get_settings().set_context_permissives(permissives) def exportation(self): """export configuration permissives""" return self.config_bag.context.cfgimpl_get_settings()._pp_.exportation() def importation(self, permissives): """import configuration permissives""" self.config_bag.context.cfgimpl_get_settings()._pp_.importation(permissives) self.config_bag.context.cfgimpl_reset_cache(None, None) class TiramisuContextOption(TiramisuContext): """manage option""" def find(self, name, value=undefined, type=None, first=False): """find an option by name""" if not first: ret = [] for path in self.config_bag.context.find(byname=name, byvalue=value, bytype=type, #_subpath=self._path, config_bag=self.config_bag): subconfig, name = self.config_bag.context.cfgimpl_get_home_by_path(path, self.config_bag) t_option = TiramisuOption(name, path, None, # index for a slave ? subconfig, self.config_bag) if first: return t_option ret.append(t_option) return ret def get(self, name): option = self.config_bag.context.cfgimpl_get_description().impl_getchild(name, self.config_bag, self.config_bag.context) return TiramisuOption(name, name, None, self.config_bag.context, self.config_bag) def list(self, type='all', group_type=None, recursive=False): """list content of an optiondescription""" def _filter(opt): if self.config_bag.properties: name = opt.impl_getname() option_bag = OptionBag() option_bag.set_option(opt, name, None, self.config_bag) self.config_bag.context.getattr(name, option_bag) if type not in ('all', 'optiondescription'): raise APIError(_('unknown list type {}').format(type)) if group_type is not None and not isinstance(group_type, groups.GroupType): raise TypeError(_("unknown group_type: {0}").format(group_type)) if recursive: if group_type: raise APIError(_('recursive with group_type is not implemented yet')) if self.config_bag.properties: raise APIError(_('not implemented yet')) for option in self.config_bag.context.cfgimpl_get_description()._cache_paths[1]: if type == 'optiondescription' and not isinstance(option, OptionDescription): continue yield option else: option = self.config_bag.context.cfgimpl_get_description() for opt in option.impl_getchildren(self.config_bag): try: subsubconfig = _filter(opt) except PropertiesOptionError: continue if opt.impl_is_optiondescription(): if type == 'optiondescription' and \ (group_type and opt.impl_get_group_type() != group_type): continue else: if type == 'optiondescription': continue name = opt.impl_getname() yield TiramisuOption(name, self.config_bag.context._get_subpath(name), None, self.config_bag.context, self.config_bag) class TiramisuContextConfig(TiramisuContext): """configuration methods""" def find(self, name: str, value=undefined, first: bool=False): """find a path from option name and optionnaly a value to MetaConfig or GroupConfig""" if first: return TiramisuAPI(self.config_bag.context.find_firsts(byname=name, byvalue=value, config_bag=self.config_bag)) else: raise APIError('not implemented yet') def name(self): return self.config_bag.context.impl_getname() def duplicate(self, session_id=None): return TiramisuAPI(self.config_bag.context.duplicate(session_id)) def _m_new(self, name): return TiramisuAPI(self.config_bag.context.new_config(name)) def _m_list(self): return self._g_list() def _g_list(self): for child in self.config_bag.context.cfgimpl_get_children(): yield TiramisuAPI(child) def __getattr__(self, name: str) -> Callable: if not name.startswith('_'): try: if isinstance(self.config_bag.context, KernelMetaConfig): return getattr(self, '_m_' + name) elif isinstance(self.config_bag.context, KernelGroupConfig): return getattr(self, '_g_' + name) except APIError: raise APIError(_('{} is unknown').format(name)) raise APIError(_('{} is unknown').format(name)) def dict(self, flatten=False, withvalue=undefined, withoption=None, fullpath=False): """return dict with path as key and value""" if not self.config_bag.properties: config_bag = self.config_bag else: config_bag = self.config_bag.copy() config_bag.properties = self.config_bag.properties - {'warnings'} return config_bag.context.make_dict(config_bag, flatten=flatten, fullpath=fullpath, withoption=withoption, withvalue=withvalue) class TiramisuDispatcher: pass class TiramisuAPI(TiramisuHelp): registers = {} def __init__(self, config) -> None: self._config = config if not self.registers: registers(self.registers, 'TiramisuContext') registers(self.registers, 'TiramisuDispatcher') def __getattr__(self, subfunc: str) -> Any: if subfunc == 'forcepermissive': if isinstance(self._config, ConfigBag): config = self._config.config force = not self._config.properties else: config = self._config force = None config_bag = ConfigBag(context=config) config_bag.set_permissive() if force is True: config_bag.properties = frozenset() return TiramisuAPI(config_bag) elif subfunc == 'unrestraint': #if isinstance(self._config, ConfigBag): # config = self._config.context # force = self._config.force_permissive #else: config = self._config #force = None config_bag = ConfigBag(context=config) config_bag.properties = frozenset() #if force is not None: #config_bag.force_permissive = force return TiramisuAPI(config_bag) elif subfunc in self.registers: if not isinstance(self._config, ConfigBag): config_bag = ConfigBag(context=self._config) else: config_bag = self._config return self.registers[subfunc](config_bag) else: raise APIError(_('please specify a valid sub function ({})').format(subfunc)) class TiramisuDispatcherConfig(TiramisuDispatcher, TiramisuContextConfig): def __call__(self, path: Optional[str]): """select a child Tiramisu configuration (only with MetaConfig or GroupConfig)""" if path is None: return TiramisuAPI(self.config_bag) spaths = path.split('.') config = self.config_bag.context for spath in spaths: config = config.getconfig(spath) config_bag = self.config_bag.copy() config_bag.context = config return TiramisuAPI(config_bag) class TiramisuDispatcherOption(TiramisuDispatcher, TiramisuContextOption): subhelp = TiramisuOption def __call__(self, path: str, index: Optional[int]=None) -> TiramisuOption: """select a option (index only for slave option)""" subconfig, name = self.config_bag.context.cfgimpl_get_home_by_path(path, self.config_bag) return TiramisuOption(name, path, index, subconfig, self.config_bag) class Config(TiramisuAPI): def __init__(self, descr: OptionDescription, session_id: str=None, persistent: bool=False, storage=None) -> None: if not isinstance(descr, KernelConfig): config = KernelConfig(descr, session_id=session_id, persistent=persistent, storage=storage) else: config = descr super().__init__(config) class MetaConfig(TiramisuAPI): def __init__(self, children, session_id: Union[str, None]=None, persistent: bool=False, optiondescription: Union[OptionDescription, None]=None) -> None: _children = [] for child in children: if isinstance(child, TiramisuAPI): _children.append(child._config) else: _children.append(child) config = KernelMetaConfig(_children, session_id=session_id, persistent=persistent, optiondescription=optiondescription) super().__init__(config) class GroupConfig(TiramisuAPI): def __init__(self, children, session_id: Union[str, None]=None, persistent: bool=False, storage=None) -> None: _children = [] for child in children: _children.append(child._config) config = KernelGroupConfig(_children, session_id=session_id, persistent=persistent, storage=storage) super().__init__(config) def getapi(config: Config): """instanciate Config :param config: KernelConfig object :type descr: an instance of ``config.KernelConfig`` """ return config