update sqlite storage
authorEmmanuel Garette <egarette@cadoles.com>
Tue, 4 Jul 2017 17:59:42 +0000 (19:59 +0200)
committerEmmanuel Garette <egarette@cadoles.com>
Tue, 4 Jul 2017 17:59:42 +0000 (19:59 +0200)
17 files changed:
test/test_cache.py
test/test_option_calculation.py
test/test_parsing_group.py
test/test_storage.py
tiramisu/config.py
tiramisu/option/baseoption.py
tiramisu/option/masterslave.py
tiramisu/storage/__init__.py
tiramisu/storage/dictionary/setting.py
tiramisu/storage/dictionary/storage.py
tiramisu/storage/dictionary/value.py
tiramisu/storage/sqlite3/__init__.py
tiramisu/storage/sqlite3/setting.py
tiramisu/storage/sqlite3/sqlite3db.py
tiramisu/storage/sqlite3/storage.py
tiramisu/storage/sqlite3/value.py
tiramisu/value.py

index 13466d6..b3963b2 100644 (file)
@@ -2,8 +2,9 @@
 from autopath import do_autopath
 do_autopath()
 
-from tiramisu import setting
+from tiramisu import setting, value
 setting.expires_time = 1
+value.expires_time = 1
 from tiramisu.option import IntOption, StrOption, OptionDescription
 from tiramisu.config import Config
 from tiramisu.error import ConfigError
index 2db667d..b1fc7dd 100644 (file)
@@ -644,7 +644,7 @@ def test_consistency_master_and_slaves_master_mandatory_non_transitive():
     maconfig = OptionDescription('rootconfig', '', [interface1, interface2])
     cfg = Config(maconfig)
     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():
index 9065608..0ac2457 100644 (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',))}
     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'})}
+    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'})}
index 6d01e3a..ad12a51 100644 (file)
@@ -6,7 +6,7 @@ do_autopath()
 
 from tiramisu.config import Config
 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
 
 
@@ -21,6 +21,7 @@ def test_list():
     o = OptionDescription('od', '', [b])
     c = Config(o, session_id='test_non_persistent')
     c.cfgimpl_get_settings().remove('cache')
+    c.b = True
     assert 'test_non_persistent' in list_sessions('config')
     del(c)
     assert 'test_non_persistent' not in list_sessions('config')
@@ -40,7 +41,8 @@ def test_list_sessions_persistent():
     b = BoolOption('b', '')
     o = OptionDescription('od', '', [b])
     try:
-        Config(o, session_id='test_persistent', persistent=True)
+        c = Config(o, session_id='test_persistent', persistent=True)
+        c.b = True
     except ValueError:
         # storage is not persistent
         pass
@@ -139,6 +141,46 @@ def test_create_persistent_retrieve_owner():
         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():
     b = BoolOption('b', '')
     o = OptionDescription('od', '', [b])
@@ -204,3 +246,28 @@ def test_two_persistent_information():
         c2.cfgimpl_get_settings().remove('cache')
         assert c2.impl_get_information('info') == 'string'
         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')
index f22e622..7fd8269 100644 (file)
@@ -87,25 +87,6 @@ class SubConfig(object):
                 return self, None
         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):
         """Pythonesque way of parsing group's ordered options.
@@ -287,6 +268,8 @@ class SubConfig(object):
             option = self.cfgimpl_get_description().__getattr__(name,
                                                                 context=context)
             subpath = self._get_subpath(name)
+            if _setting_properties is undefined:
+                _setting_properties = context.cfgimpl_get_settings()._getproperties(read_write=True)
             if isinstance(option, DynSymLinkOption):
                 cfg = self.cfgimpl_get_values()._get_cached_value(
                     option, path=subpath,
@@ -681,10 +664,10 @@ class _CommonConfig(SubConfig):
         session = self.cfgimpl_get_values()._p_.getsession()
         config.cfgimpl_get_values()._p_.importation(self.cfgimpl_get_values()._p_.exportation(
             session))
-        config.cfgimpl_get_settings()._p_._properties = self.cfgimpl_get_settings(
-            )._p_.get_modified_properties()
-        config.cfgimpl_get_settings()._p_._permissives = self.cfgimpl_get_settings(
-            )._p_.get_modified_permissives()
+        config.cfgimpl_get_settings()._p_.set_modified_properties(self.cfgimpl_get_settings(
+            )._p_.get_modified_properties())
+        config.cfgimpl_get_settings()._p_.set_modified_permissives(self.cfgimpl_get_settings(
+            )._p_.get_modified_permissives())
         return config
 
 
index 47d361b..a2435ef 100644 (file)
@@ -525,7 +525,8 @@ class Option(OnlyOption):
     def impl_validate(self, value, context=undefined, validate=True,
                       force_index=None, force_submulti_index=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 context: Config's context
@@ -544,8 +545,9 @@ class Option(OnlyOption):
         if current_opt is undefined:
             current_opt = self
 
-        display_warnings = display_warnings and (context is undefined or
-                                                 'warnings' in context.cfgimpl_get_settings())
+        if display_warnings and setting_properties is undefined and context is not undefined:
+            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):
             if display_error and self.impl_is_unique() and len(set(value)) != len(value):
                 for idx, val in enumerate(value):
@@ -1160,7 +1162,8 @@ class DynSymLinkOption(object):
 
     def impl_validate(self, value, context=undefined, validate=True,
                       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,
                                                  force_index,
                                                  force_submulti_index,
@@ -1168,7 +1171,8 @@ class DynSymLinkOption(object):
                                                  is_multi=is_multi,
                                                  display_error=display_error,
                                                  display_warnings=display_warnings,
-                                                 multi=multi)
+                                                 multi=multi,
+                                                 setting_properties=setting_properties)
 
     def impl_is_dynsymlinkoption(self):
         return True
index b40b8c8..4f7e61c 100644 (file)
@@ -182,7 +182,7 @@ class MasterSlaves(object):
         masterp = master.impl_getpath(context)
         masterlen = self.get_length(values, opt, session, validate, undefined,
                                     undefined, force_permissive,
-                                    master=master)
+                                    master=master, setting_properties=setting_properties)
         if isinstance(masterlen, Exception):
             if isinstance(masterlen, PropertiesOptionError):
                 masterlen.set_orig_opt(opt)
@@ -252,7 +252,7 @@ class MasterSlaves(object):
 
     def get_length(self, values, opt, session, validate=True, slave_path=undefined,
                    slave_value=undefined, force_permissive=False, master=None,
-                   masterp=None):
+                   masterp=None, setting_properties=undefined):
         """get master len with slave option"""
         if master is None:
             master = self.getmaster(opt)
@@ -262,7 +262,7 @@ class MasterSlaves(object):
             slave_path = undefined
         value = self.getitem(values, master, masterp, validate,
                              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):
             return value
         return len(value)
index a95a629..ff74d79 100644 (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
 # 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]
 
 
+DEFAULT_STORAGE = 'dictionary'
+
+
 class StorageType(object):
     """Object to store storage's type. If a Config is already set,
     default storage is store as selected storage. You cannot change it
     after.
     """
-    default_storage = os.environ.get('TIRAMISU_STORAGE', 'dictionary')
+    default_storage = os.environ.get('TIRAMISU_STORAGE', DEFAULT_STORAGE)
     storage_type = None
     mod = None
 
@@ -55,6 +58,9 @@ class StorageType(object):
     def get(self):
         if self.storage_type is None:
             self.storage_type = self.default_storage
+            set_to_default = True
+        else:
+            set_to_default = False
         if self.mod is None:
             modulepath = '{0}.storage.{1}'.format(MODULE_PATH, self.storage_type)
             try:
@@ -70,8 +76,9 @@ class StorageType(object):
 
 storage_type = StorageType()
 storage_option_type = StorageType()
+storage_option_type.set(DEFAULT_STORAGE)
 storage_validation = StorageType()
-storage_validation.set('dictionary')
+storage_validation.set(DEFAULT_STORAGE)
 
 
 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)
     imp = storage_type.get()
     storage = imp.Storage(session_id, persistent)
-    settings = imp.Settings(session_id, storage)
+    settings = imp.Settings(storage)
     values = imp.Values(storage)
     return settings, values
 
@@ -168,8 +175,9 @@ def delete_session(type_, session_id):  # pragma: optional cover
         session = storage_module.storage.getsession()
         storage_module.value.delete_session(session_id, session)
         storage_module.storage.delete_session(session_id, session)
-        session.commit()
+        if session:
+            session.commit()
         del(session)
 
 
-__all__ = (set_storage, list_sessions, delete_session)
+__all__ = ('set_storage', 'list_sessions', 'delete_session')
index 5bb985b..2d8ab06 100644 (file)
@@ -22,7 +22,7 @@ from ..util import Cache
 class Settings(Cache):
     __slots__ = ('_properties', '_permissives')
 
-    def __init__(self, session_id, storage):
+    def __init__(self, storage):
         # properties attribute: the name of a property enables this property
         # key is None for global properties
         self._properties = {}
@@ -59,8 +59,14 @@ class Settings(Cache):
         """
         return copy(self._properties)
 
+    def set_modified_properties(self, properties):
+        self._properties = properties
+
     def get_modified_permissives(self):
         """return all modified permissives in a dictionary
         example: {'path1': set(['perm1', 'perm2'])}
         """
         return copy(self._permissives)
+
+    def set_modified_permissives(self, permissives):
+        self._permissives = permissives
index 0f17b1b..c868928 100644 (file)
@@ -33,7 +33,7 @@ def list_sessions():  # pragma: optional cover
     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'))
 
 
index 0dc82bc..0a03e99 100644 (file)
@@ -182,11 +182,11 @@ class Values(Cache):
             indexes = self._values[1][path_idx]
             if indexes is None:
                 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]
             else:
                 if index is None:  # pragma: no cover
-                    raise ValueError('index is mandatory')
+                    raise ValueError('index is mandatory for {}'.format(path))
                 if index in indexes:
                     subidx = indexes.index(index)
                     value = self._values[nb][path_idx][subidx]
index 4f9754b..a9f67d7 100644 (file)
@@ -22,6 +22,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 Storage, list_sessions, delete_session
 
-__all__ = (setting, Values, Settings, Storage, list_sessions, delete_session)
+__all__ = ('Values', 'Settings', 'Storage', 'list_sessions', 'delete_session')
index a4478d2..8f4484a 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 "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
 # under the terms of the GNU Lesser General Public License as published by the
@@ -22,10 +22,10 @@ class Settings(Sqlite3DB):
     __slots__ = tuple()
 
     def __init__(self, storage):
-        settings_table = 'CREATE TABLE IF NOT EXISTS property(path text '
-        settings_table += 'primary key, properties text)'
-        permissives_table = 'CREATE TABLE IF NOT EXISTS permissive(path text '
-        permissives_table += 'primary key, permissives text)'
+        settings_table = 'CREATE TABLE IF NOT EXISTS property(path 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 += 'permissives TEXT, session_id TEXT, PRIMARY KEY(path, session_id))'
         # should init cache too
         super(Settings, self).__init__(storage)
         self._storage.execute(settings_table, commit=False)
@@ -34,16 +34,23 @@ class Settings(Sqlite3DB):
     # properties
     def setproperties(self, path, properties):
         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)
-        self._storage.execute("INSERT INTO property(path, properties) VALUES "
-                              "(?, ?)", (path,
-                                         self._sqlite_encode(properties)))
+        self._storage.execute("INSERT INTO property(path, properties, session_id) VALUES "
+                              "(?, ?, ?)", (path,
+                                         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):
         path = self._sqlite_encode_path(path)
         value = self._storage.select("SELECT properties FROM property WHERE "
-                                     "path = ?", (path,))
+                                     "path = ? AND session_id = ?", (path, self._session_id))
         if value is None:
             return set(default_properties)
         else:
@@ -52,29 +59,32 @@ 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
+                                    "path = ? AND session_id = ?", (path, self._session_id)
+                                    ) is not None
 
     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):
         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
     def setpermissive(self, path, permissive):
         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)
-        self._storage.execute("INSERT INTO permissive(path, permissives) "
-                              "VALUES (?, ?)", (path,
-                                                self._sqlite_encode(permissive)
-                                                ))
+        self._storage.execute("INSERT INTO permissive(path, permissives, session_id) "
+                              "VALUES (?, ?, ?)", (path,
+                                                   self._sqlite_encode(permissive),
+                                                   self._session_id))
 
     def getpermissive(self, path='_none'):
         permissives = self._storage.select("SELECT permissives FROM "
-                                           "permissive WHERE path = ?",
-                                           (path,))
+                                           "permissive WHERE path = ? AND session_id = ?",
+                                           (path, self._session_id))
         if permissives is None:
             return frozenset()
         else:
@@ -85,19 +95,43 @@ class Settings(Sqlite3DB):
         example: {'path1': set(['prop1', 'prop2'])}
         """
         ret = {}
-        for path, properties in self._storage.select("SELECT * FROM property",
-                                                     only_one=False):
+        for path, properties, _ in self._storage.select("SELECT * FROM property "
+                                                        "WHERE session_id = ?",
+                                                        (self._session_id,),
+                                                        only_one=False):
             path = self._sqlite_decode_path(path)
             ret[path] = self._sqlite_decode(properties)
         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):
         """return all modified permissives in a dictionary
         example: {'path1': set(['perm1', 'perm2'])}
         """
         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):
             path = self._sqlite_decode_path(path)
             ret[path] = self._sqlite_decode(permissives)
         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()
index 2b33117..0728454 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 "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
 # 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):
-    __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):
         if path == '_none':
             return None
index b441466..7802dfb 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 "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
 # under the terms of the GNU Lesser General Public License as published by the
@@ -17,7 +17,7 @@
 # ____________________________________________________________
 
 from os import unlink
-from os.path import basename, splitext, join
+from os.path import basename, splitext, join, isfile
 import sqlite3
 from glob import glob
 from ..util import SerializeObject
@@ -29,25 +29,39 @@ class Setting(SerializeObject):
     """
     extension = 'db'
     dir_database = '/tmp'
+    name = 'tiramisu'
 
 
-setting = Setting()
+SETTING = Setting()
 
 
-def _gen_filename(name):
-    return join(setting.dir_database, '{0}.{1}'.format(name,
-                                                       setting.extension))
+def _gen_filename():
+    return join(SETTING.dir_database, '{0}.{1}'.format(SETTING.name, SETTING.extension))
 
 
 def list_sessions():
-    names = []
-    for filename in glob(_gen_filename('*')):
-        names.append(basename(splitext(filename)[0]))
+    conn = sqlite3.connect(_gen_filename())
+    conn.text_factory = str
+    cursor = conn.cursor()
+    names = [row[0] for row in cursor.execute("SELECT DISTINCT session_id FROM value").fetchall()]
+    conn.close()
     return names
 
 
-def delete_session(session_id):
-    unlink(_gen_filename(session_id))
+def delete_session(session_id, session):
+    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):
@@ -56,12 +70,9 @@ class Storage(object):
 
     def __init__(self, session_id, persistent, test=False):
         self.persistent = persistent
-        if self.persistent:
-            self.serializable = True
-        else:
-            self.serializable = False
+        self.serializable = self.persistent
         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._cursor = self._conn.cursor()
 
@@ -83,4 +94,10 @@ class Storage(object):
         self._cursor.close()
         self._conn.close()
         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
index eaa434f..b06bf9e 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 "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
 # under the terms of the GNU Lesser General Public License as published by the
@@ -17,7 +17,9 @@
 # ____________________________________________________________
 
 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):
@@ -28,87 +30,128 @@ class Values(Sqlite3DB):
         """
         # should init cache too
         super(Values, self).__init__(storage)
-        values_table = 'CREATE TABLE IF NOT EXISTS value(path text primary '
-        values_table += 'key, value text, owner text)'
+        values_table = 'CREATE TABLE IF NOT EXISTS value(path 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)
-        informations_table = 'CREATE TABLE IF NOT EXISTS information(key text primary '
-        informations_table += 'key, value text)'
+        informations_table = 'CREATE TABLE IF NOT EXISTS information(key TEXT PRIMARY '
+        informations_table += 'KEY, value TEXT, session_id TEXT NOT NULL)'
         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:
-                owners.addowner(owner[0])
+
+    def getsession(self):
+        pass
 
     # sqlite
-    def _sqlite_select(self, path):
-        return self._storage.select("SELECT value FROM value WHERE path = ?",
-                                   (path,))
+    def _sqlite_select(self, path, index):
+        request = "SELECT value FROM value WHERE path = ? AND session_id = ?"
+        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
-    def setvalue(self, path, value, owner):
+    def setvalue(self, path, value, owner, index, session):
         """set value for an option
         a specified value must be associated to an owner
         """
-        self.resetvalue(path)
         path = self._sqlite_encode_path(path)
-        self._storage.execute("INSERT INTO value(path, value, owner) VALUES "
-                             "(?, ?, ?)", (path, self._sqlite_encode(value),
-                                           str(owner)))
-
-    def getvalue(self, path):
+        if index is not None:
+            self._storage.execute("DELETE FROM value WHERE path = ? AND idx = ? AND "
+                                  "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, session, index=None):
         """get value for an option
         return: only value, not the owner
         """
         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
         return: boolean
         """
         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
         """
         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):
         """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",
-                                                      only_one=False):
+        for path, value, owner, index, _ in self._storage.select("SELECT * FROM value WHERE "
+                                                                 "session_id = ?",
+                                                                 (self._session_id,),
+                                                                 only_one=False):
             path = self._sqlite_decode_path(path)
-            owner = getattr(owners, owner)
+            owner = getattr(owners, self._sqlite_decode(owner))
 
             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
 
     # owner
-    def setowner(self, path, owner):
+    def setowner(self, path, owner, session, index=None):
         """change owner for an option
         """
         path = self._sqlite_encode_path(path)
-        self._storage.execute("UPDATE value SET owner = ? WHERE path = ?",
-                             (str(owner), path))
+        if index is None:
+            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
         return: owner object
         """
         path = self._sqlite_encode_path(path)
-        owner = self._storage.select("SELECT owner FROM value WHERE path = ?",
-                                    (path,))
+        request = "SELECT owner FROM value WHERE path = ? AND session_id = ?"
+        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:
             return default
         else:
-            owner = owner[0]
+            owner = self._sqlite_decode(owner[0])
             # autocreate owners
             try:
                 return getattr(owners, owner)
@@ -123,19 +166,79 @@ 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 = ? AND session_id = ?",
+                             (key, self._session_id),
                              False)
-        self._storage.execute("INSERT INTO information(key, value) VALUES "
-                             "(?, ?)", (key, self._sqlite_encode(value)))
+        self._storage.execute("INSERT INTO information(key, value, session_id) VALUES "
+                             "(?, ?, ?)", (key, self._sqlite_encode(value), self._session_id))
 
-    def get_information(self, key):
+    def get_information(self, key, default):
         """retrieves one information's item
 
         :param key: the item string (ex: "help")
         """
-        value = self._storage.select("SELECT value FROM information WHERE key = ?",
-                                    (key,))
+        value = self._storage.select("SELECT value FROM information WHERE key = ? AND "
+                                     "session_id = ?",
+                                    (key, self._session_id))
         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:
             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
index a600990..afe8ea5 100644 (file)
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 "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
 # 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_submulti_index=force_submulti_index,
                               display_error=False,
-                              display_warnings=display_warnings)
+                              display_warnings=display_warnings,
+                              setting_properties=setting_properties,)
         if config_error is not None:
             return config_error
         return value