better support for sqlalchemy storage
authorEmmanuel Garette <egarette@cadoles.com>
Sun, 6 Jul 2014 13:35:13 +0000 (15:35 +0200)
committerEmmanuel Garette <egarette@cadoles.com>
Sun, 6 Jul 2014 13:35:13 +0000 (15:35 +0200)
tiramisu/storage/sqlalchemy/setting.py [new file with mode: 0644]
tiramisu/storage/sqlalchemy/storage.py [new file with mode: 0644]
tiramisu/storage/sqlalchemy/util.py [new file with mode: 0644]
tiramisu/storage/sqlalchemy/value.py [new file with mode: 0644]

diff --git a/tiramisu/storage/sqlalchemy/setting.py b/tiramisu/storage/sqlalchemy/setting.py
new file mode 100644 (file)
index 0000000..c9ad3da
--- /dev/null
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 -*-
+"default plugin for setting: set it in a simple dictionary"
+# Copyright (C) 2014 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
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# ____________________________________________________________
+from ..util import Cache
+from .util import SqlAlchemyBase
+import util
+from sqlalchemy import Column, Integer, String, PickleType, ForeignKey
+from sqlalchemy.orm import relationship
+from sqlalchemy.ext.associationproxy import association_proxy
+from sqlalchemy.orm.collections import attribute_mapped_collection
+
+
+#____________________________________________________________
+#
+# properties|permissives
+class _Property(SqlAlchemyBase):
+    __tablename__ = 'property'
+    id = Column(Integer, primary_key=True)
+    setting = Column(Integer, ForeignKey('settings.id'), nullable=False)
+    path = Column(String)
+    properties = Column(PickleType)
+
+    def __init__(self, path, properties):
+        self.path = path
+        self.properties = properties
+
+
+class _Permissive (SqlAlchemyBase):
+    __tablename__ = 'permissive'
+    id = Column(Integer, primary_key=True)
+    setting = Column(Integer, ForeignKey('settings.id'), nullable=False)
+    path = Column(String)
+    permissives = Column(PickleType)
+
+    def __init__(self, path, permissives):
+        self.path = path
+        self.permissives = permissives
+
+
+#____________________________________________________________
+#FIXME marche pas le cache ... de toute facon je vais faire un storage separe !
+class Settings(Cache, SqlAlchemyBase):
+    __tablename__ = 'settings'
+    id = Column(Integer, primary_key=True)
+    _props = relationship("_Property",
+                          collection_class=attribute_mapped_collection('path'),
+                          cascade="all, delete-orphan")
+    _properties = association_proxy("_props", "properties")
+    _perms = relationship("_Permissive",
+                          collection_class=attribute_mapped_collection('path'),
+                          cascade="all, delete-orphan")
+    _permissives = association_proxy("_perms", "permissives")
+
+    def __init__(self, storage):
+        super(Settings, self).__init__(storage)
+
+    # properties
+    def setproperties(self, path, properties):
+        self._properties[path] = properties
+
+    def getproperties(self, path, default_properties):
+        return self._properties.get(path, set(default_properties))
+
+    def hasproperties(self, path):
+        return path in self._properties
+
+    def reset_all_properties(self):
+        self._properties.clear()
+
+    def delproperties(self, path):
+        try:
+            del(self._properties[path])
+        except KeyError:
+            pass
+
+    # permissive
+    def setpermissive(self, path, permissive):
+        self._permissives[path] = frozenset(permissive)
+        util.session.commit()
+
+    def getpermissive(self, path=None):
+        ret = self._permissives.get(path, frozenset())
+        #replace None by a frozenset()
+        return {None: frozenset()}.get(ret, ret)
+
+    def get_modified_properties(self):
+        """return all modified settings in a dictionary
+        example: {'path1': set(['prop1', 'prop2'])}
+        """
+        return self._properties
+
+    def get_modified_permissives(self):
+        """return all modified permissives in a dictionary
+        example: {'path1': set(['perm1', 'perm2'])}
+        """
+        return self._permissives
diff --git a/tiramisu/storage/sqlalchemy/storage.py b/tiramisu/storage/sqlalchemy/storage.py
new file mode 100644 (file)
index 0000000..252d29e
--- /dev/null
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2013 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
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# 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
+
+
+class Setting(SerializeObject):
+    """Dictionary storage has no particular setting.
+    """
+    pass
+
+
+setting = Setting()
+_list_sessions = []
+
+
+def list_sessions():  # pragma: optional cover
+    return _list_sessions
+
+
+def delete_session(session_id):  # pragma: optional cover
+    raise ConfigError(_('dictionary storage cannot delete session'))
+
+
+class Storage(object):
+    __slots__ = ('session_id', 'persistent')
+    storage = 'dictionary'
+    #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
+            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)
+
+    def __del__(self):
+        try:
+            _list_sessions.remove(self.session_id)
+        except AttributeError:  # pragma: optional cover
+            pass
diff --git a/tiramisu/storage/sqlalchemy/util.py b/tiramisu/storage/sqlalchemy/util.py
new file mode 100644 (file)
index 0000000..7af84a1
--- /dev/null
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+""
+# Copyright (C) 2014 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+# ____________________________________________________________
+from sqlalchemy.orm import sessionmaker
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy import create_engine
+
+
+engine = create_engine('sqlite:///:memory:')
+SqlAlchemyBase = declarative_base()
+
+global session
+session = None
+
+
+def load():
+    global session
+    if session is None:
+        #engine.echo = True
+        #print SqlAlchemyBase.metadata.tables.keys()
+        SqlAlchemyBase.metadata.create_all(engine)
+        Session = sessionmaker(bind=engine)
+        session = Session()
diff --git a/tiramisu/storage/sqlalchemy/value.py b/tiramisu/storage/sqlalchemy/value.py
new file mode 100644 (file)
index 0000000..30447eb
--- /dev/null
@@ -0,0 +1,141 @@
+# -*- coding: utf-8 -*-
+"plugin for value: set it in sqlalchemy"
+# Copyright (C) 2013-2014 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
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# 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
+
+
+#____________________________________________________________
+#
+# information
+class _Vinformation(SqlAlchemyBase):
+    __tablename__ = 'vinformation'
+    id = Column(Integer, primary_key=True)
+    values = Column(Integer, ForeignKey('values.id'))
+    key = Column(String)
+    value = Column(PickleType)
+
+    def __init__(self, key, value):
+        self.key = key
+        self.value = value
+
+
+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)
+
+    def __init__(self, key, value):
+        self.path = key
+        self.value = value[0]
+        self.owner = value[1]
+
+
+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)
+
+    # 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)
+
+    def getvalue(self, path):
+        """get value for a path
+        return: only value, not the owner
+        """
+        return self._values[path][1]
+
+    def hasvalue(self, path):
+        """if path has a value
+        return: boolean
+        """
+        return path in self._values
+
+    def resetvalue(self, path):
+        """remove value means delete value in storage
+        """
+        del(self._values[path])
+
+    def get_modified_values(self):
+        """return all values in a dictionary
+        example: {'path1': (owner, 'value1'), 'path2': (owner, 'value2')}
+        """
+        return self._values
+
+    # owner
+    def setowner(self, path, owner):
+        """change owner for a path
+        """
+        self._values[path] = (owner, self._values[path][1])
+
+    def getowner(self, path, default):
+        """get owner for a path
+        return: owner object
+        """
+        return self._values.get(path, (default, None))[0]
+
+    def set_information(self, key, value):
+        """updates the information's attribute
+        (which is a dictionary)
+
+        :param key: information's key (ex: "help", "doc"
+        :param value: information's value (ex: "the help string")
+        """
+        self._informations[key] = value
+
+    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
+            raise ValueError("not found")