update sqlite storage

This commit is contained in:
Emmanuel Garette 2017-07-04 19:59:42 +02:00
parent 6f030d426b
commit 6bad3c6e64
17 changed files with 365 additions and 134 deletions

View File

@ -2,8 +2,9 @@
from autopath import do_autopath from autopath import do_autopath
do_autopath() do_autopath()
from tiramisu import setting from tiramisu import setting, value
setting.expires_time = 1 setting.expires_time = 1
value.expires_time = 1
from tiramisu.option import IntOption, StrOption, OptionDescription from tiramisu.option import IntOption, StrOption, OptionDescription
from tiramisu.config import Config from tiramisu.config import Config
from tiramisu.error import ConfigError from tiramisu.error import ConfigError

View File

@ -644,7 +644,7 @@ def test_consistency_master_and_slaves_master_mandatory_non_transitive():
maconfig = OptionDescription('rootconfig', '', [interface1, interface2]) maconfig = OptionDescription('rootconfig', '', [interface1, interface2])
cfg = Config(maconfig) cfg = Config(maconfig)
cfg.read_write() cfg.read_write()
assert list(cfg.cfgimpl_get_values().mandatory_warnings()) == ["val1.val1"] assert list(cfg.cfgimpl_get_values().mandatory_warnings()) == ["val1.val1", "val1.val2"]
def test_callback_master_and_slaves_master_list(): def test_callback_master_and_slaves_master_list():

View File

@ -692,3 +692,6 @@ def test_groups_with_master_get_modified_value():
assert cfg.cfgimpl_get_values().get_modified_values() == {'ip_admin_eth0.ip_admin_eth0': ('user', ('192.168.1.1',))} assert cfg.cfgimpl_get_values().get_modified_values() == {'ip_admin_eth0.ip_admin_eth0': ('user', ('192.168.1.1',))}
cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.255'] cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.255']
assert cfg.cfgimpl_get_values().get_modified_values() == {'ip_admin_eth0.ip_admin_eth0': ('user', ('192.168.1.1',)), 'ip_admin_eth0.netmask_admin_eth0': ({'0': 'user'}, {'0': '255.255.255.255'})} assert cfg.cfgimpl_get_values().get_modified_values() == {'ip_admin_eth0.ip_admin_eth0': ('user', ('192.168.1.1',)), 'ip_admin_eth0.netmask_admin_eth0': ({'0': 'user'}, {'0': '255.255.255.255'})}
cfg.ip_admin_eth0.ip_admin_eth0.append('192.168.1.1')
cfg.ip_admin_eth0.netmask_admin_eth0[-1] = '255.255.255.255'
assert cfg.cfgimpl_get_values().get_modified_values() == {'ip_admin_eth0.ip_admin_eth0': ('user', ('192.168.1.1', '192.168.1.1')), 'ip_admin_eth0.netmask_admin_eth0': ({'0': 'user', '1': 'user'}, {'0': '255.255.255.255', '1': '255.255.255.255'})}

View File

@ -6,7 +6,7 @@ do_autopath()
from tiramisu.config import Config from tiramisu.config import Config
from tiramisu.option import BoolOption, OptionDescription from tiramisu.option import BoolOption, OptionDescription
from tiramisu.setting import owners from tiramisu.setting import groups, owners
from tiramisu.storage import list_sessions, delete_session from tiramisu.storage import list_sessions, delete_session
@ -21,6 +21,7 @@ def test_list():
o = OptionDescription('od', '', [b]) o = OptionDescription('od', '', [b])
c = Config(o, session_id='test_non_persistent') c = Config(o, session_id='test_non_persistent')
c.cfgimpl_get_settings().remove('cache') c.cfgimpl_get_settings().remove('cache')
c.b = True
assert 'test_non_persistent' in list_sessions('config') assert 'test_non_persistent' in list_sessions('config')
del(c) del(c)
assert 'test_non_persistent' not in list_sessions('config') assert 'test_non_persistent' not in list_sessions('config')
@ -40,7 +41,8 @@ def test_list_sessions_persistent():
b = BoolOption('b', '') b = BoolOption('b', '')
o = OptionDescription('od', '', [b]) o = OptionDescription('od', '', [b])
try: try:
Config(o, session_id='test_persistent', persistent=True) c = Config(o, session_id='test_persistent', persistent=True)
c.b = True
except ValueError: except ValueError:
# storage is not persistent # storage is not persistent
pass pass
@ -139,6 +141,46 @@ def test_create_persistent_retrieve_owner():
del(c) del(c)
def test_create_persistent_retrieve_owner_masterslaves():
a = BoolOption('a', '', multi=True)
b = BoolOption('b', '', multi=True)
o = OptionDescription('a', '', [a, b])
o.impl_set_group_type(groups.master)
o1 = OptionDescription('a', '', [o])
try:
c = Config(o1, session_id='test_persistent', persistent=True)
except ValueError:
# storage is not persistent
pass
else:
assert c.getowner(a) == owners.default
assert c.getowner(b) == owners.default
c.a.a = [True]
c.a.a.append(False)
c.a.b[1] = True
assert c.getowner(a) == owners.user
assert c.getowner(b, 0) == owners.default
assert c.getowner(b, 1) == owners.user
owners.addowner('persistentowner2')
c.cfgimpl_get_values().setowner(b, owners.persistentowner2, 1)
c.a.b[0] = True
assert c.getowner(b, 0) == owners.user
assert c.getowner(b, 1) == owners.persistentowner2
del(c)
#
c = Config(o1, session_id='test_persistent', persistent=True)
assert c.getowner(b, 0) == owners.user
assert c.getowner(b, 1) == owners.persistentowner2
delete_session('config', c.impl_getsessionid())
del(c)
#
c = Config(o1, session_id='test_persistent', persistent=True)
assert c.a.b == []
assert c.getowner(b) == owners.default
delete_session('config', c.impl_getsessionid())
del(c)
def test_two_persistent_owner(): def test_two_persistent_owner():
b = BoolOption('b', '') b = BoolOption('b', '')
o = OptionDescription('od', '', [b]) o = OptionDescription('od', '', [b])
@ -204,3 +246,28 @@ def test_two_persistent_information():
c2.cfgimpl_get_settings().remove('cache') c2.cfgimpl_get_settings().remove('cache')
assert c2.impl_get_information('info') == 'string' assert c2.impl_get_information('info') == 'string'
delete_session('config', 'test_persistent') delete_session('config', 'test_persistent')
def test_two_different_persistents():
b = BoolOption('b', '')
o = OptionDescription('od', '', [b])
try:
c = Config(o, session_id='test_persistent', persistent=True)
c.cfgimpl_get_settings().remove('cache')
d = Config(o, session_id='test_persistent2', persistent=True)
d.cfgimpl_get_settings().remove('cache')
except ValueError:
# storage is not persistent
pass
else:
c.cfgimpl_get_settings()[b].append('test')
assert str(c.cfgimpl_get_settings()[b]) in ["['test']", "[u'test']"]
assert str(d.cfgimpl_get_settings()[b]) == "[]"
assert c.b is None
assert d.b is None
c.b = True
assert c.b == True
assert d.b is None
delete_session('config', 'test_persistent')
delete_session('config', 'test_persistent2')

View File

@ -87,25 +87,6 @@ class SubConfig(object):
return self, None return self, None
return self, path[-1] return self, path[-1]
#def __hash__(self):
#FIXME
# return hash(self.cfgimpl_get_description().impl_getkey(self))
#def __eq__(self, other):
#FIXME
# "Config's comparison"
# if not isinstance(other, Config):
# return False
# return self.cfgimpl_get_description().impl_getkey(self) == \
# other.cfgimpl_get_description().impl_getkey(other)
#def __ne__(self, other):
#FIXME
# "Config's comparison"
# if not isinstance(other, Config):
# return True
# return not self == other
# ______________________________________________________________________ # ______________________________________________________________________
def __iter__(self, force_permissive=False): def __iter__(self, force_permissive=False):
"""Pythonesque way of parsing group's ordered options. """Pythonesque way of parsing group's ordered options.
@ -287,6 +268,8 @@ class SubConfig(object):
option = self.cfgimpl_get_description().__getattr__(name, option = self.cfgimpl_get_description().__getattr__(name,
context=context) context=context)
subpath = self._get_subpath(name) subpath = self._get_subpath(name)
if _setting_properties is undefined:
_setting_properties = context.cfgimpl_get_settings()._getproperties(read_write=True)
if isinstance(option, DynSymLinkOption): if isinstance(option, DynSymLinkOption):
cfg = self.cfgimpl_get_values()._get_cached_value( cfg = self.cfgimpl_get_values()._get_cached_value(
option, path=subpath, option, path=subpath,
@ -681,10 +664,10 @@ class _CommonConfig(SubConfig):
session = self.cfgimpl_get_values()._p_.getsession() session = self.cfgimpl_get_values()._p_.getsession()
config.cfgimpl_get_values()._p_.importation(self.cfgimpl_get_values()._p_.exportation( config.cfgimpl_get_values()._p_.importation(self.cfgimpl_get_values()._p_.exportation(
session)) session))
config.cfgimpl_get_settings()._p_._properties = self.cfgimpl_get_settings( config.cfgimpl_get_settings()._p_.set_modified_properties(self.cfgimpl_get_settings(
)._p_.get_modified_properties() )._p_.get_modified_properties())
config.cfgimpl_get_settings()._p_._permissives = self.cfgimpl_get_settings( config.cfgimpl_get_settings()._p_.set_modified_permissives(self.cfgimpl_get_settings(
)._p_.get_modified_permissives() )._p_.get_modified_permissives())
return config return config

View File

@ -525,7 +525,8 @@ class Option(OnlyOption):
def impl_validate(self, value, context=undefined, validate=True, def impl_validate(self, value, context=undefined, validate=True,
force_index=None, force_submulti_index=None, force_index=None, force_submulti_index=None,
current_opt=undefined, is_multi=None, current_opt=undefined, is_multi=None,
display_error=True, display_warnings=True, multi=None): display_error=True, display_warnings=True, multi=None,
setting_properties=undefined):
""" """
:param value: the option's value :param value: the option's value
:param context: Config's context :param context: Config's context
@ -544,8 +545,9 @@ class Option(OnlyOption):
if current_opt is undefined: if current_opt is undefined:
current_opt = self current_opt = self
display_warnings = display_warnings and (context is undefined or if display_warnings and setting_properties is undefined and context is not undefined:
'warnings' in context.cfgimpl_get_settings()) setting_properties = context.cfgimpl_get_settings()._getproperties(read_write=False)
display_warnings = display_warnings and (setting_properties is undefined or 'warnings' in setting_properties)
def _is_not_unique(value): def _is_not_unique(value):
if display_error and self.impl_is_unique() and len(set(value)) != len(value): if display_error and self.impl_is_unique() and len(set(value)) != len(value):
for idx, val in enumerate(value): for idx, val in enumerate(value):
@ -1160,7 +1162,8 @@ class DynSymLinkOption(object):
def impl_validate(self, value, context=undefined, validate=True, def impl_validate(self, value, context=undefined, validate=True,
force_index=None, force_submulti_index=None, is_multi=None, force_index=None, force_submulti_index=None, is_multi=None,
display_error=True, display_warnings=True, multi=None): display_error=True, display_warnings=True, multi=None,
setting_properties=undefined):
return self._impl_getopt().impl_validate(value, context, validate, return self._impl_getopt().impl_validate(value, context, validate,
force_index, force_index,
force_submulti_index, force_submulti_index,
@ -1168,7 +1171,8 @@ class DynSymLinkOption(object):
is_multi=is_multi, is_multi=is_multi,
display_error=display_error, display_error=display_error,
display_warnings=display_warnings, display_warnings=display_warnings,
multi=multi) multi=multi,
setting_properties=setting_properties)
def impl_is_dynsymlinkoption(self): def impl_is_dynsymlinkoption(self):
return True return True

View File

@ -182,7 +182,7 @@ class MasterSlaves(object):
masterp = master.impl_getpath(context) masterp = master.impl_getpath(context)
masterlen = self.get_length(values, opt, session, validate, undefined, masterlen = self.get_length(values, opt, session, validate, undefined,
undefined, force_permissive, undefined, force_permissive,
master=master) master=master, setting_properties=setting_properties)
if isinstance(masterlen, Exception): if isinstance(masterlen, Exception):
if isinstance(masterlen, PropertiesOptionError): if isinstance(masterlen, PropertiesOptionError):
masterlen.set_orig_opt(opt) masterlen.set_orig_opt(opt)
@ -252,7 +252,7 @@ class MasterSlaves(object):
def get_length(self, values, opt, session, validate=True, slave_path=undefined, def get_length(self, values, opt, session, validate=True, slave_path=undefined,
slave_value=undefined, force_permissive=False, master=None, slave_value=undefined, force_permissive=False, master=None,
masterp=None): masterp=None, setting_properties=undefined):
"""get master len with slave option""" """get master len with slave option"""
if master is None: if master is None:
master = self.getmaster(opt) master = self.getmaster(opt)
@ -262,7 +262,7 @@ class MasterSlaves(object):
slave_path = undefined slave_path = undefined
value = self.getitem(values, master, masterp, validate, value = self.getitem(values, master, masterp, validate,
force_permissive, None, True, session, slave_path=slave_path, force_permissive, None, True, session, slave_path=slave_path,
slave_value=slave_value) slave_value=slave_value, setting_properties=setting_properties)
if isinstance(value, Exception): if isinstance(value, Exception):
return value return value
return len(value) return len(value)

View File

@ -1,4 +1,4 @@
# Copyright (C) 2013-2014 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2013-2017 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # 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 # under the terms of the GNU Lesser General Public License as published by the
@ -36,12 +36,15 @@ from ..i18n import _
MODULE_PATH = os.path.split(os.path.split(os.path.split(__file__)[0])[0])[1] MODULE_PATH = os.path.split(os.path.split(os.path.split(__file__)[0])[0])[1]
DEFAULT_STORAGE = 'dictionary'
class StorageType(object): class StorageType(object):
"""Object to store storage's type. If a Config is already set, """Object to store storage's type. If a Config is already set,
default storage is store as selected storage. You cannot change it default storage is store as selected storage. You cannot change it
after. after.
""" """
default_storage = os.environ.get('TIRAMISU_STORAGE', 'dictionary') default_storage = os.environ.get('TIRAMISU_STORAGE', DEFAULT_STORAGE)
storage_type = None storage_type = None
mod = None mod = None
@ -55,6 +58,9 @@ class StorageType(object):
def get(self): def get(self):
if self.storage_type is None: if self.storage_type is None:
self.storage_type = self.default_storage self.storage_type = self.default_storage
set_to_default = True
else:
set_to_default = False
if self.mod is None: if self.mod is None:
modulepath = '{0}.storage.{1}'.format(MODULE_PATH, self.storage_type) modulepath = '{0}.storage.{1}'.format(MODULE_PATH, self.storage_type)
try: try:
@ -70,8 +76,9 @@ class StorageType(object):
storage_type = StorageType() storage_type = StorageType()
storage_option_type = StorageType() storage_option_type = StorageType()
storage_option_type.set(DEFAULT_STORAGE)
storage_validation = StorageType() storage_validation = StorageType()
storage_validation.set('dictionary') storage_validation.set(DEFAULT_STORAGE)
def set_storage(type_, name, **kwargs): # pragma: optional cover def set_storage(type_, name, **kwargs): # pragma: optional cover
@ -126,7 +133,7 @@ def get_storages(context, session_id, persistent):
session_id = gen_id(context) session_id = gen_id(context)
imp = storage_type.get() imp = storage_type.get()
storage = imp.Storage(session_id, persistent) storage = imp.Storage(session_id, persistent)
settings = imp.Settings(session_id, storage) settings = imp.Settings(storage)
values = imp.Values(storage) values = imp.Values(storage)
return settings, values return settings, values
@ -168,8 +175,9 @@ def delete_session(type_, session_id): # pragma: optional cover
session = storage_module.storage.getsession() session = storage_module.storage.getsession()
storage_module.value.delete_session(session_id, session) storage_module.value.delete_session(session_id, session)
storage_module.storage.delete_session(session_id, session) storage_module.storage.delete_session(session_id, session)
session.commit() if session:
session.commit()
del(session) del(session)
__all__ = (set_storage, list_sessions, delete_session) __all__ = ('set_storage', 'list_sessions', 'delete_session')

View File

@ -22,7 +22,7 @@ from ..util import Cache
class Settings(Cache): class Settings(Cache):
__slots__ = ('_properties', '_permissives') __slots__ = ('_properties', '_permissives')
def __init__(self, session_id, storage): 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 = {}
@ -59,8 +59,14 @@ class Settings(Cache):
""" """
return copy(self._properties) return copy(self._properties)
def set_modified_properties(self, properties):
self._properties = properties
def get_modified_permissives(self): def get_modified_permissives(self):
"""return all modified permissives in a dictionary """return all modified permissives in a dictionary
example: {'path1': set(['perm1', 'perm2'])} example: {'path1': set(['perm1', 'perm2'])}
""" """
return copy(self._permissives) return copy(self._permissives)
def set_modified_permissives(self, permissives):
self._permissives = permissives

View File

@ -33,7 +33,7 @@ def list_sessions(): # pragma: optional cover
return _list_sessions return _list_sessions
def delete_session(session_id): # pragma: optional cover def delete_session(session_id, session): # pragma: optional cover
raise ConfigError(_('dictionary storage cannot delete session')) raise ConfigError(_('dictionary storage cannot delete session'))

View File

@ -182,11 +182,11 @@ class Values(Cache):
indexes = self._values[1][path_idx] indexes = self._values[1][path_idx]
if indexes is None: if indexes is None:
if index is not None: # pragma: no cover if index is not None: # pragma: no cover
raise ValueError('index is forbidden') raise ValueError('index is forbidden for {}'.format(path))
value = self._values[nb][path_idx] value = self._values[nb][path_idx]
else: else:
if index is None: # pragma: no cover if index is None: # pragma: no cover
raise ValueError('index is mandatory') raise ValueError('index is mandatory for {}'.format(path))
if index in indexes: if index in indexes:
subidx = indexes.index(index) subidx = indexes.index(index)
value = self._values[nb][path_idx][subidx] value = self._values[nb][path_idx][subidx]

View File

@ -22,6 +22,6 @@ You should not configure differents Configs with same session_id.
""" """
from .value import Values from .value import Values
from .setting import Settings from .setting import Settings
from .storage import setting, Storage, list_sessions, delete_session from .storage import Storage, list_sessions, delete_session
__all__ = (setting, Values, Settings, Storage, list_sessions, delete_session) __all__ = ('Values', 'Settings', 'Storage', 'list_sessions', 'delete_session')

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"default plugin for setting: set it in a simple dictionary" "default plugin for setting: set it in a simple dictionary"
# Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2013-2017 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # 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 # under the terms of the GNU Lesser General Public License as published by the
@ -22,10 +22,10 @@ class Settings(Sqlite3DB):
__slots__ = tuple() __slots__ = tuple()
def __init__(self, storage): def __init__(self, storage):
settings_table = 'CREATE TABLE IF NOT EXISTS property(path text ' settings_table = 'CREATE TABLE IF NOT EXISTS property(path TEXT,'
settings_table += 'primary key, properties text)' settings_table += 'properties text, session_id TEXT, PRIMARY KEY(path, session_id))'
permissives_table = 'CREATE TABLE IF NOT EXISTS permissive(path text ' permissives_table = 'CREATE TABLE IF NOT EXISTS permissive(path TEXT,'
permissives_table += 'primary key, permissives text)' permissives_table += 'permissives TEXT, session_id TEXT, PRIMARY KEY(path, session_id))'
# should init cache too # should init cache too
super(Settings, self).__init__(storage) super(Settings, self).__init__(storage)
self._storage.execute(settings_table, commit=False) self._storage.execute(settings_table, commit=False)
@ -34,16 +34,23 @@ class Settings(Sqlite3DB):
# properties # properties
def setproperties(self, path, properties): def setproperties(self, path, properties):
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
self._storage.execute("DELETE FROM property WHERE path = ?", (path,), self._storage.execute("DELETE FROM property WHERE path = ? AND session_id = ?",
(path, self._session_id),
False) False)
self._storage.execute("INSERT INTO property(path, properties) VALUES " self._storage.execute("INSERT INTO property(path, properties, session_id) VALUES "
"(?, ?)", (path, "(?, ?, ?)", (path,
self._sqlite_encode(properties))) self._sqlite_encode(properties),
self._session_id))
def delproperties(self, path):
path = self._sqlite_encode_path(path)
self._storage.execute("DELETE FROM property WHERE path = ? AND session_id = ?",
(path, self._session_id))
def getproperties(self, path, default_properties): def getproperties(self, path, default_properties):
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
value = self._storage.select("SELECT properties FROM property WHERE " value = self._storage.select("SELECT properties FROM property WHERE "
"path = ?", (path,)) "path = ? AND session_id = ?", (path, self._session_id))
if value is None: if value is None:
return set(default_properties) return set(default_properties)
else: else:
@ -52,29 +59,32 @@ class Settings(Sqlite3DB):
def hasproperties(self, path): def hasproperties(self, path):
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
return self._storage.select("SELECT properties FROM property WHERE " return self._storage.select("SELECT properties FROM property WHERE "
"path = ?", (path,)) is not None "path = ? AND session_id = ?", (path, self._session_id)
) is not None
def reset_all_properties(self): def reset_all_properties(self):
self._storage.execute("DELETE FROM property") self._storage.execute("DELETE FROM property WHERE session_id = ?", (self._session_id,))
def reset_properties(self, path): def reset_properties(self, path):
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
self._storage.execute("DELETE FROM property WHERE path = ?", (path,)) self._storage.execute("DELETE FROM property WHERE path = ? AND session_id = ?",
(path, self._session_id))
# permissive # permissive
def setpermissive(self, path, permissive): def setpermissive(self, path, permissive):
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
self._storage.execute("DELETE FROM permissive WHERE path = ?", (path,), self._storage.execute("DELETE FROM permissive WHERE path = ? AND session_id = ?",
(path, self._session_id),
False) False)
self._storage.execute("INSERT INTO permissive(path, permissives) " self._storage.execute("INSERT INTO permissive(path, permissives, session_id) "
"VALUES (?, ?)", (path, "VALUES (?, ?, ?)", (path,
self._sqlite_encode(permissive) self._sqlite_encode(permissive),
)) self._session_id))
def getpermissive(self, path='_none'): def getpermissive(self, path='_none'):
permissives = self._storage.select("SELECT permissives FROM " permissives = self._storage.select("SELECT permissives FROM "
"permissive WHERE path = ?", "permissive WHERE path = ? AND session_id = ?",
(path,)) (path, self._session_id))
if permissives is None: if permissives is None:
return frozenset() return frozenset()
else: else:
@ -85,19 +95,43 @@ class Settings(Sqlite3DB):
example: {'path1': set(['prop1', 'prop2'])} example: {'path1': set(['prop1', 'prop2'])}
""" """
ret = {} ret = {}
for path, properties in self._storage.select("SELECT * FROM property", for path, properties, _ in self._storage.select("SELECT * FROM property "
only_one=False): "WHERE session_id = ?",
(self._session_id,),
only_one=False):
path = self._sqlite_decode_path(path) path = self._sqlite_decode_path(path)
ret[path] = self._sqlite_decode(properties) ret[path] = self._sqlite_decode(properties)
return ret return ret
def set_modified_properties(self, properties):
self._storage.execute("DELETE FROM property", commit=False)
for path, property_ in properties.items():
self._storage.execute("INSERT INTO property(path, property, session_id) "
"VALUES (?, ?, ?)", (path,
self._sqlite_encode(property_),
self._session_id,
), False)
self._storage._conn.commit()
def get_modified_permissives(self): def get_modified_permissives(self):
"""return all modified permissives in a dictionary """return all modified permissives in a dictionary
example: {'path1': set(['perm1', 'perm2'])} example: {'path1': set(['perm1', 'perm2'])}
""" """
ret = {} ret = {}
for path, permissives in self._storage.select("SELECT * FROM permissive", for path, permissives in self._storage.select("SELECT * FROM permissive "
"WHERE session_id = ?",
(self._session_id,),
only_one=False): only_one=False):
path = self._sqlite_decode_path(path) path = self._sqlite_decode_path(path)
ret[path] = self._sqlite_decode(permissives) ret[path] = self._sqlite_decode(permissives)
return ret return ret
def set_modified_permissives(self, permissives):
self._storage.execute("DELETE FROM permissive", commit=False)
for path, permissive in permissives.items():
self._storage.execute("INSERT INTO permissive(path, permissive, session_id) "
"VALUES (?, ?, ?)", (path,
self._sqlite_encode(permissive),
self._session_id,
), False)
self._storage._conn.commit()

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"sqlite3 cache" "sqlite3 cache"
# Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2013-2017 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # 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 # under the terms of the GNU Lesser General Public License as published by the
@ -23,7 +23,11 @@ from ..util import Cache
class Sqlite3DB(Cache): class Sqlite3DB(Cache):
__slots__ = tuple() __slots__ = ('_session_id',)
def __init__(self, storage):
self._session_id = storage.session_id
super(Sqlite3DB, self).__init__(storage)
def _sqlite_decode_path(self, path): def _sqlite_decode_path(self, path):
if path == '_none': if path == '_none':
return None return None

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"default plugin for cache: set it in a simple dictionary" "default plugin for cache: set it in a simple dictionary"
# Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2013-2017 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # 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 # under the terms of the GNU Lesser General Public License as published by the
@ -17,7 +17,7 @@
# ____________________________________________________________ # ____________________________________________________________
from os import unlink from os import unlink
from os.path import basename, splitext, join from os.path import basename, splitext, join, isfile
import sqlite3 import sqlite3
from glob import glob from glob import glob
from ..util import SerializeObject from ..util import SerializeObject
@ -29,25 +29,39 @@ class Setting(SerializeObject):
""" """
extension = 'db' extension = 'db'
dir_database = '/tmp' dir_database = '/tmp'
name = 'tiramisu'
setting = Setting() SETTING = Setting()
def _gen_filename(name): def _gen_filename():
return join(setting.dir_database, '{0}.{1}'.format(name, return join(SETTING.dir_database, '{0}.{1}'.format(SETTING.name, SETTING.extension))
setting.extension))
def list_sessions(): def list_sessions():
names = [] conn = sqlite3.connect(_gen_filename())
for filename in glob(_gen_filename('*')): conn.text_factory = str
names.append(basename(splitext(filename)[0])) cursor = conn.cursor()
names = [row[0] for row in cursor.execute("SELECT DISTINCT session_id FROM value").fetchall()]
conn.close()
return names return names
def delete_session(session_id): def delete_session(session_id, session):
unlink(_gen_filename(session_id)) conn = sqlite3.connect(_gen_filename())
conn.text_factory = str
cursor = conn.cursor()
cursor.execute("DELETE FROM property WHERE session_id = ?",
(session_id,))
cursor.execute("DELETE FROM permissive WHERE session_id = ?",
(session_id,))
cursor.execute("DELETE FROM value WHERE session_id = ?",
(session_id,))
cursor.execute("DELETE FROM information WHERE session_id = ?",
(session_id,))
conn.commit()
conn.close()
class Storage(object): class Storage(object):
@ -56,12 +70,9 @@ class Storage(object):
def __init__(self, session_id, persistent, test=False): def __init__(self, session_id, persistent, test=False):
self.persistent = persistent self.persistent = persistent
if self.persistent: self.serializable = self.persistent
self.serializable = True
else:
self.serializable = False
self.session_id = session_id self.session_id = session_id
self._conn = sqlite3.connect(_gen_filename(self.session_id)) self._conn = sqlite3.connect(_gen_filename())
self._conn.text_factory = str self._conn.text_factory = str
self._cursor = self._conn.cursor() self._cursor = self._conn.cursor()
@ -83,4 +94,10 @@ class Storage(object):
self._cursor.close() self._cursor.close()
self._conn.close() self._conn.close()
if not self.persistent: if not self.persistent:
delete_session(self.session_id) session = None
if delete_session is not None:
delete_session(self.session_id, session)
def getsession():
pass

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"default plugin for value: set it in a simple dictionary" "default plugin for value: set it in a simple dictionary"
# Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2013-2017 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # 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 # under the terms of the GNU Lesser General Public License as published by the
@ -17,7 +17,9 @@
# ____________________________________________________________ # ____________________________________________________________
from .sqlite3db import Sqlite3DB from .sqlite3db import Sqlite3DB
from tiramisu.setting import owners from .storage import delete_session
from ...setting import undefined, owners
from ...i18n import _
class Values(Sqlite3DB): class Values(Sqlite3DB):
@ -28,87 +30,128 @@ class Values(Sqlite3DB):
""" """
# should init cache too # should init cache too
super(Values, self).__init__(storage) super(Values, self).__init__(storage)
values_table = 'CREATE TABLE IF NOT EXISTS value(path text primary ' values_table = 'CREATE TABLE IF NOT EXISTS value(path TEXT, '
values_table += 'key, value text, owner text)' values_table += 'value TEXT, owner TEXT, idx INTEGER, session_id TEXT NOT NULL, '\
'PRIMARY KEY (path, idx, session_id))'
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 = 'CREATE TABLE IF NOT EXISTS information(key TEXT PRIMARY '
informations_table += 'key, value text)' informations_table += 'KEY, value TEXT, session_id TEXT NOT NULL)'
self._storage.execute(informations_table) self._storage.execute(informations_table)
for owner in self._storage.select("SELECT DISTINCT owner FROM value", tuple(), False):
try: def getsession(self):
getattr(owners, owner[0]) pass
except AttributeError:
owners.addowner(owner[0])
# sqlite # sqlite
def _sqlite_select(self, path): def _sqlite_select(self, path, index):
return self._storage.select("SELECT value FROM value WHERE path = ?", request = "SELECT value FROM value WHERE path = ? AND session_id = ?"
(path,)) params = (path, self._session_id)
if index is not None:
request += " and idx = ?"
params = (path, self._session_id, index)
return self._storage.select(request, params)
# value # value
def setvalue(self, path, value, owner): def setvalue(self, path, value, owner, index, session):
"""set value for an option """set value for an option
a specified value must be associated to an owner a specified value must be associated to an owner
""" """
self.resetvalue(path)
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
self._storage.execute("INSERT INTO value(path, value, owner) VALUES " if index is not None:
"(?, ?, ?)", (path, self._sqlite_encode(value), self._storage.execute("DELETE FROM value WHERE path = ? AND idx = ? AND "
str(owner))) "session_id = ?", (path, index, self._session_id),
commit=False)
self._storage.execute("INSERT INTO value(path, value, owner, idx, session_id) VALUES "
"(?, ?, ?, ?, ?)", (path, self._sqlite_encode(value),
self._sqlite_encode(str(owner)),
index,
self._session_id),
commit=True)
else:
self._storage.execute("DELETE FROM value WHERE path = ? AND session_id = ?",
(path, self._session_id),
commit=False)
self._storage.execute("INSERT INTO value(path, value, owner, session_id) VALUES "
"(?, ?, ?, ?)", (path, self._sqlite_encode(value),
self._sqlite_encode(str(owner)),
self._session_id),
commit=True)
def getvalue(self, path): def getvalue(self, path, session, index=None):
"""get value for an option """get value for an option
return: only value, not the owner return: only value, not the owner
""" """
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
return self._sqlite_decode(self._sqlite_select(path)[0]) values = self._sqlite_select(path, index)
if values is None:
return values
return self._sqlite_decode(values[0])
def hasvalue(self, path): def hasvalue(self, path, index=None):
"""if opt has a value """if opt has a value
return: boolean return: boolean
""" """
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
return self._sqlite_select(path) is not None return self._sqlite_select(path, index) is not None
def resetvalue(self, path): def resetvalue(self, path, session):
"""remove value means delete value in storage """remove value means delete value in storage
""" """
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
self._storage.execute("DELETE FROM value WHERE path = ?", (path,)) self._storage.execute("DELETE FROM value WHERE path = ? AND session_id = ?", (path, self._session_id))
def get_modified_values(self): def get_modified_values(self):
"""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')}
""" """
ret = {} ret = {}
for path, value, owner in self._storage.select("SELECT * FROM value", for path, value, owner, index, _ in self._storage.select("SELECT * FROM value WHERE "
only_one=False): "session_id = ?",
(self._session_id,),
only_one=False):
path = self._sqlite_decode_path(path) path = self._sqlite_decode_path(path)
owner = getattr(owners, owner) owner = getattr(owners, self._sqlite_decode(owner))
value = self._sqlite_decode(value) value = self._sqlite_decode(value)
ret[path] = (owner, value) if isinstance(value, list):
value = tuple(value)
if index is None:
ret[path] = (owner, value)
else:
if path in ret:
ret[path][0][str(index)] = owner
ret[path][1][str(index)] = value
else:
ret[path] = ({str(index): owner}, {str(index): value})
return ret return ret
# owner # owner
def setowner(self, path, owner): def setowner(self, path, owner, session, index=None):
"""change owner for an option """change owner for an option
""" """
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
self._storage.execute("UPDATE value SET owner = ? WHERE path = ?", if index is None:
(str(owner), path)) self._storage.execute("UPDATE value SET owner = ? WHERE path = ? AND session_id = ?",
(self._sqlite_encode(str(owner)), path, self._session_id))
else:
self._storage.execute("UPDATE value SET owner = ? WHERE path = ? and idx = ? AND session_id = ?",
(self._sqlite_encode(str(owner)), path, index, self._session_id))
def getowner(self, path, default): def getowner(self, path, default, session, index=None, only_default=False):
"""get owner for an option """get owner for an option
return: owner object return: owner object
""" """
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
owner = self._storage.select("SELECT owner FROM value WHERE path = ?", request = "SELECT owner FROM value WHERE path = ? AND session_id = ?"
(path,)) if index is not None:
request += " AND idx = ?"
params = (path, self._session_id, index)
else:
params = (path, self._session_id)
owner = self._storage.select(request, params)
if owner is None: if owner is None:
return default return default
else: else:
owner = owner[0] owner = self._sqlite_decode(owner[0])
# autocreate owners # autocreate owners
try: try:
return getattr(owners, owner) return getattr(owners, owner)
@ -123,19 +166,79 @@ class Values(Sqlite3DB):
:param key: information's key (ex: "help", "doc" :param key: information's key (ex: "help", "doc"
:param value: information's value (ex: "the help string") :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 = ? AND session_id = ?",
(key, self._session_id),
False) False)
self._storage.execute("INSERT INTO information(key, value) VALUES " self._storage.execute("INSERT INTO information(key, value, session_id) VALUES "
"(?, ?)", (key, self._sqlite_encode(value))) "(?, ?, ?)", (key, self._sqlite_encode(value), self._session_id))
def get_information(self, key): def get_information(self, key, default):
"""retrieves one information's item """retrieves one information's item
:param key: the item string (ex: "help") :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 = ? AND "
(key,)) "session_id = ?",
(key, self._session_id))
if value is None: if value is None:
raise ValueError("not found") if default is undefined:
raise ValueError(_("information's item"
" not found: {0}").format(key))
return default
else: else:
return self._sqlite_decode(value[0]) return self._sqlite_decode(value[0])
def del_information(self, key, raises):
if raises and self._storage.select("SELECT value FROM information WHERE key = ? "
"AND session_id = ?",
(key, self._session_id)) is None:
raise ValueError(_("information's item not found {0}").format(key))
self._storage.execute("DELETE FROM information WHERE key = ? AND session_id = ?",
(key, self._session_id))
def exportation(self, session, fake=False):
rows = self._storage.select("SELECT path, value, owner, idx FROM value WHERE "
"session_id = ?;", (self._session_id,), only_one=False)
ret = [[], [], [], []]
for row in rows:
path = row[0]
value = self._sqlite_decode(row[1])
owner = self._sqlite_decode(row[2])
index = row[3]
if index is None:
ret[0].append(path)
ret[1].append(index)
ret[2].append(value)
ret[3].append(owner)
else:
if path in ret[0]:
path_idx = ret[0].index(path)
ret[1][path_idx].append(index)
ret[2][path_idx].append(value)
else:
ret[0].append(path)
ret[1].append([index])
ret[2].append([value])
ret[3].append(owner)
return ret
def importation(self, export):
self._storage.execute("DELETE FROM value WHERE session_id = ?", (self._session_id,),
commit=False)
for idx, path in enumerate(export[0]):
index = export[1][idx]
value = export[2][idx]
owner = export[3][idx]
self._storage.execute("INSERT INTO value(path, value, owner, idx, session_id) VALUES "
"(?, ?, ?, ?, ?)", (path, self._sqlite_encode(value),
self._sqlite_encode(str(owner)), index,
self._session_id))
self._storage._conn.commit()
def get_max_length(self, path, session):
val_max = self._storage.select("SELECT max(idx) FROM value WHERE path = ? AND session_id = ?",
(path, self._session_id), False)
if val_max[0][0] is None:
return 0
return val_max[0][0] + 1

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"takes care of the option's values and multi values" "takes care of the option's values and multi values"
# Copyright (C) 2013-2014 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2013-2017 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software: you can redistribute it and/or modify it # 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 # under the terms of the GNU Lesser General Public License as published by the
@ -390,7 +390,8 @@ class Values(object):
force_index=index, force_index=index,
force_submulti_index=force_submulti_index, force_submulti_index=force_submulti_index,
display_error=False, display_error=False,
display_warnings=display_warnings) display_warnings=display_warnings,
setting_properties=setting_properties,)
if config_error is not None: if config_error is not None:
return config_error return config_error
return value return value