optimisations

This commit is contained in:
Emmanuel Garette 2018-09-04 08:36:02 +02:00
parent 122796bd19
commit ec169a8dc6
10 changed files with 118 additions and 40 deletions

View File

@ -83,6 +83,22 @@ def test_consistency_warnings_only_more_option():
assert len(w) == 1 assert len(w) == 1
def test_consistency_warnings_only_option():
a = IntOption('a', '')
b = IntOption('b', '', warnings_only=True)
od = OptionDescription('od', '', [a, b])
a.impl_add_consistency('not_equal', b)
api = Config(od)
api.option('a').value.set(1)
warnings.simplefilter("always", ValueWarning)
with warnings.catch_warnings(record=True) as w:
api.option('b').value.set(1)
assert w != []
api.option('a').value.reset()
api.option('b').value.set(1)
raises(ValueError, "api.option('a').value.set(1)")
def test_consistency_not_equal(): def test_consistency_not_equal():
a = IntOption('a', '') a = IntOption('a', '')
b = IntOption('b', '') b = IntOption('b', '')
@ -786,6 +802,48 @@ def test_consistency_with_callback():
api.option('c').value.get() api.option('c').value.get()
def test_consistency_warnings_only_options():
a = IPOption('a', '', warnings_only=True)
b = IPOption('b', '')
c = NetworkOption('c', '', default='192.168.1.0')
d = NetmaskOption('d', '', default='255.255.255.0', properties=('disabled',))
od = OptionDescription('od', '', [a, b, c, d])
a.impl_add_consistency('not_equal', b)
a.impl_add_consistency('in_network', c, d, transitive=False)
api = Config(od)
api.property.read_write()
api.option('a').value.set('192.168.1.1')
raises(ValueError, "api.option('b').value.set('192.168.1.1')")
api.option('a').value.set('192.168.2.1')
#
api.option('a').value.set('192.168.1.1')
api.property.pop('disabled')
with warnings.catch_warnings(record=True) as w:
api.option('a').value.set('192.168.2.1')
assert len(w) == 1
#def test_consistency_warnings_only_options_callback():
# a = IPOption('a', '', warnings_only=True)
# b = IPOption('b', '')
# c = NetworkOption('c', '', default='192.168.1.0')
# d = NetmaskOption('d', '', callback=return_netmask2, callback_params=Params(ParamOption(a)))
# od = OptionDescription('od', '', [a, b, c, d])
# a.impl_add_consistency('not_equal', b)
# a.impl_add_consistency('in_network', c, d, transitive=False)
# api = Config(od)
# api.property.read_write()
# api.option('a').value.set('192.168.1.1')
# raises(ValueError, "api.option('b').value.set('192.168.1.1')")
# api.option('a').value.set('192.168.2.1')
# #
# api.option('a').value.set('192.168.1.1')
# api.property.pop('disabled')
# with warnings.catch_warnings(record=True) as w:
# api.option('a').value.set('192.168.2.1')
# assert len(w) == 1
def test_consistency_double_warnings(): def test_consistency_double_warnings():
a = IntOption('a', '') a = IntOption('a', '')
b = IntOption('b', '', 1) b = IntOption('b', '', 1)
@ -802,10 +860,7 @@ def test_consistency_double_warnings():
assert len(w) == 2 assert len(w) == 2
with warnings.catch_warnings(record=True) as w: with warnings.catch_warnings(record=True) as w:
api.option('od.c').value.set(2) api.option('od.c').value.set(2)
if TIRAMISU_VERSION == 2:
assert len(w) == 0 assert len(w) == 0
else:
assert len(w) == 1
with warnings.catch_warnings(record=True) as w: with warnings.catch_warnings(record=True) as w:
api.option('od.a').value.set(2) api.option('od.a').value.set(2)
assert w != [] assert w != []

View File

@ -71,6 +71,7 @@ def manager_callback(callbk: Union[ParamOption, ParamValue],
# don't validate if option is option that we tried to validate # don't validate if option is option that we tried to validate
config_bag = option_bag.config_bag.copy() config_bag = option_bag.config_bag.copy()
config_bag.set_permissive() config_bag.set_permissive()
config_bag.properties -= {'warnings'}
soption_bag = OptionBag() soption_bag = OptionBag()
soption_bag.set_option(opt, soption_bag.set_option(opt,
path, path,

View File

@ -626,7 +626,7 @@ class SubConfig(object):
descr = self.cfgimpl_get_description() descr = self.cfgimpl_get_description()
if not dyn and descr.impl_is_dynoptiondescription(): if not dyn and descr.impl_is_dynoptiondescription():
context_descr = self.cfgimpl_get_context().cfgimpl_get_description() context_descr = self.cfgimpl_get_context().cfgimpl_get_description()
return context_descr.impl_get_path_by_opt(descr.impl_getopt()) return descr.impl_getopt().impl_getpath(context_descr)
return self._impl_path return self._impl_path

View File

@ -57,6 +57,7 @@ class Base(object):
"""Base use by all *Option* classes (Option, OptionDescription, SymLinkOption, ...) """Base use by all *Option* classes (Option, OptionDescription, SymLinkOption, ...)
""" """
__slots__ = ('_name', __slots__ = ('_name',
'_path',
'_informations', '_informations',
#calcul #calcul
'_subdyn', '_subdyn',
@ -407,7 +408,7 @@ class BaseOption(Base):
is_readonly = True is_readonly = True
elif name != '_readonly': elif name != '_readonly':
is_readonly = self.impl_is_readonly() is_readonly = self.impl_is_readonly()
if is_readonly: if is_readonly and (name != '_path' or value != getattr(self, '_path', value)):
raise AttributeError(_('"{}" ({}) object attribute "{}" is' raise AttributeError(_('"{}" ({}) object attribute "{}" is'
' read-only').format(self.__class__.__name__, ' read-only').format(self.__class__.__name__,
self.impl_get_display_name(), self.impl_get_display_name(),
@ -416,7 +417,8 @@ class BaseOption(Base):
def impl_getpath(self, def impl_getpath(self,
context): context):
return context.cfgimpl_get_description().impl_get_path_by_opt(self) return self._path
#return context.cfgimpl_get_description().impl_get_path_by_opt(self)
def impl_has_callback(self): def impl_has_callback(self):
"to know if a callback has been defined or not" "to know if a callback has been defined or not"

View File

@ -194,6 +194,7 @@ class Option(OnlyOption):
else: else:
config_bag = undefined config_bag = undefined
force_index = None force_index = None
is_warnings_only = getattr(self, '_warnings_only', False)
if check_error and config_bag is not undefined and \ if check_error and config_bag is not undefined and \
not 'validator' in config_bag.properties: not 'validator' in config_bag.properties:
@ -201,7 +202,8 @@ class Option(OnlyOption):
self.valid_consistency(option_bag, self.valid_consistency(option_bag,
value, value,
context, context,
check_error) check_error,
is_warnings_only)
return return
def _is_not_unique(value): def _is_not_unique(value):
@ -250,15 +252,27 @@ class Option(OnlyOption):
option_bag.ori_option) option_bag.ori_option)
if ((check_error and not is_warnings_only) or if ((check_error and not is_warnings_only) or
(not check_error and is_warnings_only)): (not check_error and is_warnings_only)):
try:
calculation_validator(_value, calculation_validator(_value,
_index) _index)
self._second_level_validation(_value, self._second_level_validation(_value,
is_warnings_only) is_warnings_only)
except ValueError as err:
if is_warnings_only:
msg = _('attention, "{0}" could be an invalid {1} for "{2}"'
'').format(val,
self._display_name,
self.impl_get_display_name())
err_msg = '{0}'.format(err)
if err_msg:
msg += ', {}'.format(err_msg)
warnings.warn_explicit(ValueWarning(msg, weakref.ref(self)),
ValueWarning,
self.__class__.__name__, 0)
else:
raise err
#if is_multi is None: #if is_multi is None:
# is_multi = self.impl_is_multi() # is_multi = self.impl_is_multi()
is_warnings_only = getattr(self, '_warnings_only', False)
try: try:
val = value val = value
if not self.impl_is_multi(): if not self.impl_is_multi():
@ -294,20 +308,16 @@ class Option(OnlyOption):
self.valid_consistency(option_bag, self.valid_consistency(option_bag,
value, value,
context, context,
check_error) check_error,
is_warnings_only)
except ValueError as err: except ValueError as err:
#raise err
if debug: # pragma: no cover if debug: # pragma: no cover
log.debug('do_validation: value: {0}, index: {1}:' log.debug('do_validation: value: {0}, index: {1}:'
' {2}'.format(val, ' {2}'.format(val,
force_index, force_index,
err), err),
exc_info=True) exc_info=True)
if is_warnings_only:
msg = _('attention, "{0}" could be an invalid {1} for "{2}"'
'').format(val,
self._display_name,
self.impl_get_display_name())
else:
msg = _('"{0}" is an invalid {1} for "{2}"' msg = _('"{0}" is an invalid {1} for "{2}"'
'').format(val, '').format(val,
self._display_name, self._display_name,
@ -315,12 +325,7 @@ class Option(OnlyOption):
err_msg = '{0}'.format(err) err_msg = '{0}'.format(err)
if err_msg: if err_msg:
msg += ', {}'.format(err_msg) msg += ', {}'.format(err_msg)
if check_error:
raise ValueError(msg) raise ValueError(msg)
else:
warnings.warn_explicit(ValueWarning(msg, weakref.ref(self)),
ValueWarning,
self.__class__.__name__, 0)
def impl_is_dynsymlinkoption(self): def impl_is_dynsymlinkoption(self):
@ -449,7 +454,8 @@ class Option(OnlyOption):
option_bag, option_bag,
value, value,
context, context,
check_error): check_error,
option_warnings_only):
if context is not undefined: if context is not undefined:
descr = context.cfgimpl_get_description() descr = context.cfgimpl_get_description()
# no consistency found at all # no consistency found at all
@ -468,9 +474,10 @@ class Option(OnlyOption):
cconfig_bag = undefined cconfig_bag = undefined
else: else:
cconfig_bag = option_bag.config_bag.copy() cconfig_bag = option_bag.config_bag.copy()
cconfig_bag.properties = cconfig_bag.properties - {'warnings'}
cconfig_bag.set_permissive() cconfig_bag.set_permissive()
for cons_id, func, all_cons_opts, params in consistencies: for cons_id, func, all_cons_opts, params in consistencies:
warnings_only = params.get('warnings_only', False) warnings_only = option_warnings_only or params.get('warnings_only', False)
if (warnings_only and not check_error) or (not warnings_only and check_error): if (warnings_only and not check_error) or (not warnings_only and check_error):
transitive = params.get('transitive', True) transitive = params.get('transitive', True)
#all_cons_opts[0] is the option where func is set #all_cons_opts[0] is the option where func is set

View File

@ -213,6 +213,7 @@ class CacheOptionDescription(BaseOption):
path = str('.'.join(_currpath + [attr])) path = str('.'.join(_currpath + [attr]))
cache_option.append(option) cache_option.append(option)
cache_path.append(path) cache_path.append(path)
option._path = path
if option.impl_is_optiondescription(): if option.impl_is_optiondescription():
_currpath.append(attr) _currpath.append(attr)
option._build_cache_option(_currpath, option._build_cache_option(_currpath,
@ -230,7 +231,7 @@ class OptionDescriptionWalk(CacheOptionDescription):
option_bag): option_bag):
option = option_bag.option option = option_bag.option
dynopt = option.getsubdyn() dynopt = option.getsubdyn()
rootpath = self.impl_get_path_by_opt(dynopt) rootpath = dynopt.impl_getpath(option_bag.config_bag.context)
ori_index = len(rootpath) + 1 ori_index = len(rootpath) + 1
subpaths = [rootpath] + option.impl_getpath( subpaths = [rootpath] + option.impl_getpath(
option_bag.config_bag.context)[ori_index:].split('.')[:-1] option_bag.config_bag.context)[ori_index:].split('.')[:-1]

View File

@ -23,6 +23,7 @@ from ..i18n import _
class SymLinkOption(OnlyOption): class SymLinkOption(OnlyOption):
__slots__ = ('_opt',)
def __init__(self, def __init__(self,
name, name,
@ -39,6 +40,8 @@ class SymLinkOption(OnlyOption):
def __getattr__(self, def __getattr__(self,
name): name):
if name == '_path':
return
return getattr(self._opt, name) return getattr(self._opt, name)
def impl_has_dependency(self, def impl_has_dependency(self,
@ -55,7 +58,7 @@ class SymLinkOption(OnlyOption):
return self._opt return self._opt
def impl_is_readonly(self): def impl_is_readonly(self):
return True return self._path is not None
def get_consistencies(self): def get_consistencies(self):
return () return ()

View File

@ -138,7 +138,7 @@ class OptionBag:
index, index,
config_bag): config_bag):
if path is None: if path is None:
path = config_bag.context.cfgimpl_get_description().impl_get_path_by_opt(option) path = option.impl_getpath(config_bag.context)
self.path = path self.path = path
self.index = index self.index = index
self.option = option self.option = option
@ -156,6 +156,11 @@ class OptionBag:
return True return True
raise KeyError('unknown key {} for OptionBag'.format(key)) raise KeyError('unknown key {} for OptionBag'.format(key))
def __delattr__(self, key):
if key == 'properties':
return
raise KeyError('unknown key {} for ConfigBag'.format(key))
def copy(self): def copy(self):
kwargs = {} kwargs = {}
option_bag = OptionBag() option_bag = OptionBag()

View File

@ -88,7 +88,7 @@ class Cache(DictCache):
print('getcache expired value for path {} < {}'.format( print('getcache expired value for path {} < {}'.format(
timestamp + expires_time, ntime)) timestamp + expires_time, ntime))
# if expired, remove from cache # if expired, remove from cache
self.delcache(path) #self.delcache(path)
else: else:
if DEBUG: if DEBUG:
print('getcache in cache (2)', path, value, _display_classname(self), print('getcache in cache (2)', path, value, _display_classname(self),

View File

@ -84,6 +84,8 @@ class Values(object):
# validate value # validate value
context = self._getcontext() context = self._getcontext()
opt = option_bag.option opt = option_bag.option
#print('===', option_bag.path)
#if not is_cached:
opt.impl_validate(value, opt.impl_validate(value,
option_bag, option_bag,
context=context, context=context,
@ -160,13 +162,15 @@ class Values(object):
opt = option_bag.option opt = option_bag.option
index = option_bag.index index = option_bag.index
def _reset_cache(_value): def _reset_cache(_value):
if not 'expire' in option_bag.properties:
return
is_cache, cache_value = self._p_.getcache(option_bag.path, is_cache, cache_value = self._p_.getcache(option_bag.path,
expires_time, None,
index, index,
config_bag.properties, config_bag.properties,
option_bag.properties, option_bag.properties,
'value') 'value')
if is_cache and cache_value == _value: if not is_cache or cache_value == _value:
# calculation return same value as previous value, # calculation return same value as previous value,
# so do not invalidate cache # so do not invalidate cache
return return