works on sqlalchemy storage

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

View File

@ -61,7 +61,8 @@ class StorageBase(object):
'__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
if doc is not undefined:
self._informations = {'doc': doc}
@ -72,6 +73,15 @@ class StorageBase(object):
if warnings_only is True:
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):
if self.impl_is_multi() and default is None:
default = []
@ -214,6 +224,9 @@ class StorageBase(object):
def _impl_getsubdyn(self):
return self._subdyn
def _impl_getopt(self):
return self._opt
def _set_readonly(self):
if not self.impl_is_readonly():
dico = self._informations
@ -233,6 +246,9 @@ class StorageBase(object):
def _impl_setsubdyn(self, subdyn):
self._subdyn = subdyn
def _impl_setopt(self, opt):
self._opt = opt
def _impl_convert_informations(self, descr, load=False):
if not load:
infos = self._informations
@ -255,6 +271,13 @@ class StorageBase(object):
self._set_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):
try:
return not isinstance(self._informations, dict)
@ -320,7 +343,10 @@ class StorageOptionDescription(StorageBase):
'_group_type', '_is_build_cache', '_state_group_type')
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
def _add_children(self, child_names, children):

View File

@ -23,7 +23,7 @@ from tiramisu.error import ConfigError
from .util import SqlAlchemyBase
import util
from sqlalchemy import not_, or_
from sqlalchemy import not_, or_, and_, inspect
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy import Column, Integer, String, Boolean, PickleType, \
@ -199,6 +199,7 @@ class _CallbackParam(SqlAlchemyBase):
#
# consistency
consistency_table = Table('consistencyopt', SqlAlchemyBase.metadata,
Column('id', Integer, primary_key=True),
Column('left_id', Integer, ForeignKey('consistency.id')),
Column('right_id', Integer, ForeignKey('baseoption.id'))
)
@ -214,6 +215,7 @@ class _Consistency(SqlAlchemyBase):
self.func = func
for option in all_cons_opts:
option._consistencies.append(self)
print type(option._consistencies)
self.params = params
@ -245,6 +247,8 @@ class _Base(SqlAlchemyBase):
_default = Column(PickleType)
_default_multi = Column(PickleType)
_subdyn = Column(Integer)
_dyn = Column(String)
_opt = Column(Integer)
_choice_values = Column(PickleType)
_cho_params = relationship('_CallbackParam',
collection_class=
@ -268,19 +272,18 @@ class _Base(SqlAlchemyBase):
_validator_params = association_proxy("_val_params", "params",
getset_factory=load_callback_parm)
######
#FIXME pas 2 fois la meme properties dans la base ...
#FIXME not autoload
#FIXME normalement tuple ... transforme en set !
_props = relationship("_PropertyOption", collection_class=set)
_properties = association_proxy("_props", "name")
#FIXME fusion avec expected
_calc_props = relationship("_CalcProperties", collection_class=set)
_calc_properties = association_proxy("_calc_props", "name")
_warnings_only = Column(Boolean)
_readonly = Column(Boolean, default=False)
_consistencies = relationship('_Consistency', secondary=consistency_table,
backref=backref('options', enable_typechecks=False))
backref=backref('options',
enable_typechecks=False))
_type = Column(String(50))
_stated = Column(Boolean)
__mapper_args__ = {
'polymorphic_identity': 'option',
'polymorphic_on': _type
@ -290,9 +293,27 @@ class _Base(SqlAlchemyBase):
_group_type = Column(String)
_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)
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):
util.session.commit()
@ -300,6 +321,15 @@ class _Base(SqlAlchemyBase):
def _add_consistency(self, 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):
return [(consistency.func, consistency.options, consistency.params)
for consistency in self._consistencies]
@ -307,11 +337,109 @@ class _Base(SqlAlchemyBase):
def _get_id(self):
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):
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):
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):
@ -326,16 +454,18 @@ class Cache(SqlAlchemyBase):
subdyn_path = Column(String)
def __init__(self, descr, parent, option, path, subdyn_path):
#context
self.descr = descr.id
self.parent = parent.id
self.option = option.id
self.path = path
self.opt_type = option.__class__.__name__
#is_subdyn = option._is_subdyn()
is_subdyn = option.impl_is_dynoptiondescription()
self.is_subdyn = is_subdyn
if is_subdyn:
if subdyn_path:
self.is_subdyn = True
self.subdyn_path = subdyn_path
else:
self.is_subdyn = False
self.subdyn_path = None
class StorageOptionDescription(object):
@ -368,36 +498,59 @@ class StorageOptionDescription(object):
save = False
for option in self._impl_getchildren(dyn=False):
attr = option.impl_getname()
util.session.add(Cache(descr, self, option,
str('.'.join(_currpath + [attr])), subdyn_path))
if isinstance(option, StorageOptionDescription):
sub = subdyn_path
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)
option.impl_build_cache_option(descr,
_currpath,
subdyn_path)
sub)
_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:
self._is_build_cache = True
util.session.commit()
def impl_get_options_paths(self, bytype, byname, _subpath, only_first,
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)
if bytype is None:
sqlquery = sqlquery.filter(not_(
Cache.opt_type == 'OptionDescription'))
sqlquery = sqlquery.filter(and_(not_(
Cache.opt_type == 'OptionDescription'),
not_(Cache.opt_type == 'DynOptionDescription')))
else:
sqlquery = sqlquery.filter_by(opt_type=bytype.__name__)
query = ''
or_query = ''
if _subpath is not None:
query += _subpath + '.'
if byname is not None:
or_query = query + byname
query += '%.' + byname
query += _subpath + '.%'
#if byname is not None:
# or_query = query + byname
# query += '%.' + byname
if query != '':
filter_query = Cache.path.like(query)
if or_query != '':
@ -415,28 +568,41 @@ class StorageOptionDescription(object):
option = util.session.query(_Base).filter_by(id=opt.option).first()
if opt.is_subdyn:
name = option.impl_getname()
if byname is not None and byname.startswith(name):
found = False
for suffix in option._subdyn._impl_get_suffixes(
context):
if byname == name + suffix:
found = True
subdyn_path = opt.subdyn_path
dynpaths = opt.path[len(subdyn_path):].split('.')
path = subdyn_path
for dynpath in dynpaths:
path += '.' + dynpath + suffix
option = option._impl_to_dyn(
name + suffix, path)
break
if not found:
break
if byname is not None:
if byname.startswith(name):
found = False
dynoption = option._impl_getsubdyn()
for suffix in dynoption._impl_get_suffixes(
context):
if byname == name + suffix:
found = True
break
if not found:
continue
ret_opt = _build_ret_opt(opt, option, suffix, name)
else:
ret_opt = _build_ret_opt(opt, option, suffix, name)
else:
if not only_first:
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:
if byname is not None and byname != option.impl_getname():
continue
ret_opt = (opt.path, option)
if only_first:
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
def _add_children(self, child_names, children):
@ -453,8 +619,7 @@ class StorageOptionDescription(object):
descr = context.cfgimpl_get_description().id
for child in util.session.query(Cache).filter_by(descr=descr,
parent=self.id
).filter_by(
is_subdyn=True).all():
).all():
yield(util.session.query(_Base).filter_by(id=child.option).first())
def _getattr(self, name, suffix=undefined, context=undefined, dyn=True):

View File

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

View File

@ -15,45 +15,59 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ____________________________________________________________
from tiramisu.i18n import _
from tiramisu.error import ConfigError
from ..util import SerializeObject
from .util import SqlAlchemyBase
import util
from sqlalchemy import Column, Integer, String
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()
_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
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
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):
__slots__ = ('session_id', 'persistent')
storage = 'dictionary'
storage = 'sqlalchemy'
#if object could be serializable
serializable = True
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'))
if persistent: # pragma: optional cover
raise ValueError(_('a dictionary cannot be persistent'))
self.session_id = session_id
self.persistent = persistent
_list_sessions.append(self.session_id)
util.session.add(Session(session_id))
util.session.commit()
def __del__(self):
try:
_list_sessions.remove(self.session_id)
except AttributeError: # pragma: optional cover
pass
delete_session(self.session_id)

View File

@ -16,18 +16,12 @@
# 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 SqlAlchemyBase
from sqlalchemy import Column, Integer, String, PickleType, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm.collections import attribute_mapped_collection
from sqlalchemy.ext.associationproxy import association_proxy
import util
from sqlalchemy import Column, Integer, String, PickleType
from tiramisu.setting import owners
#____________________________________________________________
@ -36,90 +30,114 @@ from sqlalchemy.ext.associationproxy import association_proxy
class _Vinformation(SqlAlchemyBase):
__tablename__ = 'vinformation'
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)
value = Column(PickleType)
def __init__(self, key, value):
def __init__(self, session, key, value):
self.session = session
self.key = key
self.value = value
class _Value(SqlAlchemyBase):
class Value(SqlAlchemyBase):
__tablename__ = 'value'
id = Column(Integer, primary_key=True)
values = Column(Integer, ForeignKey('values.id'), nullable=False)
path = Column(String, nullable=True, unique=True, index=True)
#FIXME a revoir avec le owner dans le setting
owner = Column(String, nullable=False)
value = Column(PickleType, nullable=False)
session = Column(String, index=True)
path = Column(String, index=True)
key = Column(String)
value = Column(PickleType)
owner = Column(String)
def __init__(self, key, value):
self.path = key
self.value = value[0]
self.owner = value[1]
def __init__(self, session, path, value, owner):
self.session = session
self.path = path
self.value = value
self.owner = owner
class Values(Cache, SqlAlchemyBase):
__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)
class Values(Cache):
# value
def setvalue(self, path, value, owner):
"""set value for a path
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):
"""get value for a path
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):
"""if path has a value
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):
"""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):
"""return all values in a dictionary
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
def setowner(self, path, owner):
"""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):
"""get owner for a path
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):
"""updates the information's attribute
@ -128,14 +146,23 @@ class Values(Cache, SqlAlchemyBase):
:param key: information's key (ex: "help", "doc"
: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):
"""retrieves one information's item
:param key: the item string (ex: "help")
"""
if key in self._informations:
return self._informations[key]
else: # pragma: optional cover
val = util.session.query(_Vinformation).filter_by(
key=key, session=self._storage.session_id).first()
if not val:
raise ValueError("not found")
return val.value