ConfigBag optimisation

This commit is contained in:
2018-08-01 08:37:58 +02:00
parent acdddcfe9c
commit 41c17004d2
19 changed files with 1163 additions and 1160 deletions

View File

@ -117,77 +117,99 @@ debug = False
static_set = frozenset()
class ConfigBag(object):
__slots__ = ('default',
'config',
'option',
'ori_option',
'properties',
'setting_properties',
'force_permissive',
'force_unrestraint',
'trusted_cached_properties',
'fromconsistency',
'_validator'
class OptionBag:
__slots__ = ('option', # current option
'path',
'index',
'config_bag',
'ori_option', # original option (for example useful for symlinkoption)
'properties', # properties of current option
'apply_requires',
'fromconsistency' # history for consistency
)
def __init__(self):
self.option = None
self.fromconsistency = []
def set_option(self,
option,
path,
index,
config_bag):
if self.option != None:
raise Exception('hu?')
if path is None:
path = config_bag.config.cfgimpl_get_description().impl_get_path_by_opt(option)
self.path = path
self.index = index
self.option = option
self.config_bag = config_bag
def __getattr__(self, key):
if key == 'properties':
settings = self.config_bag.config.cfgimpl_get_settings()
self.properties = settings.getproperties(self, apply_requires=self.apply_requires)
return self.properties
elif key == 'ori_option':
return self.option
elif key == 'apply_requires':
return True
raise KeyError('unknown key {} for OptionBag'.format(key))
class ConfigBag:
__slots__ = ('config', # link to the current config (context)
'_setting_properties', # properties for current config
'force_permissive', # force permissive
'force_unrestraint', # do not validate properties
'_validate', # validate
)
def __init__(self, config, **kwargs):
self.default = {'force_permissive': False,
'force_unrestraint': False,
'trusted_cached_properties': True,
}
self.force_permissive = False
self.force_unrestraint = False
self.config = config
self._validator = True
self.fromconsistency = []
self._validate = True
for key, value in kwargs.items():
if value != self.default.get(key):
setattr(self, key, value)
setattr(self, key, value)
def __getattr__(self, key):
if key == 'validate_properties':
return not self.force_unrestraint
if key == 'validate':
if self.setting_properties is not None:
return 'validator' in self.setting_properties
return self._validator
if self._validate and self._setting_properties is not None:
return 'validator' in self._setting_properties
return self._validate
if key == 'setting_properties':
if self.force_unrestraint:
return None
self.setting_properties = self.config.cfgimpl_get_settings().get_context_properties()
return self.setting_properties
return self._setting_properties
if key == '_setting_properties':
self._setting_properties = self.config.cfgimpl_get_settings().get_context_properties()
if self._validate is False:
self._setting_properties = self._setting_properties - {'validator'}
return self._setting_properties
if key not in self.__slots__:
raise KeyError('unknown key {}'.format(key))
return self.default.get(key)
raise KeyError('unknown key {} for ConfigBag'.format(key))
return None
def __setattr__(self, key, value):
if key == 'validate':
if self.setting_properties is not None:
if value is False:
self.setting_properties = frozenset(set(self.setting_properties) - {'validator'})
else:
self.setting_properties = frozenset(set(self.setting_properties) | {'validator'})
else:
self._validator = value
try:
del self._setting_properties
except AttributeError:
pass
self._validate = value
else:
super().__setattr__(key, value)
def delete(self, key):
try:
return self.__delattr__(key)
except AttributeError:
pass
def copy(self, filters='all'):
def copy(self):
kwargs = {}
for key in self.__slots__:
if filters == 'nooption' and (key.startswith('option') or \
key == 'properties'):
continue
if key == 'fromconsistency':
if key == 'fromconsistency' and self.fromconsistency != []:
kwargs['fromconsistency'] = copy(self.fromconsistency)
elif key != 'default':
value = getattr(self, key)
if value != self.default.get(key):
kwargs[key] = value
else:
kwargs[key] = getattr(self, key)
return ConfigBag(**kwargs)
@ -357,13 +379,14 @@ class Settings(object):
return props
def getproperties(self,
path,
index,
config_bag,
option_bag,
apply_requires=True):
"""
"""
opt = config_bag.option
opt = option_bag.option
config_bag = option_bag.config_bag
path = option_bag.path
index = option_bag.index
if opt.impl_is_symlinkoption():
opt = opt.impl_getopt()
path = opt.impl_getpath(self._getcontext())
@ -384,17 +407,11 @@ class Settings(object):
props = self._p_.getproperties(path,
opt.impl_getproperties())
else:
props = meta.cfgimpl_get_settings().getproperties(path,
index,
config_bag,
props = meta.cfgimpl_get_settings().getproperties(option_bag,
apply_requires)
if apply_requires:
props |= self.apply_requires(path,
opt.impl_getrequires(),
index,
False,
config_bag,
opt.impl_get_display_name())
props |= self.apply_requires(option_bag,
False)
props -= self.getpermissive(opt,
path)
if apply_requires:
@ -421,12 +438,8 @@ class Settings(object):
return self._pp_.getpermissive(path)
def apply_requires(self,
path,
current_requires,
index,
readable,
config_bag,
name):
option_bag,
readable):
"""carries out the jit (just in time) requirements between options
a requirement is a tuple of this form that comes from the option's
@ -470,7 +483,7 @@ class Settings(object):
:param path: the option's path in the config
:type path: str
"""
#current_requires = opt.impl_getrequires()
current_requires = option_bag.option.impl_getrequires()
# filters the callbacks
if readable:
@ -489,32 +502,38 @@ class Settings(object):
breaked = False
for option, expected in exps:
reqpath = option.impl_getpath(context)
#FIXME c'est un peut tard !
if reqpath.startswith(path + '.'):
#FIXME c'est un peu tard !
if reqpath.startswith(option_bag.path + '.'):
raise RequirementError(_("malformed requirements "
"imbrication detected for option:"
" '{0}' with requirement on: "
"'{1}'").format(path, reqpath))
"'{1}'").format(option_bag.path, reqpath))
idx = None
is_indexed = False
if option.impl_is_master_slaves('slave'):
idx = index
idx = option_bag.index
elif option.impl_is_multi():
is_indexed = True
sconfig_bag = config_bag.copy('nooption')
if config_bag.option == option:
sconfig_bag.setting_properties = None
sconfig_bag.force_unrestraint= False
sconfig_bag.validate = False
else:
sconfig_bag.force_permissive = True
sconfig_bag.option = option
config_bag = ConfigBag(config=option_bag.config_bag.config,
_setting_properties=option_bag.config_bag._setting_properties,
force_permissive=True,
force_unrestraint=option_bag.config_bag.force_unrestraint,
_validate=option_bag.config_bag._validate)
soption_bag = OptionBag()
soption_bag.set_option(option,
reqpath,
idx,
config_bag)
soption_bag.config_bag.force_permissive = True
if option_bag.option == option:
soption_bag.config_bag.force_unrestraint = True
soption_bag.config_bag.validate = False
soption_bag.apply_requires = False
try:
value = context.getattr(reqpath,
idx,
sconfig_bag)
soption_bag)
if is_indexed:
value = value[index]
value = value[option_bag.index]
except PropertiesOptionError as err:
properties = err.proptype
if not transitive:
@ -532,19 +551,15 @@ class Settings(object):
prop_msg = _('properties')
raise RequirementError(_('cannot access to option "{0}" because '
'required option "{1}" has {2} {3}'
'').format(name,
'').format(option_bag.option.impl_get_display_name(),
option.impl_get_display_name(),
prop_msg,
display_list(list(properties), add_quote=True)))
# transitive action, add action
if operator != 'and':
if readable:
for msg in self.apply_requires(err._path,
err._requires,
err._index,
True,
err._config_bag,
err._name).values():
for msg in self.apply_requires(err._option_bag,
True).values():
calc_properties.setdefault(action, []).extend(msg)
else:
calc_properties.add(action)
@ -587,23 +602,25 @@ class Settings(object):
def setproperties(self,
path,
properties,
config_bag):
option_bag):
"""save properties for specified path
(never save properties if same has option properties)
"""
# should have index !!!
if self._getcontext().cfgimpl_get_meta() is not None:
raise ConfigError(_('cannot change property with metaconfig'))
if path is not None and config_bag.option.impl_getrequires() is not None:
not_allowed_props = properties & getattr(config_bag.option, '_calc_properties', static_set)
if path is not None and option_bag.option.impl_getrequires() is not None:
not_allowed_props = properties & \
getattr(option_bag.option, '_calc_properties', static_set)
if not_allowed_props:
raise ValueError(_('cannot set property {} for option "{}" this property is calculated'
'').format(display_list(list(not_allowed_props), add_quote=True),
config_bag.option.impl_get_display_name()))
if config_bag is None:
raise ValueError(_('cannot set property {} for option "{}" this property is '
'calculated').format(display_list(list(not_allowed_props),
add_quote=True),
option_bag.option.impl_get_display_name()))
if option_bag is None:
opt = None
else:
opt = config_bag.option
opt = option_bag.option
if opt and opt.impl_is_symlinkoption():
raise TypeError(_("can't assign property to the symlinkoption \"{}\""
"").format(opt.impl_get_display_name()))
@ -616,21 +633,20 @@ class Settings(object):
self._p_.setproperties(path,
properties)
#values too because of slave values could have a PropertiesOptionError has value
self._getcontext().cfgimpl_reset_cache(opt,
path,
config_bag)
self._getcontext().cfgimpl_reset_cache(option_bag)
if option_bag is not None:
try:
del option_bag.properties
except AttributeError:
pass
def set_context_permissive(self,
permissive):
self.setpermissive(None,
None,
None,
permissive)
def setpermissive(self,
opt,
path,
config_bag,
option_bag,
permissives):
"""
enables us to put the permissives in the storage
@ -645,51 +661,55 @@ class Settings(object):
raise ConfigError(_('cannot change permissive with metaconfig'))
if not isinstance(permissives, frozenset):
raise TypeError(_('permissive must be a frozenset'))
if opt and opt.impl_is_symlinkoption():
raise TypeError(_("can't assign permissive to the symlinkoption \"{}\""
"").format(opt.impl_get_display_name()))
if option_bag is not None:
opt = option_bag.option
if opt and opt.impl_is_symlinkoption():
raise TypeError(_("can't assign permissive to the symlinkoption \"{}\""
"").format(opt.impl_get_display_name()))
path = option_bag.path
else:
path = None
forbidden_permissives = FORBIDDEN_SET_PERMISSIVES & permissives
if forbidden_permissives:
raise ConfigError(_('cannot add those permissives: {0}').format(
' '.join(forbidden_permissives)))
self._pp_.setpermissive(path, permissives)
self._getcontext().cfgimpl_reset_cache(opt,
path,
config_bag)
if option_bag is not None:
self._getcontext().cfgimpl_reset_cache(option_bag)
#____________________________________________________________
# reset methods
def reset(self,
opt,
path,
config_bag,
all_properties=False):
option_bag):
# all_properties=False):
if self._getcontext().cfgimpl_get_meta() is not None:
raise ConfigError(_('cannot change property with metaconfig'))
if option_bag is None:
opt = None
else:
opt = option_bag.option
if opt and opt.impl_is_symlinkoption():
raise TypeError(_("can't reset properties to the symlinkoption \"{}\""
"").format(opt.impl_get_display_name()))
if all_properties and (path or opt):
raise ValueError(_('opt and all_properties must not be set '
'together in reset'))
if all_properties:
self._p_.reset_all_properties()
#if all_properties and (path or opt):
# raise ValueError(_('opt and all_properties must not be set '
# 'together in reset'))
#if all_properties:
# self._p_.reset_all_properties()
else:
if opt is not None and path is None:
path = opt.impl_getpath(self._getcontext())
if opt is not None:
path = option_bag.path
else:
path = None
self._p_.delproperties(path)
self._getcontext().cfgimpl_reset_cache(opt,
path,
config_bag)
self._getcontext().cfgimpl_reset_cache(option_bag)
#____________________________________________________________
# validate properties
def validate_properties(self,
path,
index,
config_bag):
option_bag):
"""
validation upon the properties related to `opt`
@ -697,70 +717,56 @@ class Settings(object):
:param force_permissive: behaves as if the permissive property
was present
"""
opt = config_bag.option
# calc properties
self_properties = config_bag.properties
if self_properties is None:
self_properties = self.getproperties(path,
index,
config_bag)
config_bag.properties = self_properties
self_properties = option_bag.properties
config_bag = option_bag.config_bag
properties = self_properties & config_bag.setting_properties - {'frozen', 'mandatory', 'empty'}
# remove permissive properties
if (config_bag.force_permissive is True or 'permissive' in config_bag.setting_properties) and properties:
if (config_bag.force_permissive is True or \
'permissive' in config_bag.setting_properties) and properties:
# remove global permissive if need
properties -= self.get_context_permissive()
# at this point an option should not remain in properties
if properties != frozenset():
raise PropertiesOptionError(path,
index,
config_bag,
raise PropertiesOptionError(option_bag,
properties,
self)
def validate_mandatory(self,
path,
index,
value,
config_bag):
option_bag):
values = self._getcontext().cfgimpl_get_values()
opt = config_bag.option
opt = option_bag.option
config_bag = option_bag.config_bag
is_mandatory = False
if config_bag.setting_properties and 'mandatory' in config_bag.setting_properties:
if (config_bag.force_permissive is True or 'permissive' in config_bag.setting_properties) and \
'mandatory' in self.get_context_permissive():
pass
elif 'mandatory' in config_bag.properties and values.isempty(opt,
elif 'mandatory' in option_bag.properties and values.isempty(opt,
value,
index=index):
index=option_bag.index):
is_mandatory = True
if 'empty' in config_bag.properties and values.isempty(opt,
if 'empty' in option_bag.properties and values.isempty(opt,
value,
force_allow_empty_list=True,
index=index):
index=option_bag.index):
is_mandatory = True
if is_mandatory:
raise PropertiesOptionError(path,
index,
config_bag,
raise PropertiesOptionError(option_bag,
['mandatory'],
self)
def validate_frozen(self,
path,
index,
config_bag):
if config_bag.setting_properties and \
('everything_frozen' in config_bag.setting_properties or
'frozen' in config_bag.properties) and \
not ((config_bag.force_permissive is True or
'permissive' in config_bag.setting_properties) and
option_bag):
if option_bag.config_bag.setting_properties and \
('everything_frozen' in option_bag.config_bag.setting_properties or
'frozen' in option_bag.properties) and \
not ((option_bag.config_bag.force_permissive is True or
'permissive' in option_bag.config_bag.setting_properties) and
'frozen' in self.get_context_permissive()):
raise PropertiesOptionError(path,
index,
config_bag,
raise PropertiesOptionError(option_bag,
['frozen'],
self)
return False