works on sqlalchemy storage

This commit is contained in:
Emmanuel Garette 2014-11-10 09:13:44 +01:00
parent c75867720f
commit 4217508f3f
12 changed files with 446 additions and 166 deletions

View file

@ -7,6 +7,8 @@ from tiramisu.option import BoolOption, IntOption, StrOption, OptionDescription,
import weakref import weakref
IS_DEREFABLE = True
def test_deref_storage(): def test_deref_storage():
b = BoolOption('b', '') b = BoolOption('b', '')
o = OptionDescription('od', '', [b]) o = OptionDescription('od', '', [b])
@ -44,16 +46,23 @@ def test_deref_config():
def test_deref_option(): def test_deref_option():
global IS_DEREFABLE
b = BoolOption('b', '') b = BoolOption('b', '')
o = OptionDescription('od', '', [b]) o = OptionDescription('od', '', [b])
w = weakref.ref(b) w = weakref.ref(b)
del(b) del(b)
assert w() is not None try:
assert w() is not None
except AssertionError:
IS_DEREFABLE = False
return
del(o) del(o)
assert w() is None assert w() is None
def test_deref_optiondescription(): def test_deref_optiondescription():
if not IS_DEREFABLE:
return
b = BoolOption('b', '') b = BoolOption('b', '')
o = OptionDescription('od', '', [b]) o = OptionDescription('od', '', [b])
w = weakref.ref(o) w = weakref.ref(o)
@ -64,6 +73,8 @@ def test_deref_optiondescription():
def test_deref_option_cache(): def test_deref_option_cache():
if not IS_DEREFABLE:
return
b = BoolOption('b', '') b = BoolOption('b', '')
o = OptionDescription('od', '', [b]) o = OptionDescription('od', '', [b])
o.impl_build_cache_option() o.impl_build_cache_option()
@ -86,6 +97,8 @@ def test_deref_optiondescription_cache():
def test_deref_option_config(): def test_deref_option_config():
if not IS_DEREFABLE:
return
b = BoolOption('b', '') b = BoolOption('b', '')
o = OptionDescription('od', '', [b]) o = OptionDescription('od', '', [b])
c = Config(o) c = Config(o)

View file

@ -27,7 +27,7 @@ def return_dynval(suffix, value='val'):
def return_list2(suffix): def return_list2(suffix):
return [suffix, 'val2'] return [str(suffix), 'val2']
def return_list(val=None): def return_list(val=None):

View file

@ -17,6 +17,7 @@ def test_symlink_option():
descr = OptionDescription("opt", "", descr = OptionDescription("opt", "",
[linkopt, OptionDescription("s1", "", [boolopt])]) [linkopt, OptionDescription("s1", "", [boolopt])])
config = Config(descr) config = Config(descr)
assert config.s1.b is False
setattr(config, "s1.b", True) setattr(config, "s1.b", True)
setattr(config, "s1.b", False) setattr(config, "s1.b", False)
assert config.s1.b is False assert config.s1.b is False

View file

@ -211,7 +211,7 @@ class SubConfig(object):
elif isinstance(child, SymLinkOption) and \ elif isinstance(child, SymLinkOption) and \
not isinstance(child, DynSymLinkOption): # pragma: no dynoptiondescription cover not isinstance(child, DynSymLinkOption): # pragma: no dynoptiondescription cover
path = context.cfgimpl_get_description().impl_get_path_by_opt( path = context.cfgimpl_get_description().impl_get_path_by_opt(
child._opt) child._impl_getopt())
context._setattr(path, value, force_permissive=force_permissive) context._setattr(path, value, force_permissive=force_permissive)
else: else:
subpath = self._get_subpath(name) subpath = self._get_subpath(name)
@ -253,7 +253,8 @@ class SubConfig(object):
return homeconfig.getattr(name, force_permissive=force_permissive, return homeconfig.getattr(name, force_permissive=force_permissive,
validate=validate) validate=validate)
context = self._cfgimpl_get_context() context = self._cfgimpl_get_context()
opt_or_descr = self.cfgimpl_get_description().__getattr__(name, context=context) opt_or_descr = self.cfgimpl_get_description().__getattr__(
name, context=context)
subpath = self._get_subpath(name) subpath = self._get_subpath(name)
if isinstance(opt_or_descr, DynSymLinkOption): if isinstance(opt_or_descr, DynSymLinkOption):
return self.cfgimpl_get_values()._get_cached_item( return self.cfgimpl_get_values()._get_cached_item(
@ -262,7 +263,7 @@ class SubConfig(object):
force_permissive=force_permissive) force_permissive=force_permissive)
elif isinstance(opt_or_descr, SymLinkOption): # pragma: no dynoptiondescription cover elif isinstance(opt_or_descr, SymLinkOption): # pragma: no dynoptiondescription cover
path = context.cfgimpl_get_description().impl_get_path_by_opt( path = context.cfgimpl_get_description().impl_get_path_by_opt(
opt_or_descr._opt) opt_or_descr._impl_getopt())
return context.getattr(path, validate=validate, return context.getattr(path, validate=validate,
force_permissive=force_permissive) force_permissive=force_permissive)
elif opt_or_descr.impl_is_optiondescription(): elif opt_or_descr.impl_is_optiondescription():
@ -319,6 +320,7 @@ class SubConfig(object):
:param first: return only one option if True, a list otherwise :param first: return only one option if True, a list otherwise
:return: find list or an exception if nothing has been found :return: find list or an exception if nothing has been found
""" """
def _filter_by_value(): def _filter_by_value():
if byvalue is undefined: if byvalue is undefined:
return True return True
@ -478,7 +480,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._opt) return context_descr.impl_get_path_by_opt(descr._impl_getopt())
return self._impl_path return self._impl_path
@ -489,8 +491,8 @@ class _CommonConfig(SubConfig):
def _impl_build_all_caches(self): def _impl_build_all_caches(self):
if not self.cfgimpl_get_description().impl_already_build_caches(): if not self.cfgimpl_get_description().impl_already_build_caches():
self.cfgimpl_get_description().impl_build_cache_consistency() self.cfgimpl_get_description().impl_build_cache_consistency()
self.cfgimpl_get_description().impl_validate_options()
self.cfgimpl_get_description().impl_build_cache_option() self.cfgimpl_get_description().impl_build_cache_option()
self.cfgimpl_get_description().impl_validate_options()
def read_only(self): def read_only(self):
"read only is a global config's setting, see `settings.py`" "read only is a global config's setting, see `settings.py`"
@ -504,7 +506,9 @@ class _CommonConfig(SubConfig):
"""convenience method to retrieve an option's owner """convenience method to retrieve an option's owner
from the config itself from the config itself
""" """
if not isinstance(opt, Option) and not isinstance(opt, SymLinkOption): # pragma: optional cover if not isinstance(opt, Option) and \
not isinstance(opt, SymLinkOption) and \
not isinstance(opt, DynSymLinkOption): # pragma: optional cover
raise TypeError(_('opt in getowner must be an option not {0}' raise TypeError(_('opt in getowner must be an option not {0}'
'').format(type(opt))) '').format(type(opt)))
return self.cfgimpl_get_values().getowner(opt, return self.cfgimpl_get_values().getowner(opt,

View file

@ -103,11 +103,11 @@ class Base(StorageBase):
if not valid_name(name): # pragma: optional cover if not valid_name(name): # pragma: optional cover
raise ValueError(_("invalid name: {0} for option").format(name)) raise ValueError(_("invalid name: {0} for option").format(name))
if requires is not None: if requires is not None:
self._calc_properties, self._requires = validate_requires_arg( calc_properties, requires = validate_requires_arg(
requires, name) requires, name)
#else: else:
# self._calc_properties = frozenset() calc_properties = frozenset()
# self._requires = [] requires = undefined
if not multi and default_multi is not None: # pragma: optional cover if not multi and default_multi is not None: # pragma: optional cover
raise ValueError(_("a default_multi is set whereas multi is False" raise ValueError(_("a default_multi is set whereas multi is False"
" in option: {0}").format(name)) " in option: {0}").format(name))
@ -127,17 +127,18 @@ class Base(StorageBase):
if validator is not None: if validator is not None:
validate_callback(validator, validator_params, 'validator') validate_callback(validator, validator_params, 'validator')
self._set_validator(validator, validator_params) self._set_validator(validator, validator_params)
if self.impl_get_calc_properties() != frozenset([]) and properties is not tuple(): # pragma: optional cover if calc_properties != frozenset([]) and properties is not tuple(): # pragma: optional cover
set_forbidden_properties = self.impl_get_calc_properties() & set(properties) set_forbidden_properties = calc_properties & set(properties)
if set_forbidden_properties != frozenset(): if set_forbidden_properties != frozenset():
raise ValueError('conflict: properties already set in ' raise ValueError('conflict: properties already set in '
'requirement {0}'.format( 'requirement {0}'.format(
list(set_forbidden_properties))) list(set_forbidden_properties)))
super(Base, self).__init__(name, _multi, warnings_only, doc, extra) StorageBase.__init__(self, name, _multi, warnings_only, doc, extra,
calc_properties, requires, properties)
self._set_default_values(default, default_multi) self._set_default_values(default, default_multi)
if callback is not False: if callback is not False:
self.impl_set_callback(callback, callback_params) self.impl_set_callback(callback, callback_params)
self._properties = properties self.commit()
def impl_set_callback(self, callback, callback_params=None): def impl_set_callback(self, callback, callback_params=None):
if callback is None and callback_params is not None: # pragma: optional cover if callback is None and callback_params is not None: # pragma: optional cover
@ -275,12 +276,12 @@ class BaseOption(Base):
except AttributeError: # pragma: optional cover except AttributeError: # pragma: optional cover
raise SystemError(_('cannot serialize Option, ' raise SystemError(_('cannot serialize Option, '
'only in OptionDescription')) 'only in OptionDescription'))
slots = set() if isinstance(self, SymLinkOption):
for subclass in self.__class__.__mro__: slots = frozenset(['_name', '_state_opt', '_stated'])
if subclass is not object: else:
slots.update(subclass.__slots__) slots = self._impl_getattributes()
slots -= frozenset(['_cache_paths', '_cache_consistencies', slots -= frozenset(['_cache_paths', '_cache_consistencies',
'__weakref__']) '__weakref__'])
states = {} states = {}
for slot in slots: for slot in slots:
# remove variable if save variable converted # remove variable if save variable converted
@ -338,9 +339,10 @@ class BaseOption(Base):
""" """
if name not in ('_option', '_is_build_cache') \ if name not in ('_option', '_is_build_cache') \
and not isinstance(value, tuple) and \ and not isinstance(value, tuple) and \
not name.startswith('_state'): not name.startswith('_state') and \
not name == '_sa_instance_state':
is_readonly = False is_readonly = False
# never change _name # never change _name dans _opt
if name == '_name': if name == '_name':
try: try:
if self.impl_getname() is not None: if self.impl_getname() is not None:
@ -348,13 +350,21 @@ class BaseOption(Base):
is_readonly = True is_readonly = True
except (KeyError, AttributeError): except (KeyError, AttributeError):
pass pass
elif name == '_opt':
try:
if self._impl_getopt() is not None:
#so _opt is already set
is_readonly = True
except (KeyError, AttributeError):
pass
elif name != '_readonly': elif name != '_readonly':
is_readonly = self.impl_is_readonly() is_readonly = self.impl_is_readonly()
if is_readonly: # pragma: optional cover if is_readonly: # pragma: optional cover
raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is" raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
" read-only").format( " read-only").format(
self.__class__.__name__, self.__class__.__name__,
self.impl_getname(), self,
#self.impl_getname(),
name)) name))
super(BaseOption, self).__setattr__(name, value) super(BaseOption, self).__setattr__(name, value)
@ -844,29 +854,31 @@ def validate_requires_arg(requires, name):
class SymLinkOption(OnlyOption): class SymLinkOption(OnlyOption):
__slots__ = ('_opt', '_state_opt', '_readonly') # __slots__ = ('_opt', '_state_opt')
def __init__(self, name, opt): def __init__(self, name, opt):
if not isinstance(opt, Option): # pragma: optional cover if not isinstance(opt, Option): # pragma: optional cover
raise ValueError(_('malformed symlinkoption ' raise ValueError(_('malformed symlinkoption '
'must be an option ' 'must be an option '
'for symlink {0}').format(name)) 'for symlink {0}').format(name))
self._opt = opt super(Base, self).__init__(name, undefined, undefined, undefined,
self._set_readonly() undefined, undefined, undefined, undefined,
super(Base, self).__init__(name, undefined, undefined, undefined, undefined) opt)
self.commit()
def __getattr__(self, name, context=undefined): def __getattr__(self, name, context=undefined):
if name in ('_opt', '_opt_type', '_readonly', 'impl_getpath', '_name', '_state_opt'): if name in ('_opt', '_opt_type', '_readonly', 'impl_getpath', '_name',
'_state_opt', '_impl_setopt'):
return object.__getattr__(self, name) return object.__getattr__(self, name)
else: else:
return getattr(self._opt, name) return getattr(self._impl_getopt(), name)
def _impl_getstate(self, descr): def _impl_getstate(self, descr):
self._stated = True self._stated = True
self._state_opt = descr.impl_get_path_by_opt(self._opt) self._state_opt = descr.impl_get_path_by_opt(self._impl_getopt())
def _impl_setstate(self, descr): def _impl_setstate(self, descr):
self._opt = descr.impl_get_opt_by_path(self._state_opt) self._impl_setopt(descr.impl_get_opt_by_path(self._state_opt))
del(self._state_opt) del(self._state_opt)
try: try:
del(self._stated) del(self._stated)
@ -875,46 +887,56 @@ class SymLinkOption(OnlyOption):
self._set_readonly() self._set_readonly()
def impl_get_information(self, key, default=undefined): def impl_get_information(self, key, default=undefined):
return self._opt.impl_get_information(key, default) return self._impl_getopt().impl_get_information(key, default)
def _set_readonly(self):
self._readonly = True
def impl_is_readonly(self): def impl_is_readonly(self):
try: return True
return self._readonly
except AttributeError:
return False
def impl_getproperties(self): def impl_getproperties(self):
return self._opt._properties return self._impl_getopt()._properties
def impl_get_callback(self): def impl_get_callback(self):
return self._opt.impl_get_callback() return self._impl_getopt().impl_get_callback()
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"
return self._opt.impl_has_callback() return self._impl_getopt().impl_has_callback()
def impl_is_multi(self):
return self._impl_getopt().impl_is_multi()
def _is_subdyn(self): def _is_subdyn(self):
try: try:
return self._opt._subdyn is not None return self._impl_getopt()._subdyn is not None
except AttributeError: except AttributeError:
return False return False
class DynSymLinkOption(SymLinkOption): class DynSymLinkOption(object):
__slots__ = ('_dyn',) __slots__ = ('_dyn', '_opt', '_name')
def __init__(self, name, opt, dyn): def __init__(self, name, opt, dyn):
self._name = name
self._dyn = dyn self._dyn = dyn
super(DynSymLinkOption, self).__init__(name, opt) self._opt = opt
def __getattr__(self, name, context=undefined):
if name in ('_opt', '_opt_type', '_readonly', 'impl_getpath', '_name', '_state_opt'):
return object.__getattr__(self, name)
else:
return getattr(self._impl_getopt(), name)
def impl_getname(self):
return self._name
def _impl_getopt(self):
return self._opt
def impl_getsuffix(self): def impl_getsuffix(self):
return self._dyn.split('.')[-1][len(self._opt.impl_getname()):] return self._dyn.split('.')[-1][len(self._impl_getopt().impl_getname()):]
def impl_getpath(self, context): def impl_getpath(self, context):
path = self._opt.impl_getpath(context) path = self._impl_getopt().impl_getpath(context)
base_path = '.'.join(path.split('.')[:-2]) base_path = '.'.join(path.split('.')[:-2])
if self.impl_is_master_slaves() and base_path is not '': if self.impl_is_master_slaves() and base_path is not '':
base_path = base_path + self.impl_getsuffix() base_path = base_path + self.impl_getsuffix()
@ -925,5 +947,7 @@ class DynSymLinkOption(SymLinkOption):
def impl_validate(self, value, context=undefined, validate=True, def impl_validate(self, value, context=undefined, validate=True,
force_index=None, force_submulti_index=None): force_index=None, force_submulti_index=None):
return self._opt.impl_validate(value, context, validate, force_index, return self._impl_getopt().impl_validate(value, context, validate,
force_submulti_index, current_opt=self) force_index,
force_submulti_index,
current_opt=self)

View file

@ -168,7 +168,7 @@ class OptionDescription(BaseOption, StorageOptionDescription):
return True return True
#consistencies is something like [('_cons_not_equal', (opt1, opt2))] #consistencies is something like [('_cons_not_equal', (opt1, opt2))]
if isinstance(option, DynSymLinkOption): if isinstance(option, DynSymLinkOption):
consistencies = self._cache_consistencies.get(option._opt) consistencies = self._cache_consistencies.get(option._impl_getopt())
else: else:
consistencies = self._cache_consistencies.get(option) consistencies = self._cache_consistencies.get(option)
if consistencies is not None: if consistencies is not None:
@ -177,7 +177,7 @@ class OptionDescription(BaseOption, StorageOptionDescription):
#all_cons_opts[0] is the option where func is set #all_cons_opts[0] is the option where func is set
if isinstance(option, DynSymLinkOption): if isinstance(option, DynSymLinkOption):
subpath = '.'.join(option._dyn.split('.')[:-1]) subpath = '.'.join(option._dyn.split('.')[:-1])
namelen = len(option._opt.impl_getname()) namelen = len(option._impl_getopt().impl_getname())
suffix = option.impl_getname()[namelen:] suffix = option.impl_getname()[namelen:]
opts = [] opts = []
for opt in all_cons_opts: for opt in all_cons_opts:
@ -361,6 +361,9 @@ class SynDynOptionDescription(object):
def impl_getpaths(self, include_groups=False, _currpath=None): def impl_getpaths(self, include_groups=False, _currpath=None):
return _impl_getpaths(self, include_groups, _currpath) return _impl_getpaths(self, include_groups, _currpath)
def _impl_getopt(self):
return self._opt
def _impl_getpaths(klass, include_groups, _currpath): def _impl_getpaths(klass, include_groups, _currpath):
"""returns a list of all paths in klass, recursively """returns a list of all paths in klass, recursively

View file

@ -61,7 +61,8 @@ class StorageBase(object):
'__weakref__' '__weakref__'
) )
def __init__(self, name, multi, warnings_only, doc, extra): def __init__(self, name, multi, warnings_only, doc, extra, calc_properties,
requires, properties, opt=undefined):
self._name = name self._name = name
if doc is not undefined: if doc is not undefined:
self._informations = {'doc': doc} self._informations = {'doc': doc}
@ -72,6 +73,15 @@ class StorageBase(object):
if warnings_only is True: if warnings_only is True:
self._warnings_only = warnings_only self._warnings_only = warnings_only
if calc_properties is not undefined:
self._calc_properties = calc_properties
if requires is not undefined:
self._requires = requires
if properties is not undefined:
self._properties = properties
if opt is not undefined:
self._opt = opt
def _set_default_values(self, default, default_multi): def _set_default_values(self, default, default_multi):
if self.impl_is_multi() and default is None: if self.impl_is_multi() and default is None:
default = [] default = []
@ -214,6 +224,9 @@ class StorageBase(object):
def _impl_getsubdyn(self): def _impl_getsubdyn(self):
return self._subdyn return self._subdyn
def _impl_getopt(self):
return self._opt
def _set_readonly(self): def _set_readonly(self):
if not self.impl_is_readonly(): if not self.impl_is_readonly():
dico = self._informations dico = self._informations
@ -233,6 +246,9 @@ class StorageBase(object):
def _impl_setsubdyn(self, subdyn): def _impl_setsubdyn(self, subdyn):
self._subdyn = subdyn self._subdyn = subdyn
def _impl_setopt(self, opt):
self._opt = opt
def _impl_convert_informations(self, descr, load=False): def _impl_convert_informations(self, descr, load=False):
if not load: if not load:
infos = self._informations infos = self._informations
@ -255,6 +271,13 @@ class StorageBase(object):
self._set_readonly() self._set_readonly()
del(self._state_readonly) del(self._state_readonly)
def _impl_getattributes(self):
slots = set()
for subclass in self.__class__.__mro__:
if subclass is not object:
slots.update(subclass.__slots__)
return slots
def impl_is_readonly(self): def impl_is_readonly(self):
try: try:
return not isinstance(self._informations, dict) return not isinstance(self._informations, dict)
@ -320,7 +343,10 @@ class StorageOptionDescription(StorageBase):
'_group_type', '_is_build_cache', '_state_group_type') '_group_type', '_is_build_cache', '_state_group_type')
def __init__(self, name, multi, warnings_only, doc, extra): def __init__(self, name, multi, warnings_only, doc, extra):
super(StorageOptionDescription, self).__init__(name, multi, warnings_only, doc, None) super(StorageOptionDescription, self).__init__(name, multi,
warnings_only, doc,
None, undefined,
undefined, undefined)
self._cache_paths = None self._cache_paths = None
def _add_children(self, child_names, children): def _add_children(self, child_names, children):

View file

@ -23,7 +23,7 @@ from tiramisu.error import ConfigError
from .util import SqlAlchemyBase from .util import SqlAlchemyBase
import util import util
from sqlalchemy import not_, or_ from sqlalchemy import not_, or_, and_, inspect
from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy import Column, Integer, String, Boolean, PickleType, \ from sqlalchemy import Column, Integer, String, Boolean, PickleType, \
@ -199,6 +199,7 @@ class _CallbackParam(SqlAlchemyBase):
# #
# consistency # consistency
consistency_table = Table('consistencyopt', SqlAlchemyBase.metadata, consistency_table = Table('consistencyopt', SqlAlchemyBase.metadata,
Column('id', Integer, primary_key=True),
Column('left_id', Integer, ForeignKey('consistency.id')), Column('left_id', Integer, ForeignKey('consistency.id')),
Column('right_id', Integer, ForeignKey('baseoption.id')) Column('right_id', Integer, ForeignKey('baseoption.id'))
) )
@ -214,6 +215,7 @@ class _Consistency(SqlAlchemyBase):
self.func = func self.func = func
for option in all_cons_opts: for option in all_cons_opts:
option._consistencies.append(self) option._consistencies.append(self)
print type(option._consistencies)
self.params = params self.params = params
@ -245,6 +247,8 @@ class _Base(SqlAlchemyBase):
_default = Column(PickleType) _default = Column(PickleType)
_default_multi = Column(PickleType) _default_multi = Column(PickleType)
_subdyn = Column(Integer) _subdyn = Column(Integer)
_dyn = Column(String)
_opt = Column(Integer)
_choice_values = Column(PickleType) _choice_values = Column(PickleType)
_cho_params = relationship('_CallbackParam', _cho_params = relationship('_CallbackParam',
collection_class= collection_class=
@ -268,19 +272,18 @@ class _Base(SqlAlchemyBase):
_validator_params = association_proxy("_val_params", "params", _validator_params = association_proxy("_val_params", "params",
getset_factory=load_callback_parm) getset_factory=load_callback_parm)
###### ######
#FIXME pas 2 fois la meme properties dans la base ...
#FIXME not autoload #FIXME not autoload
#FIXME normalement tuple ... transforme en set !
_props = relationship("_PropertyOption", collection_class=set) _props = relationship("_PropertyOption", collection_class=set)
_properties = association_proxy("_props", "name") _properties = association_proxy("_props", "name")
#FIXME fusion avec expected
_calc_props = relationship("_CalcProperties", collection_class=set) _calc_props = relationship("_CalcProperties", collection_class=set)
_calc_properties = association_proxy("_calc_props", "name") _calc_properties = association_proxy("_calc_props", "name")
_warnings_only = Column(Boolean) _warnings_only = Column(Boolean)
_readonly = Column(Boolean, default=False) _readonly = Column(Boolean, default=False)
_consistencies = relationship('_Consistency', secondary=consistency_table, _consistencies = relationship('_Consistency', secondary=consistency_table,
backref=backref('options', enable_typechecks=False)) backref=backref('options',
enable_typechecks=False))
_type = Column(String(50)) _type = Column(String(50))
_stated = Column(Boolean)
__mapper_args__ = { __mapper_args__ = {
'polymorphic_identity': 'option', 'polymorphic_identity': 'option',
'polymorphic_on': _type 'polymorphic_on': _type
@ -290,9 +293,27 @@ class _Base(SqlAlchemyBase):
_group_type = Column(String) _group_type = Column(String)
_is_build_cache = Column(Boolean, default=False) _is_build_cache = Column(Boolean, default=False)
def __init__(self): #def __init__(self):
def __init__(self, name, multi, warnings_only, doc, extra, calc_properties,
requires, properties, opt=undefined):
util.session.add(self) util.session.add(self)
self.commit() self._name = name
if multi is not undefined:
self._multi = multi
if warnings_only is not undefined:
self._warnings_only = warnings_only
if doc is not undefined:
self._informations = {'doc': doc}
if opt is not undefined:
self._opt = opt.id
if extra is not undefined:
self._extra = extra
if calc_properties is not undefined:
self._calc_properties = calc_properties
if requires is not undefined:
self._requires = requires
if properties is not undefined:
self._properties = properties
def commit(self): def commit(self):
util.session.commit() util.session.commit()
@ -300,6 +321,15 @@ class _Base(SqlAlchemyBase):
def _add_consistency(self, func, all_cons_opts, params): def _add_consistency(self, func, all_cons_opts, params):
_Consistency(func, all_cons_opts, params) _Consistency(func, all_cons_opts, params)
def _set_default_values(self, default, default_multi):
if self.impl_is_multi() and default is None:
default = []
self.impl_validate(default)
self._default = default
if self.impl_is_multi() and default_multi is not None:
self._validate(default_multi)
self._default_multi = default_multi
def _get_consistencies(self): def _get_consistencies(self):
return [(consistency.func, consistency.options, consistency.params) return [(consistency.func, consistency.options, consistency.params)
for consistency in self._consistencies] for consistency in self._consistencies]
@ -307,11 +337,109 @@ class _Base(SqlAlchemyBase):
def _get_id(self): def _get_id(self):
return self.id return self.id
def impl_get_callback(self):
ret = self._callback
if ret is None:
return (None, {})
return ret, self._callback_params
def impl_get_validator(self):
ret = self._validator
if ret is None:
return (None, {})
return ret, self._validator_params
def _impl_getsubdyn(self): def _impl_getsubdyn(self):
return self._subdyn return util.session.query(_Base).filter_by(id=self._subdyn).first()
def _impl_getopt(self):
return util.session.query(_Base).filter_by(id=self._opt).first()
def impl_getname(self):
return self._name
def impl_getrequires(self):
return self._requires
def impl_getdefault(self):
ret = self._default
if self.impl_is_multi():
if ret is None:
return []
return list(ret)
return ret
def impl_getdefault_multi(self):
if self.impl_is_multi():
return self._default_multi
def _get_extra(self, key):
return self._extra[key]
def _impl_setopt(self, opt):
self._opt = opt.id
def _impl_setsubdyn(self, subdyn): def _impl_setsubdyn(self, subdyn):
self._subdyn = subdyn.id self._subdyn = subdyn.id
self.commit()
def _set_readonly(self):
self._readonly = True
def _set_callback(self, callback, callback_params):
self._callback = callback
if callback_params is not None:
self._callback_params = callback_params
def _set_validator(self, validator, validator_params):
self._validator = validator
if validator_params is not None:
self._validator_params = validator_params
def impl_is_readonly(self):
try:
return self._readonly
except AttributeError:
return False
def impl_is_multi(self):
return self._multi == 0 or self._multi == 2
def impl_is_submulti(self):
return self._multi == 2
def _is_warnings_only(self):
return self._warnings_only
def impl_get_calc_properties(self):
try:
return self._calc_properties
except AttributeError:
return frozenset()
# information
def impl_set_information(self, key, value):
self._informations[key] = value
def impl_get_information(self, key, default=undefined):
"""retrieves one information's item
:param key: the item string (ex: "help")
"""
if default is not undefined:
return self._informations.get(key, default)
try:
return self._informations[key]
except KeyError: # pragma: optional cover
raise ValueError(_("information's item not found: {0}").format(
key))
def _impl_getattributes(self):
slots = set()
mapper = inspect(self)
for column in mapper.attrs:
slots.add(column.key)
return slots
class Cache(SqlAlchemyBase): class Cache(SqlAlchemyBase):
@ -326,16 +454,18 @@ class Cache(SqlAlchemyBase):
subdyn_path = Column(String) subdyn_path = Column(String)
def __init__(self, descr, parent, option, path, subdyn_path): def __init__(self, descr, parent, option, path, subdyn_path):
#context
self.descr = descr.id self.descr = descr.id
self.parent = parent.id self.parent = parent.id
self.option = option.id self.option = option.id
self.path = path self.path = path
self.opt_type = option.__class__.__name__ self.opt_type = option.__class__.__name__
#is_subdyn = option._is_subdyn() if subdyn_path:
is_subdyn = option.impl_is_dynoptiondescription() self.is_subdyn = True
self.is_subdyn = is_subdyn
if is_subdyn:
self.subdyn_path = subdyn_path self.subdyn_path = subdyn_path
else:
self.is_subdyn = False
self.subdyn_path = None
class StorageOptionDescription(object): class StorageOptionDescription(object):
@ -368,36 +498,59 @@ class StorageOptionDescription(object):
save = False save = False
for option in self._impl_getchildren(dyn=False): for option in self._impl_getchildren(dyn=False):
attr = option.impl_getname() attr = option.impl_getname()
util.session.add(Cache(descr, self, option,
str('.'.join(_currpath + [attr])), subdyn_path))
if isinstance(option, StorageOptionDescription): if isinstance(option, StorageOptionDescription):
sub = subdyn_path
if option.impl_is_dynoptiondescription(): if option.impl_is_dynoptiondescription():
subdyn_path = '.'.join(_currpath) sub = '.'.join(_currpath)
util.session.add(Cache(descr, self, option,
str('.'.join(_currpath + [attr])),
sub))
_currpath.append(attr) _currpath.append(attr)
option.impl_build_cache_option(descr, option.impl_build_cache_option(descr,
_currpath, _currpath,
subdyn_path) sub)
_currpath.pop() _currpath.pop()
else:
if subdyn_path:
subdyn_path = '.'.join(_currpath)
util.session.add(Cache(descr, self, option,
str('.'.join(_currpath + [attr])),
subdyn_path))
if save: if save:
self._is_build_cache = True self._is_build_cache = True
util.session.commit() util.session.commit()
def impl_get_options_paths(self, bytype, byname, _subpath, only_first, def impl_get_options_paths(self, bytype, byname, _subpath, only_first,
context): context):
def _build_ret_opt(opt, option, suffix, name):
subdyn_path = opt.subdyn_path
dynpaths = opt.path[len(subdyn_path):].split('.')
path = subdyn_path
dot = False
for dynpath in dynpaths:
if dot:
path += '.'
path += dynpath + suffix
dot = True
_opt = option._impl_to_dyn(name + suffix, path)
return (path, _opt)
sqlquery = util.session.query(Cache).filter_by(descr=self.id) sqlquery = util.session.query(Cache).filter_by(descr=self.id)
if bytype is None: if bytype is None:
sqlquery = sqlquery.filter(not_( sqlquery = sqlquery.filter(and_(not_(
Cache.opt_type == 'OptionDescription')) Cache.opt_type == 'OptionDescription'),
not_(Cache.opt_type == 'DynOptionDescription')))
else: else:
sqlquery = sqlquery.filter_by(opt_type=bytype.__name__) sqlquery = sqlquery.filter_by(opt_type=bytype.__name__)
query = '' query = ''
or_query = '' or_query = ''
if _subpath is not None: if _subpath is not None:
query += _subpath + '.' query += _subpath + '.%'
if byname is not None: #if byname is not None:
or_query = query + byname # or_query = query + byname
query += '%.' + byname # query += '%.' + byname
if query != '': if query != '':
filter_query = Cache.path.like(query) filter_query = Cache.path.like(query)
if or_query != '': if or_query != '':
@ -415,28 +568,41 @@ class StorageOptionDescription(object):
option = util.session.query(_Base).filter_by(id=opt.option).first() option = util.session.query(_Base).filter_by(id=opt.option).first()
if opt.is_subdyn: if opt.is_subdyn:
name = option.impl_getname() name = option.impl_getname()
if byname is not None and byname.startswith(name): if byname is not None:
found = False if byname.startswith(name):
for suffix in option._subdyn._impl_get_suffixes( found = False
context): dynoption = option._impl_getsubdyn()
if byname == name + suffix: for suffix in dynoption._impl_get_suffixes(
found = True context):
subdyn_path = opt.subdyn_path if byname == name + suffix:
dynpaths = opt.path[len(subdyn_path):].split('.') found = True
break
path = subdyn_path if not found:
for dynpath in dynpaths: continue
path += '.' + dynpath + suffix ret_opt = _build_ret_opt(opt, option, suffix, name)
option = option._impl_to_dyn( else:
name + suffix, path) ret_opt = _build_ret_opt(opt, option, suffix, name)
break else:
if not found: if not only_first:
break ret_opt = []
dynoption = option._impl_getsubdyn()
for suffix in dynoption._impl_get_suffixes(context):
val = _build_ret_opt(opt, option, suffix, name)
if only_first:
ret_opt = val
else:
ret_opt.append(val)
else: else:
if byname is not None and byname != option.impl_getname():
continue
ret_opt = (opt.path, option) ret_opt = (opt.path, option)
if only_first: if only_first:
return ret_opt return ret_opt
ret.append(ret_opt) if isinstance(ret_opt, list):
if ret_opt != []:
ret.extend(ret_opt)
else:
ret.append(ret_opt)
return ret return ret
def _add_children(self, child_names, children): def _add_children(self, child_names, children):
@ -453,8 +619,7 @@ class StorageOptionDescription(object):
descr = context.cfgimpl_get_description().id descr = context.cfgimpl_get_description().id
for child in util.session.query(Cache).filter_by(descr=descr, for child in util.session.query(Cache).filter_by(descr=descr,
parent=self.id parent=self.id
).filter_by( ).all():
is_subdyn=True).all():
yield(util.session.query(_Base).filter_by(id=child.option).first()) yield(util.session.query(_Base).filter_by(id=child.option).first())
def _getattr(self, name, suffix=undefined, context=undefined, dyn=True): def _getattr(self, name, suffix=undefined, context=undefined, dyn=True):

View file

@ -65,12 +65,13 @@ class Settings(Cache, SqlAlchemyBase):
cascade="all, delete-orphan") cascade="all, delete-orphan")
_permissives = association_proxy("_perms", "permissives") _permissives = association_proxy("_perms", "permissives")
def __init__(self, storage): #def __init__(self, storage):
super(Settings, self).__init__(storage) # super(Settings, self).__init__(storage)
# properties # properties
def setproperties(self, path, properties): def setproperties(self, path, properties):
self._properties[path] = properties self._properties[path] = properties
util.session.commit()
def getproperties(self, path, default_properties): def getproperties(self, path, default_properties):
return self._properties.get(path, set(default_properties)) return self._properties.get(path, set(default_properties))
@ -80,10 +81,12 @@ class Settings(Cache, SqlAlchemyBase):
def reset_all_properties(self): def reset_all_properties(self):
self._properties.clear() self._properties.clear()
util.session.commit()
def delproperties(self, path): def delproperties(self, path):
try: try:
del(self._properties[path]) del(self._properties[path])
util.session.commit()
except KeyError: except KeyError:
pass pass

View file

@ -15,45 +15,59 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.i18n import _ from tiramisu.i18n import _
from tiramisu.error import ConfigError
from ..util import SerializeObject from ..util import SerializeObject
from .util import SqlAlchemyBase
import util
from sqlalchemy import Column, Integer, String
class Setting(SerializeObject): class Setting(SerializeObject):
"""Dictionary storage has no particular setting. """:param extension: database file extension (by default: db)
:param dir_database: root database directory (by default: /tmp)
""" """
pass #FIXME
extension = 'db'
dir_database = '/tmp'
setting = Setting() setting = Setting()
_list_sessions = []
class Session(SqlAlchemyBase):
__tablename__ = 'session'
id = Column(Integer, primary_key=True)
session = Column(String, index=True)
def __init__(self, session_id):
self.session = session_id
def list_sessions(): # pragma: optional cover def list_sessions(): # pragma: optional cover
return _list_sessions ret = []
for val in util.session.query(Session).all():
ret.append(val.session)
return ret
def delete_session(session_id): # pragma: optional cover def delete_session(session_id): # pragma: optional cover
raise ConfigError(_('dictionary storage cannot delete session')) #Must remove all values for this session!
util.session.delete(util.session.query(Session).filter_by(session=session_id).first())
util.session.commit()
class Storage(object): class Storage(object):
__slots__ = ('session_id', 'persistent') __slots__ = ('session_id', 'persistent')
storage = 'dictionary' storage = 'sqlalchemy'
#if object could be serializable #if object could be serializable
serializable = True serializable = True
def __init__(self, session_id, persistent, test=False): def __init__(self, session_id, persistent, test=False):
if not test and session_id in _list_sessions: # pragma: optional cover if util.session.query(Session).filter_by(session=session_id).first(): # pragma: optional cover
raise ValueError(_('session already used')) raise ValueError(_('session already used'))
if persistent: # pragma: optional cover
raise ValueError(_('a dictionary cannot be persistent'))
self.session_id = session_id self.session_id = session_id
self.persistent = persistent self.persistent = persistent
_list_sessions.append(self.session_id) util.session.add(Session(session_id))
util.session.commit()
def __del__(self): def __del__(self):
try: delete_session(self.session_id)
_list_sessions.remove(self.session_id)
except AttributeError: # pragma: optional cover
pass

View file

@ -16,18 +16,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# ____________________________________________________________ # ____________________________________________________________
#FIXME : il me faut une classe pour le owner !
#FIXME : pas si simple que ca ... parce que on lit un owner pour une config ...
#FIXME : mais ca serait peut etre logique
#FIXME : c'est en fait dans le Setting qu'il faut faire ca ... a voir après
from ..util import Cache from ..util import Cache
from .util import SqlAlchemyBase from .util import SqlAlchemyBase
from sqlalchemy import Column, Integer, String, PickleType, ForeignKey import util
from sqlalchemy.orm import relationship from sqlalchemy import Column, Integer, String, PickleType
from sqlalchemy.orm.collections import attribute_mapped_collection from tiramisu.setting import owners
from sqlalchemy.ext.associationproxy import association_proxy
#____________________________________________________________ #____________________________________________________________
@ -36,90 +30,114 @@ from sqlalchemy.ext.associationproxy import association_proxy
class _Vinformation(SqlAlchemyBase): class _Vinformation(SqlAlchemyBase):
__tablename__ = 'vinformation' __tablename__ = 'vinformation'
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
values = Column(Integer, ForeignKey('values.id')) session = Column(String, index=True)
path = Column(String, index=True)
key = Column(String) key = Column(String)
value = Column(PickleType) value = Column(PickleType)
def __init__(self, key, value): def __init__(self, session, key, value):
self.session = session
self.key = key self.key = key
self.value = value self.value = value
class _Value(SqlAlchemyBase): class Value(SqlAlchemyBase):
__tablename__ = 'value' __tablename__ = 'value'
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
values = Column(Integer, ForeignKey('values.id'), nullable=False) session = Column(String, index=True)
path = Column(String, nullable=True, unique=True, index=True) path = Column(String, index=True)
#FIXME a revoir avec le owner dans le setting key = Column(String)
owner = Column(String, nullable=False) value = Column(PickleType)
value = Column(PickleType, nullable=False) owner = Column(String)
def __init__(self, key, value): def __init__(self, session, path, value, owner):
self.path = key self.session = session
self.value = value[0] self.path = path
self.owner = value[1] self.value = value
self.owner = owner
class Values(Cache, SqlAlchemyBase): class Values(Cache):
__tablename__ = 'values'
id = Column(Integer, primary_key=True)
_vals = relationship("_Value",
collection_class=attribute_mapped_collection('key'),
cascade="all, delete-orphan")
_informations = association_proxy("_vals", "value")
_infos = relationship("_Vinformation",
collection_class=attribute_mapped_collection('key'),
cascade="all, delete-orphan")
_informations = association_proxy("_infos", "value")
def __init__(self, storage):
"""init plugin means create values storage
"""
self._values = {}
self._informations = {}
super(Values, self).__init__(storage)
# value # value
def setvalue(self, path, value, owner): def setvalue(self, path, value, owner):
"""set value for a path """set value for a path
a specified value must be associated to an owner a specified value must be associated to an owner
""" """
self._values[path] = (owner, value) val = util.session.query(Value).filter_by(
path=path, session=self._storage.session_id).first()
if val is None:
util.session.add(Value(self._storage.session_id, path, value,
owner))
else:
val.value = value
val.owner = owner
util.session.commit()
def getvalue(self, path): def getvalue(self, path):
"""get value for a path """get value for a path
return: only value, not the owner return: only value, not the owner
""" """
return self._values[path][1] val = util.session.query(Value).filter_by(
path=path, session=self._storage.session_id).first()
if not val:
raise KeyError('no value found')
return val.value
def hasvalue(self, path): def hasvalue(self, path):
"""if path has a value """if path has a value
return: boolean return: boolean
""" """
return path in self._values return util.session.query(Value).filter_by(
path=path, session=self._storage.session_id).first() is not None
def resetvalue(self, path): def resetvalue(self, path):
"""remove value means delete value in storage """remove value means delete value in storage
""" """
del(self._values[path]) val = util.session.query(Value).filter_by(
path=path, session=self._storage.session_id).first()
if val is not None:
util.session.delete(val)
util.session.commit()
def get_modified_values(self): def get_modified_values(self):
"""return all values in a dictionary """return all values in a dictionary
example: {'path1': (owner, 'value1'), 'path2': (owner, 'value2')} example: {'path1': (owner, 'value1'), 'path2': (owner, 'value2')}
""" """
return self._values ret = {}
for val in util.session.query(Value).filter_by(
session=self._storage.session_id).all():
ret[val.path] = (val.owner, val.value)
return ret
# owner # owner
def setowner(self, path, owner): def setowner(self, path, owner):
"""change owner for a path """change owner for a path
""" """
self._values[path] = (owner, self._values[path][1]) val = util.session.query(Value).filter_by(
path=path, session=self._storage.session_id).first()
if val is None:
raise KeyError('no value found')
else:
val.owner = owner
util.session.commit()
def getowner(self, path, default): def getowner(self, path, default):
"""get owner for a path """get owner for a path
return: owner object return: owner object
""" """
return self._values.get(path, (default, None))[0] val = util.session.query(Value).filter_by(
path=path, session=self._storage.session_id).first()
if val is None:
return default
else:
owner = val.owner
# autocreate owners
try:
return getattr(owners, owner)
except AttributeError:
owners.addowner(owner)
return getattr(owners, owner)
def set_information(self, key, value): def set_information(self, key, value):
"""updates the information's attribute """updates the information's attribute
@ -128,14 +146,23 @@ class Values(Cache, SqlAlchemyBase):
:param key: information's key (ex: "help", "doc" :param key: information's key (ex: "help", "doc"
:param value: information's value (ex: "the help string") :param value: information's value (ex: "the help string")
""" """
self._informations[key] = value pass
val = util.session.query(_Vinformation).filter_by(
key=key, session=self._storage.session_id).first()
if val is None:
util.session.add(_Vinformation(self._storage.session_id, key,
value))
else:
val.value = value
util.session.commit()
def get_information(self, key): def get_information(self, key):
"""retrieves one information's item """retrieves one information's item
:param key: the item string (ex: "help") :param key: the item string (ex: "help")
""" """
if key in self._informations: val = util.session.query(_Vinformation).filter_by(
return self._informations[key] key=key, session=self._storage.session_id).first()
else: # pragma: optional cover if not val:
raise ValueError("not found") raise ValueError("not found")
return val.value

View file

@ -346,7 +346,7 @@ class Values(object):
""" """
if isinstance(opt, SymLinkOption) and \ if isinstance(opt, SymLinkOption) and \
not isinstance(opt, DynSymLinkOption): not isinstance(opt, DynSymLinkOption):
opt = opt._opt opt = opt._impl_getopt()
path = opt.impl_getpath(self._getcontext()) path = opt.impl_getpath(self._getcontext())
return self._getowner(opt, path, force_permissive=force_permissive) return self._getowner(opt, path, force_permissive=force_permissive)