add persistent option for db

This commit is contained in:
Emmanuel Garette 2013-08-20 22:45:11 +02:00
parent d971448d02
commit 0d5a447eb3
12 changed files with 124 additions and 86 deletions

View File

@ -24,11 +24,15 @@ from time import time
from tiramisu.error import PropertiesOptionError, ConfigError from tiramisu.error import PropertiesOptionError, ConfigError
from tiramisu.option import OptionDescription, Option, SymLinkOption, \ from tiramisu.option import OptionDescription, Option, SymLinkOption, \
BaseInformation BaseInformation
from tiramisu.setting import groups, Settings, default_encoding, default_storage from tiramisu.setting import groups, Settings, default_encoding, storage_type
from tiramisu.value import Values from tiramisu.value import Values
from tiramisu.i18n import _ from tiramisu.i18n import _
def gen_id(config):
return str(id(config)) + str(time())
class SubConfig(BaseInformation): class SubConfig(BaseInformation):
"sub configuration management entry" "sub configuration management entry"
__slots__ = ('_impl_context', '_impl_descr') __slots__ = ('_impl_context', '_impl_descr')
@ -473,6 +477,13 @@ class CommonConfig(SubConfig):
"abstract base class for the Config and the MetaConfig" "abstract base class for the Config and the MetaConfig"
__slots__ = ('_impl_values', '_impl_settings', '_impl_meta') __slots__ = ('_impl_values', '_impl_settings', '_impl_meta')
def _init_storage(self, config_id, is_persistent):
if config_id is None:
config_id = gen_id(self)
import_lib = 'tiramisu.storage.{0}.storage'.format(storage_type)
return __import__(import_lib, globals(), locals(), ['Storage'],
-1).Storage(config_id, is_persistent)
def _impl_build_all_paths(self): def _impl_build_all_paths(self):
self.cfgimpl_get_description().impl_build_cache() self.cfgimpl_get_description().impl_build_cache()
@ -517,7 +528,7 @@ class Config(CommonConfig):
"main configuration management entry" "main configuration management entry"
__slots__ = tuple() __slots__ = tuple()
def __init__(self, descr): def __init__(self, descr, config_id=None, is_persistent=False):
""" Configuration option management master class """ Configuration option management master class
:param descr: describes the configuration schema :param descr: describes the configuration schema
@ -525,9 +536,9 @@ class Config(CommonConfig):
:param context: the current root config :param context: the current root config
:type context: `Config` :type context: `Config`
""" """
config_id = str(id(self)) + str(time()) storage = self._init_storage(config_id, is_persistent)
self._impl_settings = Settings(self, config_id, default_storage) self._impl_settings = Settings(self, storage)
self._impl_values = Values(self, config_id, default_storage) self._impl_values = Values(self, storage)
super(Config, self).__init__(descr, self) super(Config, self).__init__(descr, self)
self._impl_build_all_paths() self._impl_build_all_paths()
self._impl_meta = None self._impl_meta = None
@ -545,7 +556,7 @@ class Config(CommonConfig):
class MetaConfig(CommonConfig): class MetaConfig(CommonConfig):
__slots__ = ('_impl_children',) __slots__ = ('_impl_children',)
def __init__(self, children, meta=True): def __init__(self, children, meta=True, config_id=None, is_persistent=False):
if not isinstance(children, list): if not isinstance(children, list):
raise ValueError(_("metaconfig's children must be a list")) raise ValueError(_("metaconfig's children must be a list"))
self._impl_descr = None self._impl_descr = None
@ -564,10 +575,12 @@ class MetaConfig(CommonConfig):
raise ValueError(_("child has already a metaconfig's")) raise ValueError(_("child has already a metaconfig's"))
child._impl_meta = self child._impl_meta = self
config_id = str(id(self)) if config_id is None:
config_id = gen_id(self)
self._impl_children = children self._impl_children = children
self._impl_settings = Settings(self, config_id, default_storage) storage = self._init_storage(config_id, is_persistent)
self._impl_values = Values(self, config_id, default_storage) self._impl_settings = Settings(self, storage)
self._impl_values = Values(self, storage)
self._impl_meta = None self._impl_meta = None
self._impl_informations = {} self._impl_informations = {}

View File

@ -34,7 +34,7 @@ ro_append = ('frozen', 'disabled', 'validator', 'everything_frozen',
rw_remove = ('permissive', 'everything_frozen', 'mandatory') rw_remove = ('permissive', 'everything_frozen', 'mandatory')
rw_append = ('frozen', 'disabled', 'validator', 'hidden') rw_append = ('frozen', 'disabled', 'validator', 'hidden')
default_properties = ('expire', 'validator') default_properties = ('expire', 'validator')
default_storage = 'dictionary' storage_type = 'dictionary'
class _const: class _const:
@ -183,13 +183,13 @@ class Settings(object):
"``Config()``'s configuration options" "``Config()``'s configuration options"
__slots__ = ('context', '_owner', '_p_') __slots__ = ('context', '_owner', '_p_')
def __init__(self, context, config_id, plugin_name): def __init__(self, context, storage):
# generic owner # generic owner
self._owner = owners.user self._owner = owners.user
self.context = context self.context = context
import_lib = 'tiramisu.plugins.{0}.setting'.format(plugin_name) import_lib = 'tiramisu.storage.{0}.setting'.format(storage_type)
self._p_ = __import__(import_lib, globals(), locals(), ['Settings'], self._p_ = __import__(import_lib, globals(), locals(), ['Settings'],
-1).Settings(config_id) -1).Settings(storage)
def _getkey(self, opt): def _getkey(self, opt):
if self._p_.key_is_path: if self._p_.key_is_path:

View File

@ -17,13 +17,13 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.plugins.dictionary.cache import Cache from tiramisu.storage.dictionary.storage import Cache
class Settings(Cache): class Settings(Cache):
__slots__ = ('_properties', '_permissives') __slots__ = ('_properties', '_permissives')
def __init__(self, config_id): def __init__(self, storage):
# properties attribute: the name of a property enables this property # properties attribute: the name of a property enables this property
# key is None for global properties # key is None for global properties
self._properties = {} self._properties = {}

View File

@ -19,6 +19,16 @@
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.i18n import _
class Storage(object):
__slots__ = tuple()
def __init__(self, config_id, is_persistent):
if is_persistent:
raise ValueError(_('a dictionary cannot be persistent'))
class Cache(object): class Cache(object):
__slots__ = ('_cache',) __slots__ = ('_cache',)
key_is_path = False key_is_path = False

View File

@ -18,14 +18,13 @@
# #
# ____________________________________________________________ # ____________________________________________________________
#FIXME from tiramisu.storage.dictionary.storage import Cache
from tiramisu.plugins.dictionary.cache import Cache
class Values(Cache): class Values(Cache):
__slots__ = ('_values',) __slots__ = ('_values',)
def __init__(self, config_id): def __init__(self, storage):
"""init plugin means create values storage """init plugin means create values storage
""" """
self._values = {} self._values = {}

View File

@ -17,53 +17,47 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.plugins.sqlite3.cache import Cache from tiramisu.storage.sqlite3.storage import Cache
class Settings(Cache): class Settings(Cache):
__slots__ = tuple() __slots__ = tuple()
def __init__(self, config_id): def __init__(self, storage):
settings_table = 'CREATE TABLE IF NOT EXISTS property(path text primary key, properties text)' settings_table = 'CREATE TABLE IF NOT EXISTS property(path text primary key, properties text)'
permissives_table = 'CREATE TABLE IF NOT EXISTS permissive(path text primary key, permissives text)' permissives_table = 'CREATE TABLE IF NOT EXISTS permissive(path text primary key, permissives text)'
# should init cache too # should init cache too
super(Settings, self).__init__(config_id, 'property') super(Settings, self).__init__('property', storage)
self._cursor.execute(settings_table) self.storage.execute(settings_table, commit=False)
self._cursor.execute(permissives_table) self.storage.execute(permissives_table)
self._conn.commit()
# propertives # propertives
def setproperties(self, path, properties): def setproperties(self, path, properties):
self._cursor.execute("DELETE FROM property WHERE path = ?", (path,)) self.storage.execute("DELETE FROM property WHERE path = ?", (path,), False)
self._cursor.execute("INSERT INTO property(path, properties) VALUES (?, ?)", self.storage.execute("INSERT INTO property(path, properties) VALUES (?, ?)",
(path, self._sqlite_encode(properties))) (path, self._sqlite_encode(properties)))
self._conn.commit()
def getproperties(self, path, default_properties): def getproperties(self, path, default_properties):
self._cursor.execute("SELECT properties FROM property WHERE path = ?", (path,)) value = self.storage.select("SELECT properties FROM property WHERE path = ?", (path,))
value = self._cursor.fetchone()
if value is None: if value is None:
return set(default_properties) return set(default_properties)
else: else:
return set(self._sqlite_decode(value[0])) return set(self._sqlite_decode(value[0]))
def hasproperties(self, path): def hasproperties(self, path):
return self._cursor.execute("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): def reset_all_propertives(self):
self._cursor.execute("DELETE FROM property") self.storage.execute("DELETE FROM property")
self._conn.commit()
def reset_properties(self, path): def reset_properties(self, path):
self._cursor.execute("DELETE FROM property WHERE path = ?", (path,)) self.storage.execute("DELETE FROM property WHERE path = ?", (path,))
self._conn.commit()
def get_properties(self, context): def get_properties(self, context):
"""return all properties in a dictionary """return all properties in a dictionary
""" """
self._cursor.execute("SELECT * FROM property")
ret = {} ret = {}
for path, properties in self._cursor.fetchall(): for path, properties in self.storage.select("SELECT * FROM property", only_one=False):
if path == '_none': if path == '_none':
opt = None opt = None
else: else:
@ -74,14 +68,12 @@ class Settings(Cache):
# permissive # permissive
def setpermissive(self, path, permissive): def setpermissive(self, path, permissive):
self._cursor.execute("DELETE FROM permissive WHERE path = ?", (path,)) self.storage.execute("DELETE FROM permissive WHERE path = ?", (path,), False)
self._cursor.execute("INSERT INTO permissive(path, permissives) VALUES (?, ?)", self.storage.execute("INSERT INTO permissive(path, permissives) VALUES (?, ?)",
(path, self._sqlite_encode(permissive))) (path, self._sqlite_encode(permissive)))
self._conn.commit()
def getpermissive(self, path='_none'): def getpermissive(self, path='_none'):
self._cursor.execute("SELECT permissives FROM permissive WHERE path = ?", (path,)) permissives = self.storage.select("SELECT permissives FROM permissive WHERE path = ?", (path,))
permissives = self._cursor.fetchone()
if permissives is None: if permissives is None:
return frozenset() return frozenset()
else: else:

View File

@ -19,19 +19,51 @@
# ____________________________________________________________ # ____________________________________________________________
from pickle import dumps, loads from pickle import dumps, loads
from os import unlink
import sqlite3 import sqlite3
class Cache(object): class Storage(object):
__slots__ = ('_conn', '_cursor') __slots__ = ('_conn', '_cursor', 'is_persistent', 'db_file')
key_is_path = True
def __init__(self, config_id, cache_type): def __init__(self, config_id, is_persistent):
cache_table = 'CREATE TABLE IF NOT EXISTS cache_{0}(path text primary key, value text, time real)'.format(cache_type) self.is_persistent = is_persistent
self._conn = sqlite3.connect(config_id + '.db') self.db_file = config_id + '.db'
self._conn = sqlite3.connect(self.db_file)
self._conn.text_factory = str self._conn.text_factory = str
self._cursor = self._conn.cursor() self._cursor = self._conn.cursor()
self._cursor.execute(cache_table)
def execute(self, sql, params=None, commit=True):
if params is None:
params = tuple()
self._cursor.execute(sql, params)
if commit:
self._conn.commit()
def select(self, sql, params=None, only_one=True):
self.execute(sql, params=params, commit=False)
if only_one:
return self._cursor.fetchone()
else:
return self._cursor.fetchall()
def __del__(self):
self._cursor.close()
self._conn.close()
if not self.is_persistent:
unlink(self.db_file)
class Cache(object):
__slots__ = ('storage',)
key_is_path = True
def __init__(self, cache_type, storage):
self.storage = storage
cache_table = 'CREATE TABLE IF NOT EXISTS cache_{0}(path '.format(
cache_type)
cache_table += 'text primary key, value text, time real)'
self.storage.execute(cache_table)
# value # value
def _sqlite_decode(self, value): def _sqlite_decode(self, value):
@ -44,38 +76,41 @@ class Cache(object):
def setcache(self, cache_type, path, val, time): def setcache(self, cache_type, path, val, time):
convert_value = self._sqlite_encode(val) convert_value = self._sqlite_encode(val)
self._cursor.execute("DELETE FROM cache_{0} WHERE path = ?".format(cache_type), (path,)) self.storage.execute("DELETE FROM cache_{0} WHERE path = ?".format(
self._cursor.execute("INSERT INTO cache_{0}(path, value, time) VALUES (?, ?, ?)".format(cache_type), cache_type), (path,), False)
self.storage.execute("INSERT INTO cache_{0}(path, value, time) "
"VALUES (?, ?, ?)".format(cache_type),
(path, convert_value, time)) (path, convert_value, time))
self._conn.commit()
def getcache(self, cache_type, path, exp): def getcache(self, cache_type, path, exp):
self._cursor.execute("SELECT value FROM cache_{0} WHERE path = ? AND time >= ?".format(cache_type), (path, exp)) cached = self.storage.select("SELECT value FROM cache_{0} WHERE "
cached = self._cursor.fetchone() "path = ? AND time >= ?".format(
cache_type), (path, exp))
if cached is None: if cached is None:
return False, None return False, None
else: else:
return True, self._sqlite_decode(cached[0]) return True, self._sqlite_decode(cached[0])
def hascache(self, cache_type, path): def hascache(self, cache_type, path):
self._cursor.execute("SELECT value FROM cache_{0} WHERE path = ?".format(cache_type), (path,)) return self.storage.select("SELECT value FROM cache_{0} WHERE "
return self._cursor.fetchone() is not None "path = ?".format(cache_type),
(path,)) is not None
def reset_expired_cache(self, cache_type, exp): def reset_expired_cache(self, cache_type, exp):
self._cursor.execute("DELETE FROM cache_{0} WHERE time < ?".format(cache_type), (exp,)) self.storage.execute("DELETE FROM cache_{0} WHERE time < ?".format(
self._conn.commit() cache_type), (exp,))
def reset_all_cache(self, cache_type): def reset_all_cache(self, cache_type):
self._cursor.execute("DELETE FROM cache_{0}".format(cache_type)) self.storage.execute("DELETE FROM cache_{0}".format(cache_type))
self._conn.commit()
def get_cached(self, cache_type, context): def get_cached(self, cache_type, context):
"""return all values in a dictionary """return all values in a dictionary
example: {option1: ('value1', 'time1'), option2: ('value2', 'time2')} example: {option1: ('value1', 'time1'), option2: ('value2', 'time2')}
""" """
self._cursor.execute("SELECT * FROM cache_{0}".format(cache_type))
ret = {} ret = {}
for path, value, time in self._cursor.fetchall(): for path, value, time in self.storage.select("SELECT * FROM cache_{0}"
"".format(cache_type),
only_one=False):
opt = context.cfgimpl_get_description().impl_get_opt_by_path(path) opt = context.cfgimpl_get_description().impl_get_opt_by_path(path)
value = self._sqlite_decode(value) value = self._sqlite_decode(value)
ret[opt] = (value, time) ret[opt] = (value, time)

View File

@ -18,26 +18,24 @@
# #
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.plugins.sqlite3.cache import Cache from tiramisu.storage.sqlite3.storage import Cache
from tiramisu.setting import owners from tiramisu.setting import owners
class Values(Cache): class Values(Cache):
__slots__ = tuple() __slots__ = tuple()
def __init__(self, config_id): def __init__(self, storage):
"""init plugin means create values storage """init plugin means create values storage
""" """
values_table = 'CREATE TABLE IF NOT EXISTS value(path text primary key, value text, owner text)' values_table = 'CREATE TABLE IF NOT EXISTS value(path text primary key, value text, owner text)'
# should init cache too # should init cache too
super(Values, self).__init__(config_id, 'value') super(Values, self).__init__('value', storage)
self._cursor.execute(values_table) self.storage.execute(values_table)
self._conn.commit()
# sqlite # sqlite
def _sqlite_select(self, path): def _sqlite_select(self, path):
self._cursor.execute("SELECT value FROM value WHERE path = ?", (path,)) return self.storage.select("SELECT value FROM value WHERE path = ?", (path,))
return self._cursor.fetchone()
# value # value
def setvalue(self, path, value, owner): def setvalue(self, path, value, owner):
@ -45,9 +43,8 @@ class Values(Cache):
a specified value must be associated to an owner a specified value must be associated to an owner
""" """
self.resetvalue(path) self.resetvalue(path)
self._cursor.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))) (path, self._sqlite_encode(value), str(owner)))
self._conn.commit()
def getvalue(self, path): def getvalue(self, path):
"""get value for an option """get value for an option
@ -64,16 +61,14 @@ class Values(Cache):
def resetvalue(self, path): def resetvalue(self, path):
"""remove value means delete value in storage """remove value means delete value in storage
""" """
self._cursor.execute("DELETE FROM value WHERE path = ?", (path,)) self.storage.execute("DELETE FROM value WHERE path = ?", (path,))
self._conn.commit()
def get_modified_values(self, context): def get_modified_values(self, context):
"""return all values in a dictionary """return all values in a dictionary
example: {option1: (owner, 'value1'), option2: (owner, 'value2')} example: {option1: (owner, 'value1'), option2: (owner, 'value2')}
""" """
self._cursor.execute("SELECT value")
ret = {} ret = {}
for path, value, owner in self._cursor.fetchall(): for path, value, owner in self.storage.select("SELECT value", only_one=False):
opt = context.cfgimpl_get_description().impl_get_opt_by_path(path) opt = context.cfgimpl_get_description().impl_get_opt_by_path(path)
owner = getattr(owners, owner) owner = getattr(owners, owner)
@ -85,20 +80,14 @@ class Values(Cache):
def setowner(self, path, owner): def setowner(self, path, owner):
"""change owner for an option """change owner for an option
""" """
self._cursor.execute("UPDATE value SET owner = ? WHERE path = ?", (str(owner), path)) self.storage.execute("UPDATE value SET owner = ? WHERE path = ?", (str(owner), path))
self._conn.commit()
def getowner(self, path, default): def getowner(self, path, default):
"""get owner for an option """get owner for an option
return: owner object return: owner object
""" """
self._cursor.execute("SELECT owner FROM value WHERE path = ?", (path,)) owner = self.storage.select("SELECT owner FROM value WHERE path = ?", (path,))
owner = self._cursor.fetchone()
if owner is None: if owner is None:
return default return default
else: else:
return getattr(owners, owner[0]) return getattr(owners, owner[0])
def __del__(self):
self._cursor.close()
self._conn.close()

View File

@ -20,7 +20,7 @@
from time import time from time import time
from copy import copy from copy import copy
from tiramisu.error import ConfigError, SlaveError from tiramisu.error import ConfigError, SlaveError
from tiramisu.setting import owners, multitypes, expires_time from tiramisu.setting import owners, multitypes, expires_time, storage_type
from tiramisu.autolib import carry_out_calculation from tiramisu.autolib import carry_out_calculation
from tiramisu.i18n import _ from tiramisu.i18n import _
from tiramisu.option import SymLinkOption from tiramisu.option import SymLinkOption
@ -33,7 +33,7 @@ class Values(object):
""" """
__slots__ = ('context', '_p_') __slots__ = ('context', '_p_')
def __init__(self, context, config_id, plugin_name): def __init__(self, context, storage):
""" """
Initializes the values's dict. Initializes the values's dict.
@ -42,9 +42,9 @@ class Values(object):
""" """
self.context = context self.context = context
import_lib = 'tiramisu.plugins.{0}.value'.format(plugin_name) import_lib = 'tiramisu.storage.{0}.value'.format(storage_type)
self._p_ = __import__(import_lib, globals(), locals(), ['Values'], self._p_ = __import__(import_lib, globals(), locals(), ['Values'],
-1).Values(config_id) -1).Values(storage)
def _getkey(self, opt): def _getkey(self, opt):
if self._p_.key_is_path: if self._p_.key_is_path: