# -*- coding: utf-8 -*- # Copyright (C) 2017 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 from .error import APIError, PropertiesOptionError, ConfigError from .i18n import _ from .setting import ConfigBag, owners, undefined from .option import ChoiceOption from time import time from copy import deepcopy TIRAMISU_VERSION = 3 try: from .value import Multi except: Multi = list COUNT_TIME = False COUNT_TIME = {} def count(func): global MOD_COUNT_TIME class_name = func.__str__().split()[1].split('.')[0] func_name = func.__name__ def wrapper(*args, **kwargs): time1 = time() ret = func(*args, **kwargs) time2 = time() diff = (time2 - time1) * 1000.0 MOD_COUNT_TIME[class_name][func_name]['max'] = max(MOD_COUNT_TIME[class_name][func_name]['max'], diff) MOD_COUNT_TIME[class_name][func_name]['min'] = min(MOD_COUNT_TIME[class_name][func_name]['min'], diff) MOD_COUNT_TIME[class_name][func_name]['total'] += diff MOD_COUNT_TIME[class_name][func_name]['nb'] += 1 #print('%s function took %0.3f ms' % (func_name, diff)) #print(COUNT_TIME) return ret if COUNT_TIME is not False: COUNT_TIME.setdefault(class_name, {}) COUNT_TIME[class_name][func_name] = {'max': 0, 'min': 1000, 'nb': 0, 'total': 0} MOD_COUNT_TIME = deepcopy(COUNT_TIME) return wrapper return func def display_count(): if COUNT_TIME is not False: global MOD_COUNT_TIME #print(MOD_COUNT_TIME) print() for class_name in MOD_COUNT_TIME: print('>', class_name) for func in MOD_COUNT_TIME[class_name]: print('=>', func) print('==> nb:', MOD_COUNT_TIME[class_name][func]['nb']) if MOD_COUNT_TIME[class_name][func]['nb'] != 0: print('==> min:', MOD_COUNT_TIME[class_name][func]['min']) print('==> max:', MOD_COUNT_TIME[class_name][func]['max']) print('==> moy:', MOD_COUNT_TIME[class_name][func]['total'] / MOD_COUNT_TIME[class_name][func]['nb']) MOD_COUNT_TIME = deepcopy(COUNT_TIME) class CommonTiramisuOption(object): icon = '\u2937' tmpl_help = u' {} {}: {}' allow_unrestraint = False allow_optiondescription = False slave_need_index = True def __init__(self, name, path, index, subconfig, config_bag): self.path = path self.index = index self.config_bag = config_bag self.name = name self.subconfig = subconfig if self.slave_need_index: self._test_slave_index() if not self.allow_unrestraint: self._unrestraint_not_allowed(self.config_bag.force_unrestraint) def _test_slave_index(self): option = self.get_option() if not option.impl_is_optiondescription() and self.index is None and \ option.impl_is_master_slaves('slave'): raise APIError('index must be set with a slave option') def _unrestraint_not_allowed(self, force_unrestraint): if force_unrestraint: name = self.__class__.__name__[14:].lower() raise APIError(_('{} cannot be unrestraint').format(name)) def __getattr__(self, name): if name == 'help': return self._help() else: if not hasattr(CommonTiramisuOption, name): raise APIError(_('unknown method {}').format(name)) else: super(CommonTiramisuOption, self).__getattribute__(name) def get_option(self): option = self.config_bag.option if option is None: option = self.subconfig.cfgimpl_get_description().impl_getchild(self.name, self.config_bag, self.subconfig) self.config_bag.option = option if not self.allow_optiondescription and option.impl_is_optiondescription(): raise APIError(_('option must not be an optiondescription')) return option def _help(self): txt = [] for func_name in dir(self): if not func_name.startswith('_'): func = getattr(self, func_name) if ismethod(func): txt.append(self.tmpl_help.format(self.icon, func_name, getdoc(func))) return '\n'.join(txt) class TiramisuOptionOption(CommonTiramisuOption): """get information from an option""" allow_unrestraint = True allow_optiondescription = True slave_need_index = False @count def ismulti(self): """test if option could have multi value""" option = self.get_option() return option.impl_is_multi() @count def issubmulti(self): """test if option could have submulti value""" option = self.get_option() return option.impl_is_submulti() @count def ismasterslaves(self): """test if option is a master or a slave""" option = self.get_option() return option.impl_is_master_slaves() @count def ismaster(self): """test if option is a master""" option = self.get_option() return option.impl_is_master_slaves('master') @count def isslave(self): """test if option is a slave""" option = self.get_option() return option.impl_is_master_slaves('slave') @count def getname(self): option = self.get_option() return option.impl_getname() @count def getdoc(self): option = self.get_option() return option.impl_get_display_name() @count def default(self): option = self.get_option() return option.impl_getdefault() @count def defaultmulti(self): option = self.get_option() return option.impl_getdefault_multi() @count def has_dependency(self, self_is_dep=True): option = self.get_option() return option.impl_has_dependency(self_is_dep) class TiramisuOptionOwner(CommonTiramisuOption): """manager option's owner""" def __init__(self, name, path, index, subconfig, config_bag): super(TiramisuOptionOwner, self).__init__(name, path, index, subconfig, config_bag) self.values = self.config_bag.config.cfgimpl_get_values() @count def get(self): """get owner for a specified option""" self.get_option() return self.values.getowner(self.path, self.index, self.config_bag) @count def isdefault(self): """is option has defaut value""" self.get_option() return self.values.is_default_owner(self.path, self.index, self.config_bag) @count def set(self, owner): """get owner for a specified option""" self.get_option() if TIRAMISU_VERSION == 2: if owner in ['default', 'forced', 'meta']: raise ConfigError() try: obj_owner = getattr(owners, owner) except AttributeError: owners.addowner(owner) obj_owner = getattr(owners, owner) self.values.setowner(self.path, self.index, obj_owner, self.config_bag) class TiramisuOptionProperty(CommonTiramisuOption): """manager option's property""" #allow_unrestraint = True allow_optiondescription = True allow_unrestraint = True slave_need_index = False def __init__(self, name, path, index, subconfig, config_bag): super(TiramisuOptionProperty, self).__init__(name, path, index, subconfig, config_bag) self.settings = config_bag.config.cfgimpl_get_settings() @count def get(self): self.get_option() self._test_slave_index() properties = self.settings.getproperties(self.path, self.index, self.config_bag) if TIRAMISU_VERSION == 2: properties = properties.get() return set(properties) @count def add(self, prop): self.get_option() self.settings.addproperty(self.path, prop, self.config_bag) @count def pop(self, prop): self.get_option() self.settings.popproperty(self.path, prop, self.config_bag) @count def reset(self): """reset all personalised properties """ self.get_option() self.settings.reset(opt=self.config_bag.option, path=self.path) class TiramisuOptionPermissive(CommonTiramisuOption): """manager option's property""" allow_unrestraint = True allow_optiondescription = True slave_need_index = False def __init__(self, name, path, index, subconfig, config_bag): super(TiramisuOptionPermissive, self).__init__(name, path, index, subconfig, config_bag) self.settings = config_bag.config.cfgimpl_get_settings() @count def get(self): """get permissive value for a specified path""" if TIRAMISU_VERSION == 2: args = [self.setting_properties, self.path] else: args = [self.get_option(), self.path] return self.settings.getpermissive(*args) @count def set(self, permissives): if TIRAMISU_VERSION == 2: permissive = tuple(permissives) self.settings.setpermissive(opt=self.get_option(), path=self.path, permissives=permissives) @count def reset(self, path): """reset all personalised permissive """ self.set(tuple()) class TiramisuOptionInformation(CommonTiramisuOption): allow_optiondescription = True @count def get(self, name, default=undefined): option = self.get_option() return option.impl_get_information(name, default) class TiramisuOptionValue(CommonTiramisuOption): """manager option's value""" slave_need_index = False @count def get(self): self.get_option() self._test_slave_index() settings = self.config_bag.config.cfgimpl_get_settings() value = self.subconfig.getattr(self.name, self.index, self.config_bag) if isinstance(value, Multi): value = list(value) return value @count def set(self, value): """set a value for a specified option""" self.get_option() self._test_slave_index() values = self.config_bag.config.cfgimpl_get_values() if isinstance(value, list): while undefined in value: idx = value.index(undefined) value[idx] = values.getdefaultvalue(self.path, idx, self.config_bag) else: if value == undefined: value = values.getdefaultvalue(self.path, self.index, self.config_bag) self.subconfig.setattr(self.name, self.index, value, self.config_bag) @count def pop(self, index): """pop value for a specified master values """ self.get_option() self._test_slave_index() #FIXME only for master self.config_bag.config.delattr(self.path, index, self.config_bag) @count def reset(self): """reset value for a value""" self.get_option() self._test_slave_index() self.config_bag.config.delattr(self.path, self.index, self.config_bag) @count def len(self): #FIXME only for slave self.get_option() subconfig_path = self.path.rsplit('.', 1)[0] subconfig = self.config.getattr(subconfig_path, None, self.config_bag) return subconfig.cfgimpl_get_length() def __getattr__(self, name): if name == 'list': self.get_option() if isinstance(self.config_bag.option, ChoiceOption): return self._list raise APIError(_('{} allowed only for choiceoption').format(name)) raise APIError(_('{} is unknown').format(name)) @count def _list(self): return self.config_bag.option.impl_get_values(self.config_bag) def registers(registers, prefix): 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(object): icon = '\u2937' tmpl_help = ' {} {}: {}' def __init__(self, name, path, index, subconfig, config_bag): self.name = name self.subconfig = subconfig self.path = path self.index = index self.config_bag = config_bag self.registers = {} registers(self.registers, self.__class__.__name__) def _help(self): txt = [] for module_name, module in self.registers.items(): module_doc = getdoc(module) txt.append(self.tmpl_help.format(self.icon, module_name, module_doc)) txt.append(module(None, None).help) return '\n'.join(txt) def __getattr__(self, subfunc): if subfunc in self.registers: return self.registers[subfunc](self.name, self.path, self.index, self.subconfig, self.config_bag) elif subfunc == 'help': return self._help() else: raise APIError(_('please specify a valid sub function ({})').format(subfunc)) @count def make_dict(self, flatten=False, withvalue=undefined, withoption=None, fullpath=False): return self.config_bag.config.getattr(self.path, None, self.config_bag).make_dict(config_bag=self.config_bag, flatten=flatten, fullpath=fullpath, withoption=withoption, withvalue=withvalue) class TiramisuContext(object): def __init__(self, config_bag): self.config_bag = config_bag class TiramisuContextInformation(TiramisuContext): @count def get(self, name, default=undefined): return self.config_bag.config.impl_get_information(name, default) @count def set(self, name, value): self.config_bag.config.impl_set_information(name, value) @count def reset(self, name): self.config_bag.config.impl_del_information(name) class TiramisuContextValue(TiramisuContext): @count def mandatory_warnings(self): return self.config_bag.config.cfgimpl_get_values().mandatory_warnings(self.config_bag) @count def get_modified(self): return self.config_bag.config.cfgimpl_get_values().get_modified_values() class TiramisuContextOwner(TiramisuContext): @count def get(self): return self.config_bag.config.cfgimpl_get_settings().getowner() @count def set(self, owner): try: obj_owner = getattr(owners, owner) except AttributeError: owners.addowner(owner) obj_owner = getattr(owners, owner) self.config_bag.config.cfgimpl_get_settings().setowner(obj_owner) class TiramisuContextProperty(TiramisuContext): @count def read_only(self): settings = self.config_bag.config.cfgimpl_get_settings() settings.read_only() self.config_bag.setting_properties = settings.get_context_properties() @count def read_write(self): settings = self.config_bag.config.cfgimpl_get_settings() settings.read_write() # #FIXME ? settings.set_context_permissive(frozenset(['hidden'])) self.config_bag.setting_properties = settings.get_context_properties() #/FIXME ? @count def add(self, prop): props = self.get() props.add(prop) self.set(frozenset(props)) self.config_bag.setting_properties = self.config_bag.config.cfgimpl_get_settings().get_context_properties() @count def pop(self, prop): props = self.get() props.remove(prop) self.set(frozenset(props)) self.config_bag.setting_properties = self.config_bag.config.cfgimpl_get_settings().get_context_properties() @count def get(self): return set(self.config_bag.setting_properties) @count def get_modified(self): return self.config_bag.config.cfgimpl_get_settings().get_modified_properties() @count def set_modified(self, props): return self.config_bag.config.cfgimpl_get_settings().set_modified_properties(props) @count def set(self, props): self.config_bag.config.cfgimpl_get_settings().set_context_properties(props) self.config_bag.setting_properties = self.config_bag.config.cfgimpl_get_settings().get_context_properties() def reset(self): self.config_bag.config.cfgimpl_get_settings().reset() class TiramisuContextPermissive(TiramisuContext): @count def set(self, permissives): self.config_bag.config.cfgimpl_get_settings().set_context_permissive(permissives) class TiramisuContextOption(TiramisuContext): @count def find_first(self, name, type='option'): check_properties = self.config_bag.force_unrestraint or self.config_bag.force_unrestraint return self.config_bag.config.find_first(byname=name, type_=type, config_bag=self.config_bag) @count def find(self, name, type='option'): return self.config_bag.config.find(byname=name, type_=type, config_bag=self.config_bag) @count def get(self, path): return self.config_bag.config.unwrap_from_path(path, None, self.config_bag) @count def make_dict(self, flatten=False, withvalue=undefined, withoption=None, fullpath=False): return self.config_bag.config.make_dict(self.config_bag, flatten=flatten, fullpath=fullpath, withoption=withoption, withvalue=withvalue) class TiramisuDispatcherOption(TiramisuContextOption): def __call__(self, path, index=None): config_bag = self.config_bag.copy() validate = not config_bag.force_unrestraint config_bag.validate = validate config_bag.validate_properties = validate if not validate: config_bag.setting_properties = None subconfig, name = config_bag.config.cfgimpl_get_home_by_path(path, config_bag) #opt = config_bag.config.unwrap_from_path(path, # index, # config_bag) #config_bag.option = opt #if index is not None and not opt.impl_is_master_slaves('slave'): # raise APIError('index must be set only with a slave option') return TiramisuOption(name, path, index, subconfig, config_bag) class TiramisuAPI(object): icon = '\u2937' tmpl_help = ' {} {}: {}' def __init__(self, config, force_permissive=False, force_unrestraint=False): self._config = config self.force_permissive = force_permissive self.force_unrestraint = force_unrestraint self.registers = {} registers(self.registers, 'TiramisuContext') registers(self.registers, 'TiramisuDispatcher') def __getattr__(self, subfunc): if subfunc == 'forcepermissive': return TiramisuAPI(config=self._config, force_permissive=True, force_unrestraint=self.force_unrestraint) elif subfunc in ['unrestraint', 'config']: return TiramisuAPI(config=self._config, force_permissive=self.force_permissive, force_unrestraint=True) elif subfunc == 'help': return self._help() elif subfunc in self.registers: config_bag = ConfigBag(config=self._config, force_permissive=self.force_permissive, force_unrestraint=self.force_unrestraint) return self.registers[subfunc](config_bag) else: raise APIError(_('please specify a valid sub function ({})').format(subfunc)) def _help(self): txt = ['[forcepermissive]'] for module_name, module in self.registers.items(): module_doc = getdoc(module) txt.append(self.tmpl_help.format(self.icon, module_name, module_doc)) txt.append(module(None, None).help) return '\n'.join(txt) @count def getapi(config): """instanciate TiramisuAPI :param config: Config object :type descr: an instance of ``config.Config`` """ return TiramisuAPI(config)