we can serialize Config now
authorEmmanuel Garette <egarette@cadoles.com>
Sun, 22 Sep 2013 18:57:52 +0000 (20:57 +0200)
committerEmmanuel Garette <egarette@cadoles.com>
Sun, 22 Sep 2013 18:57:52 +0000 (20:57 +0200)
18 files changed:
test/test_option_setting.py
test/test_state.py
tiramisu/config.py
tiramisu/option.py
tiramisu/setting.py
tiramisu/storage/__init__.py
tiramisu/storage/cache.py [deleted file]
tiramisu/storage/dictionary/__init__.py
tiramisu/storage/dictionary/setting.py
tiramisu/storage/dictionary/storage.py
tiramisu/storage/dictionary/value.py
tiramisu/storage/sqlite3/__init__.py
tiramisu/storage/sqlite3/setting.py
tiramisu/storage/sqlite3/sqlite3db.py
tiramisu/storage/sqlite3/storage.py
tiramisu/storage/sqlite3/value.py
tiramisu/storage/util.py [new file with mode: 0644]
tiramisu/value.py

index 2dc76ab..ed5f7d7 100644 (file)
@@ -327,23 +327,23 @@ def test_reset_properties():
     cfg = Config(descr)
     setting = cfg.cfgimpl_get_settings()
     option = cfg.cfgimpl_get_description().gc.dummy
-    assert setting._p_.get_properties(cfg) == {}
+    assert setting._p_.get_modified_properties() == {}
     setting.append('frozen')
-    assert setting._p_.get_properties(cfg) == {None: set(('frozen', 'expire', 'cache', 'validator'))}
+    assert setting._p_.get_modified_properties() == {None: set(('frozen', 'expire', 'cache', 'validator'))}
     setting.reset()
-    assert setting._p_.get_properties(cfg) == {}
+    assert setting._p_.get_modified_properties() == {}
     setting[option].append('test')
-    assert setting._p_.get_properties(cfg) == {'gc.dummy': set(('test',))}
+    assert setting._p_.get_modified_properties() == {'gc.dummy': set(('test',))}
     setting.reset()
-    assert setting._p_.get_properties(cfg) == {'gc.dummy': set(('test',))}
+    assert setting._p_.get_modified_properties() == {'gc.dummy': set(('test',))}
     setting.append('frozen')
-    assert setting._p_.get_properties(cfg) == {None: set(('frozen', 'expire', 'validator', 'cache')), 'gc.dummy': set(('test',))}
+    assert setting._p_.get_modified_properties() == {None: set(('frozen', 'expire', 'validator', 'cache')), 'gc.dummy': set(('test',))}
     setting.reset(option)
-    assert setting._p_.get_properties(cfg) == {None: set(('frozen', 'expire', 'validator', 'cache'))}
+    assert setting._p_.get_modified_properties() == {None: set(('frozen', 'expire', 'validator', 'cache'))}
     setting[option].append('test')
-    assert setting._p_.get_properties(cfg) == {None: set(('frozen', 'expire', 'validator', 'cache')), 'gc.dummy': set(('test',))}
+    assert setting._p_.get_modified_properties() == {None: set(('frozen', 'expire', 'validator', 'cache')), 'gc.dummy': set(('test',))}
     setting.reset(all_properties=True)
-    assert setting._p_.get_properties(cfg) == {}
+    assert setting._p_.get_modified_properties() == {}
     raises(ValueError, 'setting.reset(all_properties=True, opt=option)')
     a = descr.wantref
     setting[a].append('test')
index ea1956c..8587b4a 100644 (file)
@@ -1,5 +1,9 @@
 from tiramisu.option import BoolOption, UnicodeOption, SymLinkOption, \
     OptionDescription
+from tiramisu.config import Config
+from tiramisu.setting import owners
+from tiramisu.storage import delete_session
+from tiramisu.error import ConfigError
 from pickle import dumps, loads
 
 
@@ -152,3 +156,95 @@ def test_no_state_attr():
     _no_state(q.o.b)
     _no_state(q.o.u)
     _no_state(q.o.s)
+
+
+def test_state_config():
+    val1 = BoolOption('val1', "")
+    maconfig = OptionDescription('rootconfig', '', [val1])
+    try:
+        cfg = Config(maconfig, persistent=True, session_id='29090931')
+    except ValueError:
+        cfg = Config(maconfig, session_id='29090931')
+        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()
+    try:
+        delete_session('29090931')
+    except ConfigError:
+        pass
+
+
+def test_state_properties():
+    val1 = BoolOption('val1', "")
+    maconfig = OptionDescription('rootconfig', '', [val1])
+    try:
+        cfg = Config(maconfig, persistent=True, session_id='29090932')
+    except ValueError:
+        cfg = Config(maconfig, session_id='29090932')
+        cfg._impl_test = True
+    cfg.read_write()
+    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()
+    try:
+        delete_session('29090931')
+    except ConfigError:
+        pass
+
+
+def test_state_values():
+    val1 = BoolOption('val1', "")
+    maconfig = OptionDescription('rootconfig', '', [val1])
+    try:
+        cfg = Config(maconfig, persistent=True, session_id='29090933')
+    except ValueError:
+        cfg = Config(maconfig, session_id='29090933')
+        cfg._impl_test = True
+    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()
+    q.val1 = False
+    #assert cfg.val1 is True
+    assert q.val1 is False
+    try:
+        delete_session('29090931')
+    except ConfigError:
+        pass
+
+
+def test_state_values_owner():
+    val1 = BoolOption('val1', "")
+    maconfig = OptionDescription('rootconfig', '', [val1])
+    try:
+        cfg = Config(maconfig, persistent=True, session_id='29090934')
+    except ValueError:
+        cfg = Config(maconfig, session_id='29090934')
+        cfg._impl_test = True
+    owners.addowner('newowner')
+    cfg.cfgimpl_get_settings().setowner(owners.newowner)
+    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()
+    q.val1 = False
+    nval1 = q.cfgimpl_get_description().val1
+    assert q.getowner(nval1) == owners.newowner
+    try:
+        delete_session('29090931')
+    except ConfigError:
+        pass
index fac3caf..d817fa1 100644 (file)
@@ -24,7 +24,8 @@ import weakref
 from tiramisu.error import PropertiesOptionError, ConfigError
 from tiramisu.option import OptionDescription, Option, SymLinkOption
 from tiramisu.setting import groups, Settings, default_encoding
-from tiramisu.storage import get_storages
+from tiramisu.storage import get_storages, get_storage, set_storage, \
+    _impl_getstate_setting
 from tiramisu.value import Values
 from tiramisu.i18n import _
 
@@ -521,7 +522,7 @@ class CommonConfig(SubConfig):
 # ____________________________________________________________
 class Config(CommonConfig):
     "main configuration management entry"
-    __slots__ = ('__weakref__', )
+    __slots__ = ('__weakref__', '_impl_test')
 
     def __init__(self, descr, session_id=None, persistent=False):
         """ Configuration option management master class
@@ -542,6 +543,43 @@ class Config(CommonConfig):
         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 __getstate__(self):
+        if self._impl_meta is not None:
+            raise ConfigError('cannot serialize Config with meta')
+        slots = set()
+        for subclass in self.__class__.__mro__:
+            if subclass is not object:
+                slots.update(subclass.__slots__)
+        slots -= frozenset(['_impl_context', '__weakref__'])
+        state = {}
+        for slot in slots:
+            try:
+                state[slot] = getattr(self, slot)
+            except AttributeError:
+                pass
+        storage = self._impl_values._p_._storage
+        if not storage.serializable:
+            raise ConfigError('this storage is not serialisable, could be a '
+                              'none persistent storage')
+        state['_storage'] = {'session_id': storage.session_id,
+                             'persistent': storage.persistent}
+        state['_impl_setting'] = _impl_getstate_setting()
+        return state
+
+    def __setstate__(self, state):
+        for key, value in state.items():
+            if key not in ['_storage', '_impl_setting']:
+                setattr(self, key, value)
+        set_storage(**state['_impl_setting'])
+        self._impl_context = weakref.ref(self)
+        self._impl_settings.context = weakref.ref(self)
+        self._impl_values.context = weakref.ref(self)
+        storage = get_storage(test=self._impl_test, **state['_storage'])
+        self._impl_values._impl_setstate(storage)
+        self._impl_settings._impl_setstate(storage)
 
     def cfgimpl_reset_cache(self,
                             only_expired=False,
index 89d960a..10aba5c 100644 (file)
@@ -242,8 +242,9 @@ class BaseOption(object):
         :param descr: the parent :class:`tiramisu.option.OptionDescription`
         """
         self._stated = True
-        self._impl_convert_consistencies(descr)
-        self._impl_convert_requires(descr)
+        for func in dir(self):
+            if func.startswith('_impl_convert_'):
+                getattr(self, func)(descr)
         try:
             self._state_readonly = self._readonly
         except AttributeError:
@@ -294,8 +295,9 @@ class BaseOption(object):
 
         :type descr: :class:`tiramisu.option.OptionDescription`
         """
-        self._impl_convert_consistencies(descr, load=True)
-        self._impl_convert_requires(descr, load=True)
+        for func in dir(self):
+            if func.startswith('_impl_convert_'):
+                getattr(self, func)(descr, load=True)
         try:
             self._readonly = self._state_readonly
             del(self._state_readonly)
@@ -595,23 +597,6 @@ class Option(BaseOption):
             else:
                 self._state_callback = (callback, cllbck_prms)
 
-    # serialize
-    def _impl_getstate(self, descr):
-        """the under the hood stuff that need to be done
-        before the serialization.
-        """
-        self._stated = True
-        self._impl_convert_callbacks(descr)
-        super(Option, self)._impl_getstate(descr)
-
-    # unserialize
-    def _impl_setstate(self, descr):
-        """the under the hood stuff that need to be done
-        before the serialization.
-        """
-        self._impl_convert_callbacks(descr, load=True)
-        super(Option, self)._impl_setstate(descr)
-
 
 class ChoiceOption(Option):
     """represents a choice out of several objects.
index 531e846..684ec74 100644 (file)
@@ -105,7 +105,7 @@ rw_remove = set(['permissive', 'everything_frozen', 'mandatory'])
 
 
 # ____________________________________________________________
-class _NameSpace:
+class _NameSpace(object):
     """convenient class that emulates a module
     and builds constants (that is, unique names)
     when attribute is added, we cannot delete it
@@ -591,3 +591,23 @@ class Settings(object):
         :returns: path
         """
         return self.context().cfgimpl_get_description().impl_get_path_by_opt(opt)
+
+    def get_modified_properties(self):
+        return self._p_.get_modified_properties()
+
+    def get_modified_permissives(self):
+        return self._p_.get_modified_permissives()
+
+    def __getstate__(self):
+        return {'_p_': self._p_, '_owner': str(self._owner)}
+
+    def _impl_setstate(self, storage):
+        self._p_._storage = storage
+
+    def __setstate__(self, states):
+        self._p_ = states['_p_']
+        try:
+            self._owner = getattr(owners, states['_owner'])
+        except AttributeError:
+            owners.addowner(states['_owner'])
+            self._owner = getattr(owners, states['_owner'])
index 1394258..c232472 100644 (file)
@@ -68,7 +68,7 @@ class StorageType(object):
 storage_type = StorageType()
 
 
-def set_storage(name, **args):
+def set_storage(name, **kwargs):
     """Change storage's configuration
 
     :params name: is the storage name. If storage is already set, cannot
@@ -77,16 +77,31 @@ def set_storage(name, **args):
     Other attributes are differents according to the selected storage's name
     """
     storage_type.set(name)
-    settings = storage_type.get().Setting()
-    for option, value in args.items():
+    setting = storage_type.get().setting
+    for option, value in kwargs.items():
         try:
-            getattr(settings, option)
-            setattr(settings, option, value)
+            getattr(setting, option)
+            setattr(setting, option, value)
         except AttributeError:
             raise ValueError(_('option {0} not already exists in storage {1}'
                                '').format(option, name))
 
 
+def _impl_getstate_setting():
+    setting = storage_type.get().setting
+    state = {'name': storage_type.storage_type}
+    for var in dir(setting):
+        if not var.startswith('_'):
+            state[var] = getattr(setting, var)
+    return state
+
+
+def get_storage(session_id, persistent, test):
+    """all used when __setstate__ a Config
+    """
+    return storage_type.get().Storage(session_id, persistent, test)
+
+
 def get_storages(context, session_id, persistent):
     def gen_id(config):
         return str(id(config)) + str(time())
diff --git a/tiramisu/storage/cache.py b/tiramisu/storage/cache.py
deleted file mode 100644 (file)
index 347d270..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-# -*- coding: utf-8 -*-
-"default plugin for cache: set it in a simple dictionary"
-# 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 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
-#
-# ____________________________________________________________
-
-
-class Cache(object):
-    __slots__ = ('_cache', 'storage')
-    key_is_path = False
-
-    def __init__(self, storage):
-        self._cache = {}
-        self.storage = storage
-
-    def setcache(self, path, val, time):
-        self._cache[path] = (val, time)
-
-    def getcache(self, path, exp):
-        value, created = self._cache[path]
-        if created is None or exp <= created:
-            return True, value
-        return False, None
-
-    def hascache(self, path):
-        """ path is in the cache
-
-        :param path: the path's option
-        """
-        return path in self._cache
-
-    def reset_expired_cache(self, exp):
-        for key in tuple(self._cache.keys()):
-            val, created = self._cache[key]
-            if created is not None and exp > created:
-                del(self._cache[key])
-
-    def reset_all_cache(self):
-        "empty the cache"
-        self._cache.clear()
-
-    def get_cached(self, context):
-        """return all values in a dictionary
-        example: {'path1': ('value1', 'time1'), 'path2': ('value2', 'time2')}
-        """
-        return self._cache
index dadce23..bc81450 100644 (file)
@@ -26,6 +26,6 @@ use it. But if something goes wrong, you will lost your modifications.
 """
 from .value import Values
 from .setting import Settings
-from .storage import Setting, Storage, list_sessions, delete_session
+from .storage import setting, Storage, list_sessions, delete_session
 
-__all__ = (Setting, Values, Settings, Storage, list_sessions, delete_session)
+__all__ = (setting, Values, Settings, Storage, list_sessions, delete_session)
index 706ab2a..1b7001b 100644 (file)
@@ -17,7 +17,7 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #
 # ____________________________________________________________
-from ..cache import Cache
+from ..util import Cache
 
 
 class Settings(Cache):
@@ -50,12 +50,21 @@ class Settings(Cache):
         except KeyError:
             pass
 
-    def get_properties(self, context):
-        return self._properties
-
     # permissive
     def setpermissive(self, path, permissive):
         self._permissives[path] = frozenset(permissive)
 
     def getpermissive(self, path=None):
         return self._permissives.get(path, frozenset())
+
+    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
index 6e15c1b..465fe26 100644 (file)
 # ____________________________________________________________
 from tiramisu.i18n import _
 from tiramisu.error import ConfigError
+from ..util import SerializeObject
 
 
-class Setting(object):
+class Setting(SerializeObject):
     """Dictionary storage has no particular setting.
     """
     pass
@@ -39,15 +40,18 @@ def delete_session(session_id):
 
 
 class Storage(object):
-    __slots__ = ('session_id', 'values', 'settings')
+    __slots__ = ('session_id', 'persistent')
     storage = 'dictionary'
+    #if object could be serializable
+    serializable = True
 
-    def __init__(self, session_id, persistent):
-        if session_id in _list_sessions:
+    def __init__(self, session_id, persistent, test=False):
+        if not test and session_id in _list_sessions:
             raise ValueError(_('session already used'))
         if persistent:
             raise ValueError(_('a dictionary cannot be persistent'))
         self.session_id = session_id
+        self.persistent = persistent
         _list_sessions.append(self.session_id)
 
     def __del__(self):
index c435d06..fedf1ec 100644 (file)
@@ -18,7 +18,7 @@
 #
 # ____________________________________________________________
 
-from ..cache import Cache
+from ..util import Cache
 
 
 class Values(Cache):
index dc6c14b..8d79070 100644 (file)
@@ -24,6 +24,6 @@ You should not configure differents Configs with same session_id.
 """
 from .value import Values
 from .setting import Settings
-from .storage import Setting, Storage, list_sessions, delete_session
+from .storage import setting, Storage, list_sessions, delete_session
 
-__all__ = (Setting, Values, Settings, Storage, list_sessions, delete_session)
+__all__ = (setting, Values, Settings, Storage, list_sessions, delete_session)
index 720849b..ed79181 100644 (file)
@@ -30,22 +30,22 @@ class Settings(Sqlite3DB):
         permissives_table += 'primary key, permissives text)'
         # should init cache too
         super(Settings, self).__init__(storage)
-        self.storage.execute(settings_table, commit=False)
-        self.storage.execute(permissives_table)
+        self._storage.execute(settings_table, commit=False)
+        self._storage.execute(permissives_table)
 
     # propertives
     def setproperties(self, path, properties):
         path = self._sqlite_encode_path(path)
-        self.storage.execute("DELETE FROM property WHERE path = ?", (path,),
-                             False)
-        self.storage.execute("INSERT INTO property(path, properties) VALUES "
-                             "(?, ?)", (path,
-                                        self._sqlite_encode(properties)))
+        self._storage.execute("DELETE FROM property WHERE path = ?", (path,),
+                              False)
+        self._storage.execute("INSERT INTO property(path, properties) VALUES "
+                              "(?, ?)", (path,
+                                         self._sqlite_encode(properties)))
 
     def getproperties(self, path, default_properties):
         path = self._sqlite_encode_path(path)
-        value = self.storage.select("SELECT properties FROM property WHERE "
-                                    "path = ?", (path,))
+        value = self._storage.select("SELECT properties FROM property WHERE "
+                                     "path = ?", (path,))
         if value is None:
             return set(default_properties)
         else:
@@ -53,42 +53,53 @@ class Settings(Sqlite3DB):
 
     def hasproperties(self, path):
         path = self._sqlite_encode_path(path)
-        return self.storage.select("SELECT properties FROM property WHERE "
-                                   "path = ?", (path,)) is not None
+        return self._storage.select("SELECT properties FROM property WHERE "
+                                    "path = ?", (path,)) is not None
 
     def reset_all_propertives(self):
-        self.storage.execute("DELETE FROM property")
+        self._storage.execute("DELETE FROM property")
 
     def reset_properties(self, path):
         path = self._sqlite_encode_path(path)
-        self.storage.execute("DELETE FROM property WHERE path = ?", (path,))
-
-    def get_properties(self, context):
-        """return all properties in a dictionary
-        """
-        ret = {}
-        for path, properties in self.storage.select("SELECT * FROM property",
-                                                    only_one=False):
-            path = self._sqlite_decode_path(path)
-            properties = self._sqlite_decode(properties)
-            ret[path] = properties
-        return ret
+        self._storage.execute("DELETE FROM property WHERE path = ?", (path,))
 
     # permissive
     def setpermissive(self, path, permissive):
         path = self._sqlite_encode_path(path)
-        self.storage.execute("DELETE FROM permissive WHERE path = ?", (path,),
-                             False)
-        self.storage.execute("INSERT INTO permissive(path, permissives) "
-                             "VALUES (?, ?)", (path,
-                                               self._sqlite_encode(permissive)
-                                               ))
+        self._storage.execute("DELETE FROM permissive WHERE path = ?", (path,),
+                              False)
+        self._storage.execute("INSERT INTO permissive(path, permissives) "
+                              "VALUES (?, ?)", (path,
+                                                self._sqlite_encode(permissive)
+                                                ))
 
     def getpermissive(self, path='_none'):
-        permissives = self.storage.select("SELECT permissives FROM "
-                                          "permissive WHERE path = ?",
-                                          (path,))
+        permissives = self._storage.select("SELECT permissives FROM "
+                                           "permissive WHERE path = ?",
+                                           (path,))
         if permissives is None:
             return frozenset()
         else:
             return frozenset(self._sqlite_decode(permissives[0]))
+
+    def get_modified_properties(self):
+        """return all modified settings in a dictionary
+        example: {'path1': set(['prop1', 'prop2'])}
+        """
+        ret = {}
+        for path, properties in self._storage.select("SELECT * FROM property",
+                                                     only_one=False):
+            path = self._sqlite_decode_path(path)
+            ret[path] = self._sqlite_decode(properties)
+        return ret
+
+    def get_modified_permissives(self):
+        """return all modified permissives in a dictionary
+        example: {'path1': set(['perm1', 'perm2'])}
+        """
+        ret = {}
+        for path, permissives in self._storage.select("SELECT * FROM permissive",
+                                                      only_one=False):
+            path = self._sqlite_decode_path(path)
+            ret[path] = self._sqlite_decode(permissives)
+        return ret
index 9a967cd..68f2886 100644 (file)
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #
 # ____________________________________________________________
-from cPickle import loads, dumps
-from ..cache import Cache
+try:
+    from cPickle import loads, dumps
+except ImportError:
+    from pickle import loads, dumps
+from ..util import Cache
 
 
 class Sqlite3DB(Cache):
index 2ab8e08..3b4f265 100644 (file)
@@ -22,9 +22,10 @@ from os import unlink
 from os.path import basename, splitext, join
 import sqlite3
 from glob import glob
+from ..util import SerializeObject
 
 
-class Setting(object):
+class Setting(SerializeObject):
     """:param extension: database file extension (by default: db)
     :param dir_database: root database directory (by default: /tmp)
     """
@@ -52,13 +53,17 @@ def delete_session(session_id):
 
 
 class Storage(object):
-    __slots__ = ('_conn', '_cursor', 'persistent', '_session_id')
+    __slots__ = ('_conn', '_cursor', 'persistent', 'session_id', 'serializable')
     storage = 'sqlite3'
 
-    def __init__(self, session_id, persistent):
+    def __init__(self, session_id, persistent, test=False):
         self.persistent = persistent
-        self._session_id = session_id
-        self._conn = sqlite3.connect(_gen_filename(self._session_id))
+        if self.persistent:
+            self.serializable = True
+        else:
+            self.serializable = False
+        self.session_id = session_id
+        self._conn = sqlite3.connect(_gen_filename(self.session_id))
         self._conn.text_factory = str
         self._cursor = self._conn.cursor()
 
@@ -80,4 +85,4 @@ class Storage(object):
         self._cursor.close()
         self._conn.close()
         if not self.persistent:
-            delete_session(self._session_id)
+            delete_session(self.session_id)
index 3f76e2c..672ecab 100644 (file)
@@ -32,11 +32,11 @@ class Values(Sqlite3DB):
         super(Values, self).__init__(storage)
         values_table = 'CREATE TABLE IF NOT EXISTS value(path text primary '
         values_table += 'key, value text, owner text)'
-        self.storage.execute(values_table, commit=False)
+        self._storage.execute(values_table, commit=False)
         informations_table = 'CREATE TABLE IF NOT EXISTS information(key text primary '
         informations_table += 'key, value text)'
-        self.storage.execute(informations_table)
-        for owner in self.storage.select("SELECT DISTINCT owner FROM value", tuple(), False):
+        self._storage.execute(informations_table)
+        for owner in self._storage.select("SELECT DISTINCT owner FROM value", tuple(), False):
             try:
                 getattr(owners, owner[0])
             except AttributeError:
@@ -44,7 +44,7 @@ class Values(Sqlite3DB):
 
     # sqlite
     def _sqlite_select(self, path):
-        return self.storage.select("SELECT value FROM value WHERE path = ?",
+        return self._storage.select("SELECT value FROM value WHERE path = ?",
                                    (path,))
 
     # value
@@ -54,7 +54,7 @@ class Values(Sqlite3DB):
         """
         self.resetvalue(path)
         path = self._sqlite_encode_path(path)
-        self.storage.execute("INSERT INTO value(path, value, owner) VALUES "
+        self._storage.execute("INSERT INTO value(path, value, owner) VALUES "
                              "(?, ?, ?)", (path, self._sqlite_encode(value),
                                            str(owner)))
 
@@ -76,14 +76,14 @@ class Values(Sqlite3DB):
         """remove value means delete value in storage
         """
         path = self._sqlite_encode_path(path)
-        self.storage.execute("DELETE FROM value WHERE path = ?", (path,))
+        self._storage.execute("DELETE FROM value WHERE path = ?", (path,))
 
     def get_modified_values(self):
         """return all values in a dictionary
         example: {option1: (owner, 'value1'), option2: (owner, 'value2')}
         """
         ret = {}
-        for path, value, owner in self.storage.select("SELECT * FROM value",
+        for path, value, owner in self._storage.select("SELECT * FROM value",
                                                       only_one=False):
             path = self._sqlite_decode_path(path)
             owner = getattr(owners, owner)
@@ -97,7 +97,7 @@ class Values(Sqlite3DB):
         """change owner for an option
         """
         path = self._sqlite_encode_path(path)
-        self.storage.execute("UPDATE value SET owner = ? WHERE path = ?",
+        self._storage.execute("UPDATE value SET owner = ? WHERE path = ?",
                              (str(owner), path))
 
     def getowner(self, path, default):
@@ -105,7 +105,7 @@ class Values(Sqlite3DB):
         return: owner object
         """
         path = self._sqlite_encode_path(path)
-        owner = self.storage.select("SELECT owner FROM value WHERE path = ?",
+        owner = self._storage.select("SELECT owner FROM value WHERE path = ?",
                                     (path,))
         if owner is None:
             return default
@@ -125,9 +125,9 @@ class Values(Sqlite3DB):
         :param key: information's key (ex: "help", "doc"
         :param value: information's value (ex: "the help string")
         """
-        self.storage.execute("DELETE FROM information WHERE key = ?", (key,),
+        self._storage.execute("DELETE FROM information WHERE key = ?", (key,),
                              False)
-        self.storage.execute("INSERT INTO information(key, value) VALUES "
+        self._storage.execute("INSERT INTO information(key, value) VALUES "
                              "(?, ?)", (key, self._sqlite_encode(value)))
 
     def get_information(self, key):
@@ -135,7 +135,7 @@ class Values(Sqlite3DB):
 
         :param key: the item string (ex: "help")
         """
-        value = self.storage.select("SELECT value FROM information WHERE key = ?",
+        value = self._storage.select("SELECT value FROM information WHERE key = ?",
                                     (key,))
         if value is None:
             raise ValueError("not found")
diff --git a/tiramisu/storage/util.py b/tiramisu/storage/util.py
new file mode 100644 (file)
index 0000000..68482e6
--- /dev/null
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 -*-
+"default plugin for cache: set it in a simple dictionary"
+# 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 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 tiramisu.setting import owners
+
+
+class SerializeObject(object):
+    def __getstate__(self):
+        ret = {}
+        for key in dir(self):
+            if not key.startswith('__'):
+                ret[key] = getattr(self, key)
+        return ret
+
+
+class Cache(object):
+    __slots__ = ('_cache', '_storage')
+    key_is_path = False
+
+    def __init__(self, storage):
+        self._cache = {}
+        self._storage = storage
+
+    def __getstate__(self):
+        slots = set()
+        for subclass in self.__class__.__mro__:
+            if subclass is not object:
+                slots.update(subclass.__slots__)
+        slots -= frozenset(['__weakref__', '_storage'])
+        states = {}
+        for slot in slots:
+            try:
+                value = getattr(self, slot)
+                #value has owners object, need 'str()' it
+                if slot == '_values':
+                    _value = {}
+                    for key, values in value.items():
+                        vals = list(values)
+                        vals[0] = str(vals[0])
+                        _value[key] = tuple(vals)
+                    states[slot] = _value
+                else:
+                    states[slot] = value
+            except AttributeError:
+                pass
+        return states
+
+    def __setstate__(self, states):
+        for key, value in states.items():
+            #value has owners object, need to reconstruct it
+            if key == '_values':
+                _value = {}
+                for key_, values_ in value.items():
+                    vals = list(values_)
+                    try:
+                        vals[0] = getattr(owners, vals[0])
+                    except AttributeError:
+                        owners.addowner(vals[0])
+                        vals[0] = getattr(owners, vals[0])
+                    _value[key_] = tuple(vals)
+                value = _value
+            setattr(self, key, value)
+
+    def setcache(self, path, val, time):
+        self._cache[path] = (val, time)
+
+    def getcache(self, path, exp):
+        value, created = self._cache[path]
+        if created is None or exp <= created:
+            return True, value
+        return False, None
+
+    def hascache(self, path):
+        """ path is in the cache
+
+        :param path: the path's option
+        """
+        return path in self._cache
+
+    def reset_expired_cache(self, exp):
+        for key in tuple(self._cache.keys()):
+            val, created = self._cache[key]
+            if created is not None and exp > created:
+                del(self._cache[key])
+
+    def reset_all_cache(self):
+        "empty the cache"
+        self._cache.clear()
+
+    def get_cached(self, context):
+        """return all values in a dictionary
+        example: {'path1': ('value1', 'time1'), 'path2': ('value2', 'time2')}
+        """
+        return self._cache
index c6f0e76..043f15f 100644 (file)
@@ -204,7 +204,9 @@ class Values(object):
             if not no_value_slave:
                 try:
                     value = self._getcallback_value(opt, max_len=lenmaster)
-                except ConfigError as config_error:
+                except ConfigError as err:
+                    # cannot assign config_err directly in python 3.3
+                    config_error = err
                     value = None
                     # should not raise PropertiesOptionError if option is
                     # mandatory
@@ -359,6 +361,14 @@ class Values(object):
                 raise ValueError(_("information's item"
                                    " not found: {0}").format(key))
 
+    def __getstate__(self):
+        return {'_p_': self._p_}
+
+    def _impl_setstate(self, storage):
+        self._p_._storage = storage
+
+    def __setstate__(self, states):
+        self._p_ = states['_p_']
 
 # ____________________________________________________________
 # multi types
@@ -386,7 +396,7 @@ class Multi(list):
             value = [value]
         if validate and self.opt.impl_get_multitype() == multitypes.slave:
             value = self._valid_slave(value, setitem)
-        elif self.opt.impl_get_multitype() == multitypes.master:
+        elif validate and self.opt.impl_get_multitype() == multitypes.master:
             self._valid_master(value)
         super(Multi, self).__init__(value)