better cache

This commit is contained in:
Emmanuel Garette 2017-07-08 15:59:56 +02:00
parent 6bad3c6e64
commit dadf859905
21 changed files with 476 additions and 109 deletions

View file

@ -1,3 +1,7 @@
Sat Jul 8 15:57:13 2017 +0200 Emmanuel Garette <egarette@cadoles.com>
* better cache, only remove value/property from cache for value
modified and for all value affected by this modification
Sat May 20 16:27:09 2017 +0200 Emmanuel Garette <egarette@cadoles.com>
* add 'operator' to requirement

View file

@ -5,7 +5,7 @@ do_autopath()
from tiramisu import setting, value
setting.expires_time = 1
value.expires_time = 1
from tiramisu.option import IntOption, StrOption, OptionDescription
from tiramisu.option import BoolOption, IPOption, IntOption, StrOption, OptionDescription
from tiramisu.config import Config
from tiramisu.error import ConfigError
from tiramisu.setting import groups
@ -77,31 +77,42 @@ def test_cache_reset():
settings = c.cfgimpl_get_settings()
#when change a value
c.u1
c.u2
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
assert 'u2' in values._p_.get_cached(c)
assert 'u2' in settings._p_.get_cached(c)
c.u2 = 1
assert 'u1' not in values._p_.get_cached(c)
assert 'u1' not in settings._p_.get_cached(c)
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
assert 'u2' not in values._p_.get_cached(c)
assert 'u2' not in settings._p_.get_cached(c)
#when remove a value
c.u1
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
del(c.u2)
assert 'u1' not in values._p_.get_cached(c)
assert 'u1' not in settings._p_.get_cached(c)
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
assert 'u2' not in values._p_.get_cached(c)
assert 'u2' not in settings._p_.get_cached(c)
#when add/del property
c.u1
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
c.cfgimpl_get_settings()[od1.u2].append('test')
assert 'u1' not in values._p_.get_cached(c)
assert 'u1' not in settings._p_.get_cached(c)
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
assert 'u2' not in values._p_.get_cached(c)
assert 'u2' not in settings._p_.get_cached(c)
c.u1
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
c.cfgimpl_get_settings()[od1.u2].remove('test')
assert 'u1' not in values._p_.get_cached(c)
assert 'u1' not in settings._p_.get_cached(c)
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
assert 'u2' not in values._p_.get_cached(c)
assert 'u2' not in settings._p_.get_cached(c)
#when enable/disabled property
c.u1
assert 'u1' in values._p_.get_cached(c)
@ -122,34 +133,51 @@ def test_cache_reset_multi():
c = Config(od1)
values = c.cfgimpl_get_values()
settings = c.cfgimpl_get_settings()
#when change a value
c.u1
c.u3
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
assert 'u3' in values._p_.get_cached(c)
assert 'u3' in settings._p_.get_cached(c)
#when change a value
c.u3 = [1]
assert 'u1' not in values._p_.get_cached(c)
assert 'u1' not in settings._p_.get_cached(c)
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
assert 'u3' not in values._p_.get_cached(c)
assert 'u3' not in settings._p_.get_cached(c)
#when append value
c.u1
c.u3
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
assert 'u3' in values._p_.get_cached(c)
assert 'u3' in settings._p_.get_cached(c)
c.u3.append(1)
assert 'u1' not in values._p_.get_cached(c)
assert 'u1' not in settings._p_.get_cached(c)
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
assert 'u3' not in values._p_.get_cached(c)
assert 'u3' not in settings._p_.get_cached(c)
#when pop value
c.u1
c.u3
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
assert 'u3' in values._p_.get_cached(c)
assert 'u3' in settings._p_.get_cached(c)
c.u3.pop(1)
assert 'u1' not in values._p_.get_cached(c)
assert 'u1' not in settings._p_.get_cached(c)
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
assert 'u3' not in values._p_.get_cached(c)
assert 'u3' not in settings._p_.get_cached(c)
#when remove a value
c.u1
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
del(c.u3)
assert 'u1' not in values._p_.get_cached(c)
assert 'u1' not in settings._p_.get_cached(c)
assert 'u1' in values._p_.get_cached(c)
assert 'u1' in settings._p_.get_cached(c)
assert 'u3' not in values._p_.get_cached(c)
assert 'u3' not in settings._p_.get_cached(c)
def test_reset_cache():
@ -348,3 +376,210 @@ def test_cache_master_slave():
assert set(cache['ip_admin_eth0.ip_admin_eth0'].keys()) == set([None])
assert set(cache['ip_admin_eth0.netmask_admin_eth0'].keys()) == set([None, 0, 1])
#DEL, insert, ...
def return_value(value=None):
return value
def test_cache_callback():
val1 = StrOption('val1', "", 'val')
val2 = StrOption('val2', "", callback=return_value, callback_params={'': ((val1, False),)}, properties=('mandatory',))
val3 = StrOption('val3', "", callback=return_value, callback_params={'': ('yes',)})
val4 = StrOption('val4', "", callback=return_value, callback_params={'value': ((val1, False),)})
val5 = StrOption('val5', "", callback=return_value, callback_params={'value': ('yes',)}, multi=True)
maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4, val5])
cfg = Config(maconfig)
cfg.cfgimpl_get_settings().remove('expire')
cfg.read_write()
cfg.cfgimpl_get_values().force_cache()
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)},
'val2': {None: (set(['mandatory']), None)},
'val3': {None: (set([]), None)},
'val4': {None: (set([]), None)},
'val5': {None: (set(['empty']), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val1': {None: ('val', None)},
'val2': {None: ('val', None)},
'val3': {None: ('yes', None)},
'val4': {None: ('val', None)},
'val5': {None: (['yes'], None)}}
cfg.val1 = 'new'
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val3': {None: (set([]), None)},
'val5': {None: (set(['empty']), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val3': {None: ('yes', None)},
'val5': {None: (['yes'], None)}}
cfg.cfgimpl_get_values().force_cache()
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)},
'val2': {None: (set(['mandatory']), None)},
'val3': {None: (set([]), None)},
'val4': {None: (set([]), None)},
'val5': {None: (set(['empty']), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val1': {None: ('new', None)},
'val2': {None: ('new', None)},
'val3': {None: ('yes', None)},
'val4': {None: ('new', None)},
'val5': {None: (['yes'], None)}}
cfg.val3 = 'new2'
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)},
'val2': {None: (set(['mandatory']), None)},
'val4': {None: (set([]), None)},
'val5': {None: (set(['empty']), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val1': {None: ('new', None)},
'val2': {None: ('new', None)},
'val4': {None: ('new', None)},
'val5': {None: (['yes'], None)}}
cfg.cfgimpl_get_values().force_cache()
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)},
'val2': {None: (set(['mandatory']), None)},
'val3': {None: (set([]), None)},
'val4': {None: (set([]), None)},
'val5': {None: (set(['empty']), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val1': {None: ('new', None)},
'val2': {None: ('new', None)},
'val3': {None: ('new2', None)},
'val4': {None: ('new', None)},
'val5': {None: (['yes'], None)}}
cfg.val4 = 'new3'
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)},
'val2': {None: (set(['mandatory']), None)},
'val3': {None: (set([]), None)},
'val5': {None: (set(['empty']), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val1': {None: ('new', None)},
'val2': {None: ('new', None)},
'val3': {None: ('new2', None)},
'val5': {None: (['yes'], None)}}
cfg.cfgimpl_get_values().force_cache()
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)},
'val2': {None: (set(['mandatory']), None)},
'val3': {None: (set([]), None)},
'val4': {None: (set([]), None)},
'val5': {None: (set(['empty']), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val1': {None: ('new', None)},
'val2': {None: ('new', None)},
'val3': {None: ('new2', None)},
'val4': {None: ('new3', None)},
'val5': {None: (['yes'], None)}}
cfg.val5.append('new4')
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)},
'val2': {None: (set(['mandatory']), None)},
'val3': {None: (set([]), None)},
'val4': {None: (set([]), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val1': {None: ('new', None)},
'val2': {None: ('new', None)},
'val3': {None: ('new2', None)},
'val4': {None: ('new3', None)}}
cfg.cfgimpl_get_values().force_cache()
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)},
'val2': {None: (set(['mandatory']), None)},
'val3': {None: (set([]), None)},
'val4': {None: (set([]), None)},
'val5': {None: (set(['empty']), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val1': {None: ('new', None)},
'val2': {None: ('new', None)},
'val3': {None: ('new2', None)},
'val4': {None: ('new3', None)},
'val5': {None: (['yes', 'new4'], None)}}
def test_cache_master_and_slaves_master():
val1 = StrOption('val1', "", multi=True)
val2 = StrOption('val2', "", multi=True)
interface1 = OptionDescription('val1', '', [val1, val2])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('rootconfig', '', [interface1])
cfg = Config(maconfig)
cfg.cfgimpl_get_settings().remove('expire')
cfg.read_write()
cfg.cfgimpl_get_values().force_cache()
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)},
'val1.val1': {None: (set(['empty']), None)},
'val1.val2': {None: (set([]), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val1.val1': {None: ([], None)}, 'val1.val2': {None: ([], None)}}
cfg.val1.val1.append()
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {}
cfg.cfgimpl_get_values().force_cache()
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)},
'val1.val1': {None: (set(['empty']), None)},
'val1.val2': {None: (set([]), None), 0: (set([]), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val1.val1': {None: ([None], None)},
'val1.val2': {None: ([None], None), 0: (None, None)}}
cfg.val1.val1.append()
cfg.cfgimpl_get_values().force_cache()
cfg.val1.val2[1] = 'oui'
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)},
'val1.val1': {None: (set(['empty']), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val1.val1': {None: ([None, None], None)}}
cfg.cfgimpl_get_values().force_cache()
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)},
'val1.val1': {None: (set(['empty']), None)},
'val1.val2': {None: (set([]), None), 0: (set([]), None), 1: (set([]), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val1.val1': {None: ([None, None], None)},
'val1.val2': {None: ([None, 'oui'], None), 0: (None, None), 1: ('oui', None)}}
def test_cache_master_callback():
val1 = StrOption('val1', "", multi=True)
val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'value': ((val1, False),)})
interface1 = OptionDescription('val1', '', [val1, val2])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('rootconfig', '', [interface1])
cfg = Config(maconfig)
cfg.cfgimpl_get_settings().remove('expire')
cfg.read_write()
cfg.cfgimpl_get_values().force_cache()
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)},
'val1.val1': {None: (set(['empty']), None)},
'val1.val2': {None: (set([]), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val1.val1': {None: ([], None)}, 'val1.val2': {None: ([], None)}}
cfg.val1.val1.append()
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {}
cfg.cfgimpl_get_values().force_cache()
assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {'val1': {None: (set([]), None)},
'val1.val1': {None: (set(['empty']), None)},
'val1.val2': {None: (set([]), None), 0: (set([]), None)}}
assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {'val1.val1': {None: ([None], None)},
'val1.val2': {None: ([None], None), 0: (None, None)}}
def test_cache_requires():
a = BoolOption('activate_service', '', True)
b = IPOption('ip_address_service', '',
requires=[{'option': a, 'expected': False, 'action': 'disabled'}])
od = OptionDescription('service', '', [a, b])
c = Config(od)
c.cfgimpl_get_settings().remove('expire')
c.read_write()
assert c.cfgimpl_get_settings()._p_.get_cached(c) == {}
assert c.cfgimpl_get_values()._p_.get_cached(c) == {}
assert c.ip_address_service == None
assert c.cfgimpl_get_settings()._p_.get_cached(c) == {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}}
assert c.cfgimpl_get_values()._p_.get_cached(c) == {'ip_address_service': {None: (None, None)}}
c.cfgimpl_get_values().force_cache()
assert c.cfgimpl_get_settings()._p_.get_cached(c) == {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}}
assert c.cfgimpl_get_values()._p_.get_cached(c) == {'ip_address_service': {None: (None, None)},
'activate_service': {None: (True, None)}}
c.ip_address_service = '1.1.1.1'
assert c.cfgimpl_get_settings()._p_.get_cached(c) == {'activate_service': {None: (set([]), None)}}
assert c.cfgimpl_get_values()._p_.get_cached(c) == {'activate_service': {None: (True, None)}}
c.cfgimpl_get_values().force_cache()
assert c.cfgimpl_get_settings()._p_.get_cached(c) == {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}}
assert c.cfgimpl_get_values()._p_.get_cached(c) == {'ip_address_service': {None: ('1.1.1.1', None)},
'activate_service': {None: (True, None)}}
c.activate_service = False
assert c.cfgimpl_get_settings()._p_.get_cached(c) == {}
assert c.cfgimpl_get_values()._p_.get_cached(c) == {}
c.cfgimpl_get_values().force_cache()
assert c.cfgimpl_get_settings()._p_.get_cached(c) == {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set(['disabled']), None)}}
assert c.cfgimpl_get_values()._p_.get_cached(c) == {'activate_service': {None: (False, None)}}

View file

@ -3,7 +3,7 @@ do_autopath()
from tiramisu.option import BoolOption, UnicodeOption, SymLinkOption, \
IntOption, IPOption, NetmaskOption, StrOption, OptionDescription, \
DynOptionDescription
DynOptionDescription, MasterSlaves
from tiramisu.config import Config, GroupConfig, MetaConfig
from tiramisu.setting import groups, owners
from tiramisu.storage import delete_session
@ -118,8 +118,15 @@ def _diff_opt(opt1, opt2):
assert val1.impl_getname() == val2.impl_getname()
except AttributeError:
assert val1 == val2
elif attr == '_dependencies':
assert len(val1) == len(val2)
for idx, val in enumerate(val1):
if isinstance(val, MasterSlaves):
assert val._p_.master.impl_getname() == val2[idx]._p_.master.impl_getname()
else:
assert val.impl_getname() == val2[idx].impl_getname()
else:
assert val1 == val2
assert val1 == val2, "error for {}".format(attr)
def _diff_opts(opt1, opt2):

View file

@ -1,4 +1,4 @@
# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2012-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

View file

@ -1,4 +1,4 @@
# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2012-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

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2012-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
@ -21,6 +21,7 @@
"options handler global entry point"
import weakref
import sys
from time import time
from .error import PropertiesOptionError, ConfigError, ConflictError
@ -70,10 +71,78 @@ class SubConfig(object):
self._impl_context = context
self._impl_path = subpath
def cfgimpl_reset_cache(self, only_expired=False, only=('values',
'settings')):
"remove cache (in context)"
self._cfgimpl_get_context().cfgimpl_reset_cache(only_expired, only) # pragma: optional cover
def cfgimpl_reset_cache(self,
only_expired=False,
only=('values', 'settings'),
opt=None,
path=None):
"""reset all settings in cache
:param only_expired: if True reset only expired cached values
:type only_expired: boolean
"""
context = self._cfgimpl_get_context()
if 'values' in only:
values = context.cfgimpl_get_values()
if 'settings' in only:
settings = context.cfgimpl_get_settings()
if only_expired:
if 'values' in only:
values._p_.reset_expired_cache(int(time()))
if 'settings' in only:
settings._p_.reset_expired_cache(int(time()))
elif not None in (opt, path):
if opt.__class__.__name__ == 'DynOptionDescription':
descr = context.cfgimpl_get_description()
spath = path.split('.')
subpath = '.'.join(spath[:-1])
dynopt = getattr(descr, subpath)._getattr(spath[-1], context=context,
dyn=False)
for suffix in dynopt._impl_get_suffixes(context):
path = subpath + '.' + spath[-1] + suffix
if 'values' in only:
values._p_.delcache(path)
if 'settings' in only:
settings._p_.delcache(path)
elif not isinstance(opt, DynSymLinkOption) and opt._is_subdyn():
descr = context.cfgimpl_get_description()
spath = path.split('.')
try:
subpath = '.'.join(spath[:-2])
dynsubopt = getattr(descr, subpath)
spath1 = spath[-2]
spath2 = spath[-1]
spath3 = None
except AttributeError:
subpath = '.'.join(spath[:-3])
dynsubopt = getattr(descr, subpath)
spath1 = spath[-3]
spath2 = spath[-2]
spath3 = spath[-1]
dynopt = dynsubopt._getattr(spath1, context=context, dyn=False)
for suffix in dynopt._impl_get_suffixes(context):
path = subpath + '.' + spath1 + suffix + '.' + spath2 + suffix
if spath3:
path += '.' + spath3 + suffix
if 'values' in only:
values._p_.delcache(path)
if 'settings' in only:
settings._p_.delcache(path)
else:
if 'values' in only:
values._p_.delcache(path)
if 'settings' in only:
settings._p_.delcache(path)
for option in getattr(opt, '_dependencies', []):
if 'values' in only:
option.reset_cache(opt, values, 'values')
if 'settings' in only:
option.reset_cache(opt, settings, 'settings')
else:
if 'values' in only:
values._p_.reset_all_cache()
if 'settings' in only:
settings._p_.reset_all_cache()
def cfgimpl_get_home_by_path(self, path, force_permissive=False,
returns_raise=False):
@ -678,7 +747,7 @@ class Config(_CommonConfig):
def __init__(self, descr, session_id=None, persistent=False,
name=undefined, force_values=None, force_settings=None,
_duplicate=False):
_duplicate=False, mandatory_name=False):
""" Configuration option management master class
:param descr: describes the configuration schema
@ -700,6 +769,8 @@ class Config(_CommonConfig):
name = 'config'
if session_id is not None:
name += session_id
if mandatory_name and name is None:
raise ValueError(_("name is mandatory for the config").format(name))
if name is not None and not valid_name(name): # pragma: optional cover
raise ValueError(_("invalid name: {0} for config").format(name))
self._impl_settings = Settings(self, settings)
@ -712,14 +783,6 @@ class Config(_CommonConfig):
self._impl_build_all_caches()
self._impl_name = name
def cfgimpl_reset_cache(self,
only_expired=False,
only=('values', 'settings')):
if 'values' in only:
self.cfgimpl_get_values().reset_cache(only_expired=only_expired)
if 'settings' in only:
self.cfgimpl_get_settings().reset_cache(only_expired=only_expired)
def impl_getname(self):
return self._impl_name
@ -763,19 +826,15 @@ class GroupConfig(_CommonConfig):
def cfgimpl_get_children(self):
return self._impl_children
#def cfgimpl_get_context(self):
# "a meta config is a config which has a setting, that is itself"
# return self
def cfgimpl_reset_cache(self,
only_expired=False,
only=('values', 'settings')):
if 'values' in only:
self.cfgimpl_get_values().reset_cache(only_expired=only_expired)
if 'settings' in only:
self.cfgimpl_get_settings().reset_cache(only_expired=only_expired)
only=('values', 'settings'),
opt=None,
path=None):
if isinstance(self, MetaConfig):
super(GroupConfig, self).cfgimpl_reset_cache(only_expired=only_expired, only=only, opt=opt, path=path)
for child in self._impl_children:
child.cfgimpl_reset_cache(only_expired=only_expired, only=only)
child.cfgimpl_reset_cache(only_expired=only_expired, only=only, opt=opt, path=path)
def set_value(self, path, value):
"""Setattr not in current GroupConfig, but in each children
@ -906,3 +965,7 @@ class MetaConfig(GroupConfig):
setattr(child, path, child_value)
setattr(self, path, value)
def new_config(self, session_id=None, name=undefined):
return Config(self._impl_descr, _duplicate=True, session_id=session_id, name=name,
mandatory_name=True)

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2012-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

View file

@ -1,5 +1,5 @@
# -*- coding: UTF-8 -*-
# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2012-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

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2014 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2014-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
@ -25,7 +25,7 @@ import sys
from inspect import getargspec
from ..i18n import _
from ..setting import log, undefined, debug
from ..setting import log, undefined, debug, groups
from ..autolib import carry_out_calculation
from ..error import (ConfigError, ValueWarning, PropertiesOptionError,
display_list)
@ -58,7 +58,7 @@ def valid_name(name):
return False
def validate_callback(callback, callback_params, type_):
def validate_callback(callback, callback_params, type_, callbackoption):
if not isinstance(callback, FunctionType):
raise ValueError(_('{0} must be a function').format(type_))
if callback_params is not None:
@ -94,6 +94,17 @@ def validate_callback(callback, callback_params, type_):
' not a {} for second argument'
).format(type_, type(
force_permissive)))
if isinstance(option, SymLinkOption):
cur_opt = option._impl_getopt()
else:
cur_opt = option
if cur_opt != callbackoption:
if not getattr(cur_opt, '_dependencies', None):
options = []
else:
options = list(cur_opt._dependencies)
options.append(callbackoption)
cur_opt._dependencies = tuple(options)
#____________________________________________________________
#
@ -127,7 +138,7 @@ class Base(StorageBase):
if not is_multi and unique is True:
raise ValueError(_('unique must be set only with multi value'))
if requires is not None:
calc_properties, requires = validate_requires_arg(is_multi,
calc_properties, requires = validate_requires_arg(self, is_multi,
requires, name)
else:
calc_properties = frozenset()
@ -143,7 +154,7 @@ class Base(StorageBase):
if multi: # and validator_params is None:
validator_params = self._build_validator_params(validator, validator_params)
validate_callback(validator, validator_params, 'validator')
validate_callback(validator, validator_params, 'validator', self)
self._set_validator(validator, validator_params)
self._set_has_dependency()
if calc_properties != frozenset([]) and properties is not tuple():
@ -217,7 +228,7 @@ class Base(StorageBase):
"cannot set another one's").format(self.impl_getname()))
self._validate_callback(callback, callback_params)
if callback is not None:
validate_callback(callback, callback_params, 'callback')
validate_callback(callback, callback_params, 'callback', self)
self._set_callback(callback, callback_params)
def impl_is_optiondescription(self):
@ -239,6 +250,36 @@ class BaseOption(Base):
# ____________________________________________________________
# serialize object
def _impl_convert_dependencies(self, descr, load=False):
"""export of the requires during the serialization process
:type descr: :class:`tiramisu.option.OptionDescription`
:param load: `True` if we are at the init of the option description
:type load: bool
"""
if not load and getattr(self, '_dependencies', None) is None:
self._state_dependencies = None
elif load and self._state_dependencies is None:
del(self._state_dependencies)
else:
if load:
self._dependencies = []
for dependency in self._state_dependencies:
option = descr.impl_get_opt_by_path(dependency)
if option.impl_is_optiondescription() and \
option.impl_get_group_type() == groups.master:
master_path = dependency + '.' + dependency.split('.')[-1]
option = descr.impl_get_opt_by_path(master_path).impl_get_master_slaves()
self._dependencies.append(option)
del(self._state_dependencies)
else:
self._state_dependencies = []
for dependency in self._dependencies:
if isinstance(dependency, MasterSlaves):
self._state_dependencies.append('.'.join(descr.impl_get_path_by_opt(dependency._p_.master).split('.')[:-1]))
else:
self._state_dependencies.append(descr.impl_get_path_by_opt(dependency))
def _impl_convert_requires(self, descr, load=False):
"""export of the requires during the serialization process
@ -417,6 +458,14 @@ class BaseOption(Base):
name = name.encode('utf8')
return name
def reset_cache(self, opt, obj, type_):
context = obj._getcontext()
path = self.impl_getpath(context)
obj._p_.delcache(path)
context.cfgimpl_reset_cache(only=(type_,),
opt=self,
path=path)
class OnlyOption(BaseOption):
__slots__ = tuple()
@ -927,7 +976,7 @@ class Option(OnlyOption):
"is calculated").format(self.impl_getname()))
def validate_requires_arg(multi, requires, name):
def validate_requires_arg(new_option, multi, requires, name):
"""check malformed requirements
and tranform dict to internal tuple
@ -936,6 +985,14 @@ def validate_requires_arg(multi, requires, name):
know more about
the description of the requires dictionary
"""
def set_dependency(option):
if not getattr(option, '_dependencies', None):
options = []
else:
options = list(option._dependencies)
options.append(new_option)
option._dependencies = tuple(options)
def get_option(require):
option = require['option']
if not isinstance(option, Option):
@ -945,6 +1002,7 @@ def validate_requires_arg(multi, requires, name):
raise ValueError(_('malformed requirements '
'multi option must not set '
'as requires of non multi option {0}').format(name))
set_dependency(option)
return option
def _set_expected(action, inverse, transitive, same_action, option, expected, operator):
@ -970,6 +1028,7 @@ def validate_requires_arg(multi, requires, name):
raise ValueError(_('malformed requirements expected must have '
'option and value for option {0}').format(name))
option = exp['option']
set_dependency(option)
if option is not None:
err = option._validate(exp['value'])
if err:

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"master slave support"
# Copyright (C) 2014 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2014-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
@ -275,3 +275,8 @@ class MasterSlaves(object):
raise SlaveError(_("invalid len for the slave: {0}"
" which has {1} as master").format(
name, self.getmaster(opt).impl_getname()))
def reset_cache(self, opt, values, type_):
for slave in self.getslaves(opt):
slave_path = slave.impl_getpath(values._getcontext())
values._p_.delcache(slave_path)

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"option types and option description"
# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2012-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
@ -49,7 +49,7 @@ class ChoiceOption(Option):
:param values: is a list of values the option can possibly take
"""
if isinstance(values, FunctionType):
validate_callback(values, values_params, 'values')
validate_callback(values, values_params, 'values', self)
else:
if values_params is not None:
raise ValueError(_('values is not a function, so values_params must be None'))

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2014 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2014-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
@ -125,6 +125,13 @@ class OptionDescription(BaseOption, StorageOptionDescription):
cache_option, force_store_values)
#cannot set multi option as OptionDescription requires
else:
if option.impl_is_master_slaves('master'):
if not getattr(option, '_dependencies', None):
options = []
else:
options = list(option._dependencies)
options.append(option.impl_get_master_slaves())
option._dependencies = tuple(options)
option._set_readonly(True)
is_multi = option.impl_is_multi()
if not isinstance(option, SymLinkOption) and 'force_store_value' in option.impl_getproperties():
@ -132,8 +139,8 @@ class OptionDescription(BaseOption, StorageOptionDescription):
for func, all_cons_opts, params in option._get_consistencies():
option._valid_consistencies(all_cons_opts[1:], init=False)
if func not in allowed_const_list and is_multi:
is_slave = option.impl_is_master_slaves()
if not is_slave:
is_masterslaves = option.impl_is_master_slaves()
if not is_masterslaves:
raise ValueError(_('malformed consistency option "{0}" '
'must be a master/slaves').format(
option.impl_getname()))
@ -178,7 +185,6 @@ class OptionDescription(BaseOption, StorageOptionDescription):
'must not be a multi for {1}').format(
require_opt.impl_getname(), option.impl_getname()))
if init:
session = config._impl_values._p_.getsession()
if len(cache_option) != len(set(cache_option)):
for idx in xrange(1, len(cache_option) + 1):
opt = cache_option.pop(0)
@ -194,7 +200,6 @@ class OptionDescription(BaseOption, StorageOptionDescription):
self._cache_consistencies[opt] = tuple(cons)
self._cache_force_store_values = force_store_values
self._set_readonly(False)
del(session)
def impl_build_force_store_values(self, config):
@ -408,8 +413,7 @@ class SynDynOptionDescription(object):
def _impl_getchildren(self, dyn=True, context=undefined):
children = []
for child in self._opt._impl_getchildren():
children.append(self._opt._impl_get_dynchild(child, self._suffix))
return children
yield(self._opt._impl_get_dynchild(child, self._suffix))
def impl_getchildren(self):
return self._impl_getchildren()

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"sets the options of the configuration objects Config object itself"
# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2012-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
@ -269,7 +269,7 @@ class Property(object):
raise ConfigError(_('cannot add those properties: {0}').format(propname))
self._properties.add(propname)
if save:
self._setting._setproperties(self._properties, self._path, force=True)
self._setting._setproperties(self._properties, self._opt, self._path, force=True)
def remove(self, propname):
"""Removes a property named propname
@ -279,7 +279,7 @@ class Property(object):
"""
if propname in self._properties:
self._properties.remove(propname)
self._setting._setproperties(self._properties, self._path)
self._setting._setproperties(self._properties, self._opt, self._path)
def extend(self, propnames):
"""Extends properties to the existing properties
@ -289,7 +289,7 @@ class Property(object):
"""
for propname in propnames:
self._append(propname, save=False)
self._setting._setproperties(self._properties, self._path)
self._setting._setproperties(self._properties, self._opt, self._path)
def reset(self):
"""resets the properties (does not **clear** the properties,
@ -370,7 +370,7 @@ class Settings(object):
if opt is not None and _path is None:
_path = opt.impl_getpath(self._getcontext())
self._p_.delproperties(_path)
self._getcontext().cfgimpl_reset_cache()
self._getcontext().cfgimpl_reset_cache(opt=opt, path=_path)
def _getproperties(self, opt=None, path=None,
setting_properties=undefined, read_write=True,
@ -415,20 +415,20 @@ class Settings(object):
props = self._p_.getproperties(None, default_properties)
if propname not in props:
props.add(propname)
self._setproperties(props, None)
self._setproperties(props, None, None)
def remove(self, propname):
"deletes property propname in the Config's properties attribute"
props = self._p_.getproperties(None, default_properties)
if propname in props:
props.remove(propname)
self._setproperties(props, None)
self._setproperties(props, None, None)
def extend(self, propnames):
for propname in propnames:
self.append(propname)
def _setproperties(self, properties, path, force=False):
def _setproperties(self, properties, opt, path, force=False):
"""save properties for specified path
(never save properties if same has option properties)
"""
@ -438,7 +438,7 @@ class Settings(object):
raise ConfigError(_('cannot add those properties: {0}').format(
' '.join(forbidden_properties)))
self._p_.setproperties(path, properties)
self._getcontext().cfgimpl_reset_cache()
self._getcontext().cfgimpl_reset_cache(opt=opt, path=path)
#____________________________________________________________
def validate_properties(self, opt_or_descr, is_descr, check_frozen, path,
@ -541,7 +541,7 @@ class Settings(object):
if not isinstance(permissive, tuple): # pragma: optional cover
raise TypeError(_('permissive must be a tuple'))
self._p_.setpermissive(path, permissive)
self._getcontext().cfgimpl_reset_cache()
self._getcontext().cfgimpl_reset_cache(opt=opt, path=path)
#____________________________________________________________
def setowner(self, owner):
@ -564,7 +564,7 @@ class Settings(object):
props = props | append
modified = True
if modified:
self._setproperties(props, None)
self._setproperties(props, None, None)
def read_only(self):
"convenience method to freeze, hide and disable"
@ -574,17 +574,6 @@ class Settings(object):
"convenience method to freeze, hide and disable"
self._read(rw_remove, rw_append)
def reset_cache(self, only_expired):
"""reset all settings in cache
:param only_expired: if True reset only expired cached values
:type only_expired: boolean
"""
if only_expired:
self._p_.reset_expired_cache(int(time()))
else:
self._p_.reset_all_cache()
def apply_requires(self, opt, path, setting_properties, index, debug):
"""carries out the jit (just in time) requirements between options

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2013-2014 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-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

View file

@ -56,6 +56,7 @@ class StorageBase(object):
'_choice_values_params',
#other
'_has_dependency',
'_dependencies',
'_state_master_slaves',
'_state_val_call',
'_state_requires',
@ -64,8 +65,9 @@ class StorageBase(object):
'_state_informations',
'_state_extra',
'_state_readonly',
'_state_dependencies',
'__weakref__'
)
)
def __init__(self, name, multi, warnings_only, doc, extra, calc_properties,
requires, properties, allow_empty_list, unique, opt=undefined,
@ -588,10 +590,10 @@ class StorageMasterSlaves(object):
def __init__(self, master, slaves):
self.master = master
self.slaves = slaves
self.slaves = tuple(slaves)
def _sm_getmaster(self):
return self.master
def _sm_getslaves(self):
return tuple(self.slaves)
return self.slaves

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"default plugin for setting: set it in a simple dictionary"
# Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-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

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-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

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"default plugin for value: set it in a simple dictionary"
# Copyright (C) 2013-2014 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-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

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-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

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"utils used by storage"
# Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-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
@ -98,6 +98,9 @@ class Cache(object):
setattr(self, key, value)
def setcache(self, path, val, time, index):
"""add val in cache for a specified path
if slave, add index
"""
self._cache.setdefault(path, {})[index] = (val, time)
def getcache(self, path, exp, index):
@ -106,6 +109,12 @@ class Cache(object):
return True, value
return False, None # pragma: no cover
def delcache(self, path):
"""remove cache for a specified path
"""
if path in self._cache:
del self._cache[path]
def hascache(self, path, index):
""" path is in the cache

View file

@ -201,7 +201,7 @@ class Values(object):
self._setvalue(opt, path, value, force_owner=owners.forced)
else:
self._p_.resetvalue(path, session)
context.cfgimpl_reset_cache()
context.cfgimpl_reset_cache(opt=opt, path=path)
def _isempty(self, opt, value, force_allow_empty_list=False, index=None):
"convenience method to know if an option is empty"
@ -427,7 +427,7 @@ class Values(object):
def _setvalue(self, opt, path, value, force_owner=undefined, index=None):
context = self._getcontext()
context.cfgimpl_reset_cache()
context.cfgimpl_reset_cache(opt=opt, path=path)
if force_owner is undefined:
owner = context.cfgimpl_get_settings().getowner()
else:
@ -599,15 +599,6 @@ class Values(object):
index=index, force_permissive=force_permissive)
return d == owners.default
def reset_cache(self, only_expired):
"""
clears the cache if necessary
"""
if only_expired:
self._p_.reset_expired_cache(int(time()))
else:
self._p_.reset_all_cache()
# information
def set_information(self, key, value):
"""updates the information's attribute
@ -699,8 +690,7 @@ class Values(object):
if not 'cache' in context.cfgimpl_get_settings():
raise ConfigError(_('can force cache only if cache '
'is actived in config'))
#remove all cached properties and value to update "expired" time
context.cfgimpl_reset_cache()
#FIXME properties and value should update "expired" time
for path in context.cfgimpl_get_description().impl_getpaths(
include_groups=True):
err = context.getattr(path, returns_raise=True)