sqlalchemy has a storage
authorEmmanuel Garette <egarette@cadoles.com>
Mon, 20 Jan 2014 13:53:08 +0000 (14:53 +0100)
committerEmmanuel Garette <egarette@cadoles.com>
Mon, 20 Jan 2014 13:53:14 +0000 (14:53 +0100)
test/test_option_calculation.py
tiramisu/autolib.py
tiramisu/option.py
tiramisu/setting.py
tiramisu/storage/sqlalchemy/__init__.py [new file with mode: 0644]
tiramisu/storage/sqlalchemy/option.py [new file with mode: 0644]
tiramisu/storage/util.py

index 2cacf5e..8c73a5a 100644 (file)
@@ -481,6 +481,7 @@ def test_callback_master_and_slaves_slave_list():
     assert cfg.val1.val2 == ['val', 'val']
     cfg.val1.val1 = ['val1']
     #wrong len
+    print cfg.val1.val2
     raises(SlaveError, 'cfg.val1.val2')
 
 
index 503bcdd..f61cf18 100644 (file)
@@ -148,7 +148,7 @@ def carry_out_calculation(option, config, callback, callback_params,
         for callbk in callbacks.params:
             if callbk.option is not None:
                 # callbk is something link (opt, True|False)
-                opt = callbk.get_option(config)
+                opt = callbk.option
                 force_permissive = callbk.force_permissive
                 path = config.cfgimpl_get_description().impl_get_path_by_opt(
                     opt)
index 7de14a2..b6e01e8 100644 (file)
@@ -28,17 +28,15 @@ from IPy import IP
 import warnings
 #from pickle import loads, dumps
 
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy import create_engine, Column, Integer, String, Boolean, \
-    PickleType, ForeignKey, Table
-from sqlalchemy.orm import relationship, backref
-from sqlalchemy.orm import sessionmaker
-
 from tiramisu.error import ConfigError, ConflictError, ValueWarning
 from tiramisu.setting import groups, multitypes
 from tiramisu.i18n import _
 from tiramisu.autolib import carry_out_calculation
 
+#FIXME : need storage...
+from tiramisu.storage.sqlalchemy.option import StorageBase, StorageOptionDescription
+from sqlalchemy.ext.declarative import declarative_base, declared_attr
+
 name_regexp = re.compile(r'^\d+')
 forbidden_names = ('iter_all', 'iter_group', 'find', 'find_first',
                    'make_dict', 'unwrap_from_path', 'read_only',
@@ -58,170 +56,7 @@ def valid_name(name):
         return False
 #____________________________________________________________
 #
-
-engine = create_engine('sqlite:///:memory:')
-Base = declarative_base()
-
-
-class _RequireExpected(Base):
-    __tablename__ = 'expected'
-    id = Column(Integer, primary_key=True)
-    expected = Column(PickleType)
-    require = Column(Integer, ForeignKey('require.id'))
-
-    def __init__(self, expected):
-        self.expected = expected
-
-
-class _RequireOption(Base):
-    __tablename__ = 'require'
-    id = Column(Integer, primary_key=True)
-    option = Column(Integer, ForeignKey('baseoption.id'))
-    r_opt = Column(Integer)
-    expected = relationship("_RequireExpected")
-    action = Column(String, nullable=False)
-    inverse = Column(Boolean, default=False)
-    transitive = Column(Boolean, default=True)
-    same_action = Column(Boolean, default=True)
-
-    def __init__(self, option, expected, action, inverse, transitive,
-                 same_action):
-        self.r_opt = option.id
-        for expect in expected:
-            self.expected.append(_RequireExpected(expect))
-        self.action = action
-        self.inverse = inverse
-        self.transitive = transitive
-        self.same_action = same_action
-
-    def get_expected(self):
-        for expected in self.expected:
-            yield(expected.expected)
-
-    def get_option(self, config):
-        return config.cfgimpl_get_description().impl_get_opt_by_id(self.r_opt)
-
-
-property_table = Table('property', Base.metadata,
-                       Column('left_id', Integer, ForeignKey('propertyoption.name')),
-                       Column('right_id', Integer, ForeignKey('baseoption.id'))
-                       )
-
-
-class _PropertyOption(Base):
-    __tablename__ = 'propertyoption'
-    name = Column(String, primary_key=True)
-
-    def __init__(self, name):
-        self.name = name
-
-
-class _Information(Base):
-    __tablename__ = 'information'
-    id = Column(Integer, primary_key=True)
-    option = Column(Integer, ForeignKey('baseoption.id'))
-    key = Column(String)
-    value = Column(PickleType)
-
-    def __init__(self, key, value):
-        self.key = key
-        self.value = value
-
-
-class _CallbackParamOption(Base):
-    __tablename__ = 'callback_param_option'
-    id = Column(Integer, primary_key=True)
-    callback_param = Column(Integer, ForeignKey('callback_param.id'))
-    option = Column(Integer)
-    force_permissive = Column(Boolean)
-    value = Column(PickleType)
-
-    def __init__(self, option=None, force_permissive=None,  value=None):
-        if value is not None:
-            self.value = value
-        else:
-            if isinstance(option, SymLinkOption):
-                option = option._opt
-            self.option = option.id
-            self.force_permissive = force_permissive
-
-    def get_option(self, config):
-        return config.cfgimpl_get_description().impl_get_opt_by_id(self.option)
-
-
-class _CallbackParam(Base):
-    __tablename__ = 'callback_param'
-    id = Column(Integer, primary_key=True)
-    callback = Column(Integer, ForeignKey('baseoption.id'))
-    name = Column(String)
-    params = relationship('_CallbackParamOption')
-
-    def __init__(self, name, params):
-        self.name = name
-        for param in params:
-            if isinstance(param, tuple):
-                self.params.append(_CallbackParamOption(option=param[0],
-                                                        force_permissive=param[1]))
-            else:
-                self.params.append(_CallbackParamOption(value=param))
-
-
-consistency_table = Table('consistencyopt', Base.metadata,
-                          Column('left_id', Integer, ForeignKey('consistency.id')),
-                          Column('right_id', Integer, ForeignKey('baseoption.id'))
-                          )
-
-
-class _Consistency(Base):
-    __tablename__ = 'consistency'
-    id = Column(Integer, primary_key=True)
-    func = Column(PickleType)
-
-    def __init__(self, func, all_cons_opts):
-        self.func = func
-        for option in all_cons_opts:
-            option._consistencies.append(self)
-
-
-class BaseOption(Base):
-    """This abstract base class stands for attribute access
-    in options that have to be set only once, it is of course done in the
-    __setattr__ method
-    """
-    __tablename__ = 'baseoption'
-    id = Column(Integer, primary_key=True)
-    _name = Column(String)
-    _informations = relationship('_Information')
-    _default = Column(PickleType)
-    _default_multi = Column(PickleType)
-    _requires = relationship('_RequireOption')
-    _multi = Column(Boolean)
-    _multitype = Column(String)
-    _callback = Column(PickleType)
-    _callback_params = relationship('_CallbackParam')
-    _validator = Column(PickleType)
-    _validator_params = relationship('_CallbackParam')
-    _parent = Column(Integer, ForeignKey('baseoption.id'))
-    _children = relationship('BaseOption', enable_typechecks=False)
-    _properties = relationship('_PropertyOption', secondary=property_table,
-                               backref=backref('options', enable_typechecks=False))
-    _warnings_only = Column(Boolean)
-    _readonly = Column(Boolean, default=False)
-    _consistencies = relationship('_Consistency', secondary=consistency_table,
-                                  backref=backref('options', enable_typechecks=False))
-    _choice_values = Column(PickleType)
-    _choice_open_values = Column(Boolean)
-    _type = Column(String(50))
-    __mapper_args__ = {
-        'polymorphic_identity': 'person',
-        'polymorphic_on': _type
-    }
-    #FIXME devrait etre une table
-    _optiondescription_group_type = Column(String)
-    #__slots__ = ('_name', '_requires', '_properties', '_readonly',
-    #             '_calc_properties', '_impl_informations',
-    #             '_state_readonly', '_state_requires', '_stated')
-
+class Base(StorageBase):
     def __init__(self, name, doc, default=None, default_multi=None,
                  requires=None, multi=False, callback=None,
                  callback_params=None, validator=None, validator_params=None,
@@ -235,7 +70,7 @@ class BaseOption(Base):
         if requires is not None:
             for values in requires.values():
                 for require in values.values():
-                    self._requires.append(_RequireOption(*require))
+                    self._add_require(require)
         if not multi and default_multi is not None:
             raise ValueError(_("a default_multi is set whereas multi is False"
                              " in option: {0}").format(name))
@@ -271,7 +106,7 @@ class BaseOption(Base):
             self._validator = validator
             if validator_params is not None:
                 for key, values in validator_params.items():
-                    self._validator_params.append(_CallbackParam(key, values))
+                    self._add_validator(key, values)
         if callback is None and callback_params is not None:
             raise ValueError(_("params defined for a callback function but "
                              "no callback defined"
@@ -281,7 +116,7 @@ class BaseOption(Base):
             self._callback = callback
             if callback_params is not None:
                 for key, values in callback_params.items():
-                    self._callback_params.append(_CallbackParam(key, values))
+                    self._add_callback(key, values)
         if requires is not None and properties is not tuple():
             set_forbidden_properties = set(properties) & set(requires.keys())
             if set_forbidden_properties != frozenset():
@@ -298,41 +133,19 @@ class BaseOption(Base):
         else:
             self._default = default
         for prop in properties:
-            prop_obj = session.query(_PropertyOption).filter(_PropertyOption.name == prop).first()
-            if prop_obj is None:
-                prop_obj = _PropertyOption(prop)
-            self._properties.append(prop_obj)
+            self._properties.append(self._get_property_object(prop))
         self._warnings_only = warnings_only
+        return super(Base, self).__init__()
 
-    # ____________________________________________________________
-    # information
-    def impl_set_information(self, key, value):
-        """updates the information's attribute
-        (which is a dictionary)
-
-        :param key: information's key (ex: "help", "doc"
-        :param value: information's value (ex: "the help string")
-        """
-        #FIXME pas append ! remplacer !
-        info = session.query(_Information).filter_by(option=self.id, key=key).first()
-        if info is None:
-            self._informations.append(_Information(key, value))
-        else:
-            info.value = value
-
-    def impl_get_information(self, key, default=None):
-        """retrieves one information's item
 
-        :param key: the item string (ex: "help")
-        """
-        info = session.query(_Information).filter_by(option=self.id, key=key).first()
-        if info is not None:
-            return info.value
-        elif default is not None:
-            return default
-        else:
-            raise ValueError(_("information's item not found: {0}").format(
-                key))
+class BaseOption(Base):
+    """This abstract base class stands for attribute access
+    in options that have to be set only once, it is of course done in the
+    __setattr__ method
+    """
+    #__slots__ = ('_name', '_requires', '_properties', '_readonly',
+    #             '_calc_properties', '_impl_informations',
+    #             '_state_readonly', '_state_requires', '_stated')
 
     # ____________________________________________________________
     # serialize object
@@ -500,8 +313,6 @@ class Option(BaseOption):
                                      validator_params, properties,
                                      warnings_only, choice_values,
                                      choice_open_values)
-        session.add(self)
-        session.commit()
 
     #def __setattr__(self, name, value):
     #    """set once and only once some attributes in the option,
@@ -749,7 +560,7 @@ class Option(BaseOption):
             else:
                 self._launch_consistency(func, self, value, None,
                                          None, all_cons_opts)
-        _Consistency(func, all_cons_opts)
+        self._add_consistency(func, all_cons_opts)
         self.impl_validate(self.impl_getdefault())
 
     def _cons_not_equal(self, opts, vals):
@@ -856,10 +667,6 @@ class ChoiceOption(Option):
     """
 
     #__slots__ = ('_values', '_open_values')
-    __mapper_args__ = {
-        'polymorphic_identity': 'choice',
-    }
-
     def __init__(self, name, doc, values, default=None, default_multi=None,
                  requires=None, multi=False, callback=None,
                  callback_params=None, open_values=False, validator=None,
@@ -901,9 +708,6 @@ class ChoiceOption(Option):
 class BoolOption(Option):
     "represents a choice between ``True`` and ``False``"
 #    __slots__ = tuple()
-    __mapper_args__ = {
-        'polymorphic_identity': 'bool',
-    }
 
     def _validate(self, value):
         if not isinstance(value, bool):
@@ -913,9 +717,6 @@ class BoolOption(Option):
 class IntOption(Option):
     "represents a choice of an integer"
 #    __slots__ = tuple()
-    __mapper_args__ = {
-        'polymorphic_identity': 'int',
-    }
 
     def _validate(self, value):
         if not isinstance(value, int):
@@ -925,9 +726,6 @@ class IntOption(Option):
 class FloatOption(Option):
     "represents a choice of a floating point number"
     #__slots__ = tuple()
-    __mapper_args__ = {
-        'polymorphic_identity': 'float',
-    }
 
     def _validate(self, value):
         if not isinstance(value, float):
@@ -937,9 +735,6 @@ class FloatOption(Option):
 class StrOption(Option):
     "represents the choice of a string"
     #__slots__ = tuple()
-    __mapper_args__ = {
-        'polymorphic_identity': 'string',
-    }
 
     def _validate(self, value):
         if not isinstance(value, str):
@@ -955,9 +750,6 @@ else:
     class UnicodeOption(Option):
         "represents the choice of a unicode string"
         #__slots__ = tuple()
-        __mapper_args__ = {
-            'polymorphic_identity': 'unicode',
-        }
         _empty = u''
 
         def _validate(self, value):
@@ -967,9 +759,6 @@ else:
 
 class SymLinkOption(BaseOption):
     #__slots__ = ('_name', '_opt', '_state_opt', '_readonly', '_parent')
-    __mapper_args__ = {
-        'polymorphic_identity': 'symlink',
-    }
     #not return _opt consistencies
     #_consistencies = None
 
@@ -982,8 +771,7 @@ class SymLinkOption(BaseOption):
         self._opt = opt
         self._readonly = True
         self._parent = None
-        session.add(self)
-        session.commit()
+        self.commit()
 
     def __getattr__(self, name):
         if name in ('_opt', '_opt_type', '_readonly', 'impl_getname'):
@@ -1001,17 +789,13 @@ class SymLinkOption(BaseOption):
         super(SymLinkOption, self)._impl_setstate(descr)
 
     def impl_get_information(self, key, default=None):
-        #FIXME ne devrait pas etre util si ?
+        #FIXME ne devrait pas etre utile si ?
         return self._opt.impl_get_information(key, default)
 
 
 class IPOption(Option):
     "represents the choice of an ip"
     #__slots__ = ('_private_only', '_allow_reserved')
-    __mapper_args__ = {
-        'polymorphic_identity': 'ip',
-    }
-
     def __init__(self, name, doc, default=None, default_multi=None,
                  requires=None, multi=False, callback=None,
                  callback_params=None, validator=None, validator_params=None,
@@ -1061,10 +845,6 @@ class PortOption(Option):
     see: http://en.wikipedia.org/wiki/Port_numbers
     """
     #__slots__ = ('_allow_range', '_allow_zero', '_min_value', '_max_value')
-    __mapper_args__ = {
-        'polymorphic_identity': 'port',
-    }
-
     def __init__(self, name, doc, default=None, default_multi=None,
                  requires=None, multi=False, callback=None,
                  callback_params=None, validator=None, validator_params=None,
@@ -1126,10 +906,6 @@ class PortOption(Option):
 class NetworkOption(Option):
     "represents the choice of a network"
     #__slots__ = tuple()
-    __mapper_args__ = {
-        'polymorphic_identity': 'network',
-    }
-
     def _validate(self, value):
         try:
             IP(value)
@@ -1145,9 +921,6 @@ class NetworkOption(Option):
 class NetmaskOption(Option):
     "represents the choice of a netmask"
     #__slots__ = tuple()
-    __mapper_args__ = {
-        'polymorphic_identity': 'netmask',
-    }
 
     def _validate(self, value):
         try:
@@ -1201,9 +974,6 @@ class NetmaskOption(Option):
 
 class BroadcastOption(Option):
     #__slots__ = tuple()
-    __mapper_args__ = {
-        'polymorphic_identity': 'broadcast',
-    }
 
     def _validate(self, value):
         try:
@@ -1232,9 +1002,6 @@ class DomainnameOption(Option):
     fqdn: with tld, not supported yet
     """
     #__slots__ = ('_dom_type', '_allow_ip', '_allow_without_dot', '_domain_re')
-    __mapper_args__ = {
-        'polymorphic_identity': 'domainname',
-    }
 
     def __init__(self, name, doc, default=None, default_multi=None,
                  requires=None, multi=False, callback=None,
@@ -1297,9 +1064,6 @@ class DomainnameOption(Option):
 
 class EmailOption(DomainnameOption):
     #__slots__ = tuple()
-    __mapper_args__ = {
-        'polymorphic_identity': 'email',
-    }
     username_re = re.compile(r"^[\w!#$%&'*+\-/=?^`{|}~.]+$")
 
     def _validate(self, value):
@@ -1316,9 +1080,6 @@ class EmailOption(DomainnameOption):
 
 class URLOption(DomainnameOption):
     #__slots__ = tuple()
-    __mapper_args__ = {
-        'polymorphic_identity': 'url',
-    }
     proto_re = re.compile(r'(http|https)://')
     path_re = re.compile(r"^[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
 
@@ -1355,9 +1116,6 @@ class URLOption(DomainnameOption):
 
 class FilenameOption(Option):
     #__slots__ = tuple()
-    __mapper_args__ = {
-        'polymorphic_identity': 'file',
-    }
     path_re = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$")
 
     def _validate(self, value):
@@ -1366,7 +1124,7 @@ class FilenameOption(Option):
             raise ValueError(_('invalid filename'))
 
 
-class OptionDescription(BaseOption):
+class OptionDescription(BaseOption, StorageOptionDescription):
     """Config's schema (organisation, group) and container of Options
     The `OptionsDescription` objects lives in the `tiramisu.config.Config`.
     """
@@ -1375,9 +1133,6 @@ class OptionDescription(BaseOption):
     #          '_cache_consistencies', '_calc_properties', '__weakref__',
     #          '_readonly', '_impl_informations', '_state_requires',
     #          '_stated', '_state_readonly')
-    __mapper_args__ = {
-        'polymorphic_identity': 'optiondescription',
-    }
 
     def __init__(self, name, doc, children, requires=None, properties=None):
         """
@@ -1385,8 +1140,6 @@ class OptionDescription(BaseOption):
 
         """
         super(OptionDescription, self).__init__(name, doc=doc, requires=requires, properties=properties)
-        session.add(self)
-        session.commit()
         child_names = [child.impl_getname() for child in children]
         #better performance like this
         valid_child = copy(child_names)
@@ -1528,28 +1281,6 @@ class OptionDescription(BaseOption):
                         self._cache_consistencies[opt] = tuple(cons)
                 self._readonly = True
 
-    def impl_get_opt_by_path(self, path):
-        try:
-            #FIXME
-            idx = self._cache_paths[1].index(path)
-            opt_id = self._cache_paths[0][idx]
-            return session.query(BaseOption).filter_by(id=opt_id).first()
-        except ValueError:
-            raise AttributeError(_('no option for path {0}').format(path))
-
-    def impl_get_opt_by_id(self, opt_id):
-        try:
-            #FIXME
-            #idx = self._cache_paths[0].index(opt_id)
-            return session.query(BaseOption).filter_by(id=opt_id).first()
-        except ValueError:
-            raise AttributeError(_('no id {0} found').format(opt_id))
-
-    def impl_get_path_by_opt(self, opt):
-        try:
-            return self._cache_paths[1][self._cache_paths[0].index(opt.id)]
-        except ValueError:
-            raise AttributeError(_('no option {0} found').format(opt))
 
     # ____________________________________________________________
     def impl_set_group_type(self, group_type):
@@ -1800,8 +1531,3 @@ def validate_callback(callback, callback_params, type_):
                                            ' not a {0} for second argument'
                                            ).format(type_, type(
                                                force_permissive)))
-
-#FIXME
-Base.metadata.create_all(engine)
-Session = sessionmaker(bind=engine)
-session = Session()
index 6a69986..c461d5e 100644 (file)
@@ -588,7 +588,7 @@ class Settings(object):
         for require in opt.impl_getrequires():
             expected = tuple(require.get_expected())
             inverse = require.inverse
-            option = require.get_option(self.context())
+            option = require.option
             reqpath = self._get_path_by_opt(option)
             if reqpath == path or reqpath.startswith(path + '.'):
                 raise RequirementError(_("malformed requirements "
diff --git a/tiramisu/storage/sqlalchemy/__init__.py b/tiramisu/storage/sqlalchemy/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tiramisu/storage/sqlalchemy/option.py b/tiramisu/storage/sqlalchemy/option.py
new file mode 100644 (file)
index 0000000..b408daa
--- /dev/null
@@ -0,0 +1,284 @@
+# -*- coding: utf-8 -*-
+""
+# Copyright (C) 2014 Team tiramisu (see AUTHORS for all contributors)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+# ____________________________________________________________
+from tiramisu.setting import multitypes
+
+
+from sqlalchemy.ext.declarative import declarative_base, declared_attr
+from sqlalchemy import create_engine, Column, Integer, String, Boolean, \
+    PickleType, ForeignKey, Table
+from sqlalchemy.orm import relationship, backref
+from sqlalchemy.orm import sessionmaker
+
+
+#FIXME
+engine = create_engine('sqlite:///:memory:')
+SqlAlchemyBase = declarative_base()
+#____________________________________________________________
+#
+# require
+require_table = Table('require', SqlAlchemyBase.metadata,
+                      Column('left_id', Integer, ForeignKey('requireoption.id')),
+                      Column('right_id', Integer, ForeignKey('baseoption.id'))
+                      )
+
+class _RequireExpected(SqlAlchemyBase):
+    __tablename__ = 'expected'
+    id = Column(Integer, primary_key=True)
+    expected = Column(PickleType)
+    require = Column(Integer, ForeignKey('requireoption.id'))
+
+    def __init__(self, expected):
+        self.expected = expected
+
+
+class _RequireOption(SqlAlchemyBase):
+    __tablename__ = 'requireoption'
+    id = Column(Integer, primary_key=True)
+    r_opt = Column(Integer)
+    expected = relationship("_RequireExpected")
+    action = Column(String, nullable=False)
+    inverse = Column(Boolean, default=False)
+    transitive = Column(Boolean, default=True)
+    same_action = Column(Boolean, default=True)
+
+    def __init__(self, option, expected, action, inverse, transitive,
+                 same_action):
+        self.r_opt = option.id
+        for expect in expected:
+            self.expected.append(_RequireExpected(expect))
+        self.action = action
+        self.inverse = inverse
+        self.transitive = transitive
+        self.same_action = same_action
+
+    def get_expected(self):
+        for expected in self.expected:
+            yield(expected.expected)
+
+    #def get_option(self, config):
+    #    return config.cfgimpl_get_description().impl_get_opt_by_id(self.r_opt)
+
+
+#____________________________________________________________
+#
+# properties
+property_table = Table('property', SqlAlchemyBase.metadata,
+                       Column('left_id', Integer, ForeignKey('propertyoption.name')),
+                       Column('right_id', Integer, ForeignKey('baseoption.id'))
+                       )
+
+
+class _PropertyOption(SqlAlchemyBase):
+    __tablename__ = 'propertyoption'
+    name = Column(String, primary_key=True)
+
+    def __init__(self, name):
+        self.name = name
+
+
+#____________________________________________________________
+#
+# information
+class _Information(SqlAlchemyBase):
+    __tablename__ = 'information'
+    id = Column(Integer, primary_key=True)
+    option = Column(Integer, ForeignKey('baseoption.id'))
+    key = Column(String)
+    value = Column(PickleType)
+
+    def __init__(self, key, value):
+        self.key = key
+        self.value = value
+
+
+#____________________________________________________________
+#
+# callback
+class _CallbackParamOption(SqlAlchemyBase):
+    __tablename__ = 'callback_param_option'
+    id = Column(Integer, primary_key=True)
+    callback_param = Column(Integer, ForeignKey('callback_param.id'))
+    option = Column(Integer)
+    force_permissive = Column(Boolean)
+    value = Column(PickleType)
+
+    def __init__(self, option=None, force_permissive=None,  value=None):
+        if value is not None:
+            self.value = value
+        else:
+            self.option = option.id
+            self.force_permissive = force_permissive
+
+
+class _CallbackParam(SqlAlchemyBase):
+    __tablename__ = 'callback_param'
+    id = Column(Integer, primary_key=True)
+    callback = Column(Integer, ForeignKey('baseoption.id'))
+    name = Column(String)
+    params = relationship('_CallbackParamOption')
+
+    def __init__(self, name, params):
+        self.name = name
+        for param in params:
+            if isinstance(param, tuple):
+                self.params.append(_CallbackParamOption(option=param[0],
+                                                        force_permissive=param[1]))
+            else:
+                self.params.append(_CallbackParamOption(value=param))
+
+
+#____________________________________________________________
+#
+# consistency
+consistency_table = Table('consistencyopt', SqlAlchemyBase.metadata,
+                          Column('left_id', Integer, ForeignKey('consistency.id')),
+                          Column('right_id', Integer, ForeignKey('baseoption.id'))
+                          )
+
+
+class _Consistency(SqlAlchemyBase):
+    __tablename__ = 'consistency'
+    id = Column(Integer, primary_key=True)
+    func = Column(PickleType)
+
+    def __init__(self, func, all_cons_opts):
+        self.func = func
+        for option in all_cons_opts:
+            option._consistencies.append(self)
+
+
+#____________________________________________________________
+#
+# Base
+class _Base(SqlAlchemyBase):
+    __tablename__ = 'baseoption'
+    id = Column(Integer, primary_key=True)
+    _name = Column(String)
+    _informations = relationship('_Information')
+    _default = Column(PickleType)
+    _default_multi = Column(PickleType)
+    _requires = relationship('_RequireOption', secondary=require_table,
+                             backref=backref('option', enable_typechecks=False))
+    _multi = Column(Boolean)
+    _multitype = Column(String)
+    _callback = Column(PickleType)
+    _callback_params = relationship('_CallbackParam')
+    _validator = Column(PickleType)
+    _validator_params = relationship('_CallbackParam')
+    _parent = Column(Integer, ForeignKey('baseoption.id'))
+    _children = relationship('BaseOption', enable_typechecks=False)
+    _properties = relationship('_PropertyOption', secondary=property_table,
+                               backref=backref('options', enable_typechecks=False))
+    _warnings_only = Column(Boolean)
+    _readonly = Column(Boolean, default=False)
+    _consistencies = relationship('_Consistency', secondary=consistency_table,
+                                  backref=backref('options', enable_typechecks=False))
+    _choice_values = Column(PickleType)
+    _choice_open_values = Column(Boolean)
+    _type = Column(String(50))
+    __mapper_args__ = {
+        'polymorphic_identity': 'option',
+        'polymorphic_on': _type
+    }
+    #FIXME devrait etre une table
+    _optiondescription_group_type = Column(String)
+
+    def __init__(self):
+        self.commit()
+
+    def commit(self):
+        session.add(self)
+        session.commit()
+
+    def _get_property_object(self, propname):
+        prop_obj = session.query(_PropertyOption).filter(_PropertyOption.name == propname).first()
+        if prop_obj is None:
+            prop_obj = _PropertyOption(propname)
+        return prop_obj
+
+    def _add_require(self, require):
+        self._requires.append(_RequireOption(*require))
+
+    def _add_callback(self, key, values):
+        self._callback_params.append(_CallbackParam(key, values))
+        
+    def _add_validator(self, key, values):
+        self._validator_params.append(_CallbackParam(key, values))
+
+    def _add_consistency(self, func, all_cons_opts):
+        _Consistency(func, all_cons_opts)
+    # ____________________________________________________________
+    # information
+    def impl_set_information(self, key, value):
+        """updates the information's attribute
+        (which is a dictionary)
+
+        :param key: information's key (ex: "help", "doc"
+        :param value: information's value (ex: "the help string")
+        """
+        info = session.query(_Information).filter_by(option=self.id, key=key).first()
+        #FIXME pas append ! remplacer !
+        if info is None:
+            self._informations.append(_Information(key, value))
+        else:
+            info.value = value
+
+    def impl_get_information(self, key, default=None):
+        """retrieves one information's item
+
+        :param key: the item string (ex: "help")
+        """
+        info = session.query(_Information).filter_by(option=self.id, key=key).first()
+        if info is not None:
+            return info.value
+        elif default is not None:
+            return default
+        else:
+            raise ValueError(_("information's item not found: {0}").format(
+                key))
+
+
+class StorageOptionDescription(object):
+    def impl_get_opt_by_path(self, path):
+        try:
+            #FIXME
+            idx = self._cache_paths[1].index(path)
+            opt_id = self._cache_paths[0][idx]
+            return session.query(_Base).filter_by(id=opt_id).first()
+        except ValueError:
+            raise AttributeError(_('no option for path {0}').format(path))
+
+    def impl_get_path_by_opt(self, opt):
+        try:
+            return self._cache_paths[1][self._cache_paths[0].index(opt)]
+        except ValueError:
+            raise AttributeError(_('no option {0} found').format(opt))
+
+
+class StorageBase(_Base):
+    @declared_attr
+    def __mapper_args__(self):
+        return {'polymorphic_identity': self.__name__.lower()}
+
+
+#FIXME
+SqlAlchemyBase.metadata.create_all(engine)
+Session = sessionmaker(bind=engine)
+session = Session()
index 68482e6..88cf787 100644 (file)
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-"default plugin for cache: set it in a simple dictionary"
+"utils used by storage"
 # Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors)
 #
 # This program is free software; you can redistribute it and/or modify