From c84d13a1c6c59e82d570d6c6fe645f4035528423 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 22 Sep 2013 20:57:52 +0200 Subject: [PATCH] we can serialize Config now --- test/test_option_setting.py | 18 ++--- test/test_state.py | 96 +++++++++++++++++++++++++ tiramisu/config.py | 42 ++++++++++- tiramisu/option.py | 27 ++----- tiramisu/setting.py | 22 +++++- tiramisu/storage/__init__.py | 25 +++++-- tiramisu/storage/dictionary/__init__.py | 4 +- tiramisu/storage/dictionary/setting.py | 17 +++-- tiramisu/storage/dictionary/storage.py | 12 ++-- tiramisu/storage/dictionary/value.py | 2 +- tiramisu/storage/sqlite3/__init__.py | 4 +- tiramisu/storage/sqlite3/setting.py | 77 +++++++++++--------- tiramisu/storage/sqlite3/sqlite3db.py | 7 +- tiramisu/storage/sqlite3/storage.py | 17 +++-- tiramisu/storage/sqlite3/value.py | 24 +++---- tiramisu/storage/{cache.py => util.py} | 54 +++++++++++++- tiramisu/value.py | 14 +++- 17 files changed, 354 insertions(+), 108 deletions(-) rename tiramisu/storage/{cache.py => util.py} (51%) diff --git a/test/test_option_setting.py b/test/test_option_setting.py index 2dc76ab..ed5f7d7 100644 --- a/test/test_option_setting.py +++ b/test/test_option_setting.py @@ -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') diff --git a/test/test_state.py b/test/test_state.py index ea1956c..8587b4a 100644 --- a/test/test_state.py +++ b/test/test_state.py @@ -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 diff --git a/tiramisu/config.py b/tiramisu/config.py index fac3caf..d817fa1 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -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, diff --git a/tiramisu/option.py b/tiramisu/option.py index 89d960a..10aba5c 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -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. diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 531e846..684ec74 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -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']) diff --git a/tiramisu/storage/__init__.py b/tiramisu/storage/__init__.py index 1394258..c232472 100644 --- a/tiramisu/storage/__init__.py +++ b/tiramisu/storage/__init__.py @@ -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/dictionary/__init__.py b/tiramisu/storage/dictionary/__init__.py index dadce23..bc81450 100644 --- a/tiramisu/storage/dictionary/__init__.py +++ b/tiramisu/storage/dictionary/__init__.py @@ -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) diff --git a/tiramisu/storage/dictionary/setting.py b/tiramisu/storage/dictionary/setting.py index 706ab2a..1b7001b 100644 --- a/tiramisu/storage/dictionary/setting.py +++ b/tiramisu/storage/dictionary/setting.py @@ -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 diff --git a/tiramisu/storage/dictionary/storage.py b/tiramisu/storage/dictionary/storage.py index 6e15c1b..465fe26 100644 --- a/tiramisu/storage/dictionary/storage.py +++ b/tiramisu/storage/dictionary/storage.py @@ -18,9 +18,10 @@ # ____________________________________________________________ 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): diff --git a/tiramisu/storage/dictionary/value.py b/tiramisu/storage/dictionary/value.py index c435d06..fedf1ec 100644 --- a/tiramisu/storage/dictionary/value.py +++ b/tiramisu/storage/dictionary/value.py @@ -18,7 +18,7 @@ # # ____________________________________________________________ -from ..cache import Cache +from ..util import Cache class Values(Cache): diff --git a/tiramisu/storage/sqlite3/__init__.py b/tiramisu/storage/sqlite3/__init__.py index dc6c14b..8d79070 100644 --- a/tiramisu/storage/sqlite3/__init__.py +++ b/tiramisu/storage/sqlite3/__init__.py @@ -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) diff --git a/tiramisu/storage/sqlite3/setting.py b/tiramisu/storage/sqlite3/setting.py index 720849b..ed79181 100644 --- a/tiramisu/storage/sqlite3/setting.py +++ b/tiramisu/storage/sqlite3/setting.py @@ -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 diff --git a/tiramisu/storage/sqlite3/sqlite3db.py b/tiramisu/storage/sqlite3/sqlite3db.py index 9a967cd..68f2886 100644 --- a/tiramisu/storage/sqlite3/sqlite3db.py +++ b/tiramisu/storage/sqlite3/sqlite3db.py @@ -17,8 +17,11 @@ # 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): diff --git a/tiramisu/storage/sqlite3/storage.py b/tiramisu/storage/sqlite3/storage.py index 2ab8e08..3b4f265 100644 --- a/tiramisu/storage/sqlite3/storage.py +++ b/tiramisu/storage/sqlite3/storage.py @@ -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) diff --git a/tiramisu/storage/sqlite3/value.py b/tiramisu/storage/sqlite3/value.py index 3f76e2c..672ecab 100644 --- a/tiramisu/storage/sqlite3/value.py +++ b/tiramisu/storage/sqlite3/value.py @@ -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/cache.py b/tiramisu/storage/util.py similarity index 51% rename from tiramisu/storage/cache.py rename to tiramisu/storage/util.py index 347d270..68482e6 100644 --- a/tiramisu/storage/cache.py +++ b/tiramisu/storage/util.py @@ -17,15 +17,65 @@ # 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') + __slots__ = ('_cache', '_storage') key_is_path = False def __init__(self, storage): self._cache = {} - self.storage = storage + 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) diff --git a/tiramisu/value.py b/tiramisu/value.py index c6f0e76..043f15f 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -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)