serialize metaconfig/groupconfig

This commit is contained in:
Emmanuel Garette 2013-09-30 16:22:08 +02:00
parent 6b7db20716
commit feeb9842f5
4 changed files with 227 additions and 105 deletions

View File

@ -2,8 +2,8 @@
import autopath
#from py.test import raises
from tiramisu.config import Config
from tiramisu.option import BoolOption, OptionDescription
from tiramisu.config import Config, GroupConfig, MetaConfig
from tiramisu.option import BoolOption, IntOption, OptionDescription
import weakref
@ -109,3 +109,31 @@ def test_deref_optiondescription_config():
assert w() is not None
del(c)
assert w() is None
def test_deref_groupconfig():
i1 = IntOption('i1', '')
od1 = OptionDescription('od1', '', [i1])
od2 = OptionDescription('od2', '', [od1])
conf1 = Config(od2)
conf2 = Config(od2)
meta = GroupConfig([conf1, conf2])
w = weakref.ref(conf1)
del(conf1)
assert w() is not None
del(meta)
assert w() is None
def test_deref_metaconfig():
i1 = IntOption('i1', '')
od1 = OptionDescription('od1', '', [i1])
od2 = OptionDescription('od2', '', [od1])
conf1 = Config(od2)
conf2 = Config(od2)
meta = MetaConfig([conf1, conf2])
w = weakref.ref(conf1)
del(conf1)
assert w() is not None
del(meta)
assert w() is None

View File

@ -3,7 +3,7 @@ import autopath
from py.test import raises
from tiramisu.setting import owners
from tiramisu.config import Config, MetaConfig
from tiramisu.config import Config, GroupConfig, MetaConfig
from tiramisu.option import IntOption, OptionDescription
from tiramisu.error import ConfigError
@ -26,6 +26,7 @@ def make_description():
#FIXME ne pas mettre 2 meta dans une config
#FIXME ne pas mettre 2 OD differents dans un meta
#FIXME serialization
def test_none():
meta = make_description()
conf1, conf2 = meta._impl_children
@ -89,7 +90,7 @@ def test_contexts():
conf1, conf2 = meta._impl_children
assert conf1.od1.i2 == conf2.od1.i2 == 1
assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
meta.set_contexts('od1.i2', 6)
meta.setattrs('od1.i2', 6)
assert meta.od1.i2 == 1
assert conf1.od1.i2 == conf2.od1.i2 == 6
assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.user
@ -142,14 +143,14 @@ def test_meta_meta_set():
meta2 = MetaConfig([meta1])
meta2.cfgimpl_get_settings().setowner(owners.meta)
conf1, conf2 = meta1._impl_children
meta2.set_contexts('od1.i1', 7)
meta2.setattrs('od1.i1', 7)
assert conf1.od1.i1 == conf2.od1.i1 == 7
assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user
assert [conf1, conf2] == meta2.find_first_contexts(byname='i1', byvalue=7)
assert [conf1, conf2] == meta2.find_firsts(byname='i1', byvalue=7)
conf1.od1.i1 = 8
assert [conf2] == meta2.find_first_contexts(byname='i1', byvalue=7)
assert [conf1] == meta2.find_first_contexts(byname='i1', byvalue=8)
raises(AttributeError, "meta2.find_first_contexts(byname='i1', byvalue=10)")
assert [conf2] == meta2.find_firsts(byname='i1', byvalue=7)
assert [conf1] == meta2.find_firsts(byname='i1', byvalue=8)
raises(AttributeError, "meta2.find_firsts(byname='i1', byvalue=10)")
def test_not_meta():
@ -158,10 +159,10 @@ def test_not_meta():
od2 = OptionDescription('od2', '', [od1])
conf1 = Config(od2)
conf2 = Config(od2)
meta = MetaConfig([conf1, conf2], False)
meta = GroupConfig([conf1, conf2])
raises(ConfigError, 'meta.od1.i1')
conf1, conf2 = meta._impl_children
meta.set_contexts('od1.i1', 7)
meta.setattrs('od1.i1', 7)
assert conf1.od1.i1 == conf2.od1.i1 == 7
assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user

View File

@ -1,6 +1,6 @@
from tiramisu.option import BoolOption, UnicodeOption, SymLinkOption, \
OptionDescription
from tiramisu.config import Config
IntOption, OptionDescription
from tiramisu.config import Config, GroupConfig, MetaConfig
from tiramisu.setting import owners
from tiramisu.storage import delete_session
from tiramisu.error import ConfigError
@ -90,6 +90,45 @@ def _diff_opt(opt1, opt2):
assert val1 == val2
def _diff_conf(cfg1, cfg2):
attr1 = set(_get_slots(cfg1))
attr2 = set(_get_slots(cfg2))
diff1 = attr1 - attr2
diff2 = attr2 - attr1
if diff1 != set():
raise Exception('more attribute in cfg1 {0}'.format(list(diff1)))
if diff2 != set():
raise Exception('more attribute in cfg2 {0}'.format(list(diff2)))
for attr in attr1:
if attr in ('_impl_context', '__weakref__'):
continue
err1 = False
err2 = False
val1 = None
val2 = None
try:
val1 = getattr(cfg1, attr)
except:
err1 = True
try:
val2 = getattr(cfg2, attr)
except:
err2 = True
assert err1 == err2
if val1 is None:
assert val1 == val2
elif attr == '_impl_values':
assert cfg1.cfgimpl_get_values().get_modified_values() == cfg2.cfgimpl_get_values().get_modified_values()
elif attr == '_impl_settings':
assert cfg1.cfgimpl_get_settings().get_modified_properties() == cfg2.cfgimpl_get_settings().get_modified_properties()
assert cfg1.cfgimpl_get_settings().get_modified_permissives() == cfg2.cfgimpl_get_settings().get_modified_permissives()
elif attr == '_impl_descr':
_diff_opt(cfg1.cfgimpl_get_description(), cfg2.cfgimpl_get_description())
else:
assert val1 == val2
def test_diff_opt():
b = BoolOption('b', '')
u = UnicodeOption('u', '', requires=[{'option': b, 'expected': True, 'action': 'disabled', 'inverse': True}])
@ -169,10 +208,7 @@ def test_state_config():
cfg._impl_test = True
a = dumps(cfg)
q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description())
assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values()
assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties()
assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives()
_diff_conf(cfg, q)
try:
delete_session('29090931')
except ConfigError:
@ -191,12 +227,9 @@ def test_state_properties():
cfg.cfgimpl_get_settings()[val1].append('test')
a = dumps(cfg)
q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description())
assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values()
assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties()
assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives()
_diff_conf(cfg, q)
try:
delete_session('29090931')
delete_session('29090932')
except ConfigError:
pass
@ -212,15 +245,12 @@ def test_state_values():
cfg.val1 = True
a = dumps(cfg)
q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description())
assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values()
assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties()
assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives()
_diff_conf(cfg, q)
q.val1 = False
#assert cfg.val1 is True
assert q.val1 is False
try:
delete_session('29090931')
delete_session('29090933')
except ConfigError:
pass
@ -238,14 +268,53 @@ def test_state_values_owner():
cfg.val1 = True
a = dumps(cfg)
q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description())
assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values()
assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties()
assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives()
_diff_conf(cfg, q)
q.val1 = False
nval1 = q.cfgimpl_get_description().val1
assert q.getowner(nval1) == owners.newowner
try:
delete_session('29090931')
delete_session('29090934')
except ConfigError:
pass
def test_state_metaconfig():
i1 = IntOption('i1', '')
od1 = OptionDescription('od1', '', [i1])
od2 = OptionDescription('od2', '', [od1])
conf1 = Config(od2, session_id='29090935')
conf1._impl_test = True
conf2 = Config(od2, session_id='29090936')
conf2._impl_test = True
meta = MetaConfig([conf1, conf2], session_id='29090937')
meta._impl_test = True
a = dumps(meta)
q = loads(a)
_diff_conf(meta, q)
try:
delete_session('29090935')
delete_session('29090936')
delete_session('29090937')
except ConfigError:
pass
def test_state_groupconfig():
i1 = IntOption('i1', '')
od1 = OptionDescription('od1', '', [i1])
od2 = OptionDescription('od2', '', [od1])
conf1 = Config(od2, session_id='29090935')
conf1._impl_test = True
conf2 = Config(od2, session_id='29090936')
conf2._impl_test = True
meta = GroupConfig([conf1, conf2], session_id='29090937')
meta._impl_test = True
a = dumps(meta)
q = loads(a)
_diff_conf(meta, q)
try:
delete_session('29090935')
delete_session('29090936')
delete_session('29090937')
except ConfigError:
pass

View File

@ -161,7 +161,7 @@ class SubConfig(object):
def cfgimpl_get_description(self):
if self._impl_descr is None:
raise ConfigError(_('no option description found for this config'
' (may be metaconfig without meta)'))
' (may be GroupConfig)'))
else:
return self._impl_descr
@ -467,9 +467,9 @@ class SubConfig(object):
return context_descr.impl_get_path_by_opt(descr)
class CommonConfig(SubConfig):
"abstract base class for the Config and the MetaConfig"
__slots__ = ('_impl_values', '_impl_settings', '_impl_meta')
class _CommonConfig(SubConfig):
"abstract base class for the Config, GroupConfig and the MetaConfig"
__slots__ = ('_impl_values', '_impl_settings', '_impl_meta', '_impl_test')
def _impl_build_all_paths(self):
self.cfgimpl_get_description().impl_build_cache()
@ -508,7 +508,8 @@ class CommonConfig(SubConfig):
return None
def cfgimpl_get_meta(self):
return self._impl_meta
if self._impl_meta is not None:
return self._impl_meta()
# information
def impl_set_information(self, key, value):
@ -526,37 +527,12 @@ class CommonConfig(SubConfig):
"""
return self._impl_values.get_information(key, default)
# ____________________________________________________________
class Config(CommonConfig):
"main configuration management entry"
__slots__ = ('__weakref__', '_impl_test')
def __init__(self, descr, session_id=None, persistent=False):
""" Configuration option management master class
:param descr: describes the configuration schema
:type descr: an instance of ``option.OptionDescription``
:param context: the current root config
:type context: `Config`
:param session_id: session ID is import with persistent Config to
retrieve good session
:type session_id: `str`
:param persistent: if persistent, don't delete storage when leaving
:type persistent: `boolean`
"""
settings, values = get_storages(self, session_id, persistent)
self._impl_settings = Settings(self, settings)
self._impl_values = Values(self, values)
super(Config, self).__init__(descr, weakref.ref(self))
self._impl_build_all_paths()
self._impl_meta = None
#undocumented option used only in test script
self._impl_test = False
# ----- state
def __getstate__(self):
if self._impl_meta is not None:
raise ConfigError('cannot serialize Config with meta')
#FIXME _impl_meta est un weakref => faut pas sauvegarder mais faut bien savoir si c'est un méta ou pas au final ...
#en fait il faut ne pouvoir sérialisé que depuis une MetaConfig ... et pas directement comme pour les options
raise ConfigError('cannot serialize Config with MetaConfig')
slots = set()
for subclass in self.__class__.__mro__:
if subclass is not object:
@ -589,6 +565,34 @@ class Config(CommonConfig):
self._impl_values._impl_setstate(storage)
self._impl_settings._impl_setstate(storage)
# ____________________________________________________________
class Config(_CommonConfig):
"main configuration management entry"
__slots__ = ('__weakref__',)
def __init__(self, descr, session_id=None, persistent=False):
""" Configuration option management master class
:param descr: describes the configuration schema
:type descr: an instance of ``option.OptionDescription``
:param context: the current root config
:type context: `Config`
:param session_id: session ID is import with persistent Config to
retrieve good session
:type session_id: `str`
:param persistent: if persistent, don't delete storage when leaving
:type persistent: `boolean`
"""
settings, values = get_storages(self, session_id, persistent)
self._impl_settings = Settings(self, settings)
self._impl_values = Values(self, values)
super(Config, self).__init__(descr, weakref.ref(self))
self._impl_build_all_paths()
self._impl_meta = None
#undocumented option used only in test script
self._impl_test = False
def cfgimpl_reset_cache(self,
only_expired=False,
only=('values', 'settings')):
@ -598,42 +602,29 @@ class Config(CommonConfig):
self.cfgimpl_get_settings().reset_cache(only_expired=only_expired)
class MetaConfig(CommonConfig):
class GroupConfig(_CommonConfig):
__slots__ = ('_impl_children', '__weakref__')
def __init__(self, children, meta=True, session_id=None, persistent=False):
def __init__(self, children, session_id=None, persistent=False,
_descr=None):
if not isinstance(children, list):
raise ValueError(_("metaconfig's children must be a list"))
descr = None
if meta:
for child in children:
if not isinstance(child, CommonConfig):
raise TypeError(_("metaconfig's children "
"must be config, not {0}"
).format(type(child)))
if descr is None:
descr = child.cfgimpl_get_description()
elif not descr is child.cfgimpl_get_description():
raise ValueError(_('all config in metaconfig must '
'have the same optiondescription'))
if child.cfgimpl_get_meta() is not None:
raise ValueError(_("child has already a metaconfig's"))
child._impl_meta = self
self._impl_children = children
settings, values = get_storages(self, session_id, persistent)
self._impl_settings = Settings(self, settings)
self._impl_values = Values(self, values)
super(MetaConfig, self).__init__(descr, weakref.ref(self))
super(GroupConfig, self).__init__(_descr, weakref.ref(self))
self._impl_meta = None
#undocumented option used only in test script
self._impl_test = False
def cfgimpl_get_children(self):
return self._impl_children
def cfgimpl_get_context(self):
"a meta config is a config wich has a setting, that is itself"
return self
#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')):
@ -644,38 +635,49 @@ class MetaConfig(CommonConfig):
for child in self._impl_children:
child.cfgimpl_reset_cache(only_expired=only_expired, only=only)
def set_contexts(self, path, value):
def setattrs(self, path, value):
"""Setattr not in current GroupConfig, but in each children
"""
for child in self._impl_children:
try:
if not isinstance(child, MetaConfig):
if not isinstance(child, GroupConfig):
setattr(child, path, value)
else:
child.set_contexts(path, value)
child.setattrs(path, value)
except PropertiesOptionError:
pass
def find_first_contexts(self, byname=None, bypath=None, byvalue=None,
type_='path', display_error=True):
def find_firsts(self, byname=None, bypath=None, byvalue=None,
type_='path', display_error=True):
"""Find first not in current GroupConfig, but in each children
"""
ret = []
#if MetaConfig, all children have same OptionDescription as context
#so search only one time for all children
try:
if bypath is None and byname is not None and \
self.cfgimpl_get_description() is not None:
isinstance(self, MetaConfig):
bypath = self._find(bytype=None, byvalue=None, byname=byname,
first=True, type_='path',
check_properties=False,
display_error=display_error)
except ConfigError:
byname = None
except AttributeError:
pass
for child in self._impl_children:
try:
if not isinstance(child, MetaConfig):
if bypath is not None:
#if byvalue is None, try if not raise
value = getattr(child, bypath)
if byvalue is not None:
if getattr(child, bypath) == byvalue:
ret.append(child)
if isinstance(value, Multi):
if byvalue in value:
ret.append(child)
else:
if value == byvalue:
ret.append(child)
else:
#not raise
getattr(child, bypath)
ret.append(child)
else:
ret.append(child.find_first(byname=byname,
@ -683,16 +685,38 @@ class MetaConfig(CommonConfig):
type_=type_,
display_error=False))
else:
ret.extend(child.find_first_contexts(byname=byname,
bypath=bypath,
byvalue=byvalue,
type_=type_,
display_error=False))
ret.extend(child.find_firsts(byname=byname,
bypath=bypath,
byvalue=byvalue,
type_=type_,
display_error=False))
except AttributeError:
pass
return self._find_return_results(ret, display_error)
class MetaConfig(GroupConfig):
__slots__ = tuple()
def __init__(self, children, session_id=None, persistent=False):
descr = None
for child in children:
if not isinstance(child, _CommonConfig):
raise TypeError(_("metaconfig's children "
"should be config, not {0}"
).format(type(child)))
if child.cfgimpl_get_meta() is not None:
raise ValueError(_("child has already a metaconfig's"))
if descr is None:
descr = child.cfgimpl_get_description()
elif not descr is child.cfgimpl_get_description():
raise ValueError(_('all config in metaconfig must '
'have the same optiondescription'))
child._impl_meta = weakref.ref(self)
super(MetaConfig, self).__init__(children, session_id, persistent, descr)
def mandatory_warnings(config):
"""convenience function to trace Options that are mandatory and
where no value has been set