sqlalchemy has a storage
[tiramisu.git] / tiramisu / setting.py
index 1dba144..c461d5e 100644 (file)
@@ -105,7 +105,7 @@ rw_remove = set(['permissive', 'everything_frozen', 'mandatory'])
 
 
 # ____________________________________________________________
-class _NameSpace:
+class _NameSpace(object):
     """convenient class that emulates a module
     and builds constants (that is, unique names)
     when attribute is added, we cannot delete it
@@ -243,6 +243,14 @@ populate_multitypes()
 
 
 # ____________________________________________________________
+class Undefined():
+    pass
+
+
+undefined = Undefined()
+
+
+# ____________________________________________________________
 class Property(object):
     "a property is responsible of the option's value access rules"
     __slots__ = ('_setting', '_properties', '_opt', '_path')
@@ -254,21 +262,43 @@ class Property(object):
         self._properties = prop
 
     def append(self, propname):
-        if self._opt is not None and self._opt._calc_properties is not None \
-                and propname in self._opt._calc_properties:
+        """Appends a property named propname
+
+        :param propname: a predefined or user defined property name
+        :type propname: string
+        """
+        if self._opt is not None and self._opt.impl_getrequires() is not None \
+                and propname in self._opt.impl_getrequires():
             raise ValueError(_('cannot append {0} property for option {1}: '
                                'this property is calculated').format(
-                                   propname, self._opt._name))
+                                   propname, self._opt.impl_getname()))
         self._properties.add(propname)
         self._setting._setproperties(self._properties, self._opt, self._path)
 
     def remove(self, propname):
+        """Removes a property named propname
+
+        :param propname: a predefined or user defined property name
+        :type propname: string
+        """
         if propname in self._properties:
             self._properties.remove(propname)
             self._setting._setproperties(self._properties, self._opt,
                                          self._path)
 
+    def extend(self, propnames):
+        """Extends properties to the existing properties
+
+        :param propnames: an iterable made of property names
+        :type propnames: iterable of string
+        """
+        for propname in propnames:
+            self.append(propname)
+
     def reset(self):
+        """resets the properties (does not **clear** the properties,
+        default properties are still present)
+        """
         self._setting.reset(_path=self._path)
 
     def __contains__(self, propname):
@@ -280,7 +310,7 @@ class Property(object):
 
 #____________________________________________________________
 class Settings(object):
-    "``Config()``'s configuration options"
+    "``config.Config()``'s configuration options settings"
     __slots__ = ('context', '_owner', '_p_', '__weakref__')
 
     def __init__(self, context, storage):
@@ -308,7 +338,7 @@ class Settings(object):
         return str(list(self._getproperties()))
 
     def __getitem__(self, opt):
-        path = self._get_opt_path(opt)
+        path = self._get_path_by_opt(opt)
         return self._getitem(opt, path)
 
     def _getitem(self, opt, path):
@@ -325,7 +355,7 @@ class Settings(object):
             self._p_.reset_all_propertives()
         else:
             if opt is not None and _path is None:
-                _path = self._get_opt_path(opt)
+                _path = self._get_path_by_opt(opt)
             self._p_.reset_properties(_path)
         self.context().cfgimpl_reset_cache()
 
@@ -343,7 +373,8 @@ class Settings(object):
                 is_cached, props = self._p_.getcache(path, ntime)
                 if is_cached:
                     return props
-            props = self._p_.getproperties(path, opt._properties)
+            #FIXME
+            props = self._p_.getproperties(path, opt.impl_getproperties())
             if is_apply_req:
                 props |= self.apply_requires(opt, path)
             if 'cache' in self:
@@ -374,24 +405,31 @@ class Settings(object):
         if opt is None:
             self._p_.setproperties(None, properties)
         else:
-            if opt._calc_properties is not None:
-                properties -= opt._calc_properties
-            if set(opt._properties) == properties:
-                self._p_.reset_properties(path)
-            else:
-                self._p_.setproperties(path, properties)
+            #if opt._calc_properties is not None:
+            #    properties -= opt._calc_properties
+            #FIXME a revoir ---------
+            #if set(opt._properties) == properties:
+            #    self._p_.reset_properties(path)
+            #else:
+            #    self._p_.setproperties(path, properties)
+            self._p_.setproperties(path, properties)
+            #FIXME fin revoir -----
         self.context().cfgimpl_reset_cache()
 
     #____________________________________________________________
     def validate_properties(self, opt_or_descr, is_descr, is_write, path,
                             value=None, force_permissive=False,
-                            force_properties=None):
+                            force_properties=None, force_permissives=None):
         """
         validation upon the properties related to `opt_or_descr`
 
         :param opt_or_descr: an option or an option description object
         :param force_permissive: behaves as if the permissive property
                                  was present
+        :param force_properties: set() with properties that is force to add
+                                 in global properties
+        :param force_permissives: set() with permissives that is force to add
+                                 in global permissives
         :param is_descr: we have to know if we are in an option description,
                          just because the mandatory property
                          doesn't exist here
@@ -402,12 +440,16 @@ class Settings(object):
         """
         # opt properties
         properties = copy(self._getproperties(opt_or_descr, path))
+        self_properties = copy(self._getproperties())
         # remove opt permissive
+        # permissive affect option's permission with or without permissive
+        # global property
         properties -= self._p_.getpermissive(path)
         # remove global permissive if need
-        self_properties = copy(self._getproperties())
         if force_permissive is True or 'permissive' in self_properties:
             properties -= self._p_.getpermissive()
+        if force_permissives is not None:
+            properties -= force_permissives
 
         # global properties
         if force_properties is not None:
@@ -434,12 +476,12 @@ class Settings(object):
                 raise PropertiesOptionError(_('cannot change the value for '
                                               'option {0} this option is'
                                               ' frozen').format(
-                                                  opt_or_descr._name),
+                                                  opt_or_descr.impl_getname()),
                                             props)
             else:
                 raise PropertiesOptionError(_("trying to access to an option "
                                               "named: {0} with properties {1}"
-                                              "").format(opt_or_descr._name,
+                                              "").format(opt_or_descr.impl_getname(),
                                                          str(props)), props)
 
     def setpermissive(self, permissive, opt=None, path=None):
@@ -453,7 +495,7 @@ class Settings(object):
                     instead of passing a :class:`tiramisu.option.Option()` object.
         """
         if opt is not None and path is None:
-            path = self._get_opt_path(opt)
+            path = self._get_path_by_opt(opt)
         if not isinstance(permissive, tuple):
             raise TypeError(_('permissive must be a tuple'))
         self._p_.setpermissive(path, permissive)
@@ -484,6 +526,11 @@ class Settings(object):
         self._read(rw_remove, rw_append)
 
     def reset_cache(self, only_expired):
+        """reset all settings in cache
+
+        :param only_expired: if True reset only expired cached values
+        :type only_expired: boolean
+        """
         if only_expired:
             self._p_.reset_expired_cache(int(time()))
         else:
@@ -499,12 +546,13 @@ class Settings(object):
 
         let's have a look at all the tuple's items:
 
-        - **option** is the target option's name or path
+        - **option** is the target option's
 
-        - **expected** is the target option's value that is going to trigger an action
+        - **expected** is the target option's value that is going to trigger
+          an action
 
-        - **action** is the (property) action to be accomplished if the target option
-          happens to have the expected value
+        - **action** is the (property) action to be accomplished if the target
+          option happens to have the expected value
 
         - if **inverse** is `True` and if the target option's value does not
           apply, then the property action must be removed from the option's
@@ -532,45 +580,69 @@ class Settings(object):
         :param path: the option's path in the config
         :type path: str
         """
-        if opt._requires is None:
+        if opt.impl_getrequires() is None:
             return frozenset()
 
         # filters the callbacks
         calc_properties = set()
-        for requires in opt._requires:
-            for require in requires:
-                option, expected, action, inverse, \
-                    transitive, same_action = require
-                reqpath = self._get_opt_path(option)
-                if reqpath == path or reqpath.startswith(path + '.'):
-                    raise RequirementError(_("malformed requirements "
-                                             "imbrication detected for option:"
-                                             " '{0}' with requirement on: "
-                                             "'{1}'").format(path, reqpath))
-                try:
-                    value = self.context()._getattr(reqpath,
-                                                    force_permissive=True)
-                except PropertiesOptionError as err:
-                    if not transitive:
-                        continue
-                    properties = err.proptype
-                    if same_action and action not in properties:
-                        raise RequirementError(_("option '{0}' has "
-                                                 "requirement's property "
-                                                 "error: "
-                                                 "{1} {2}").format(opt._name,
-                                                                   reqpath,
-                                                                   properties))
-                    # transitive action, force expected
-                    value = expected[0]
-                    inverse = False
-                if (not inverse and
-                        value in expected or
-                        inverse and value not in expected):
-                    calc_properties.add(action)
-                    # the calculation cannot be carried out
-                    break
+        for require in opt.impl_getrequires():
+            expected = tuple(require.get_expected())
+            inverse = require.inverse
+            option = require.option
+            reqpath = self._get_path_by_opt(option)
+            if reqpath == path or reqpath.startswith(path + '.'):
+                raise RequirementError(_("malformed requirements "
+                                         "imbrication detected for option:"
+                                         " '{0}' with requirement on: "
+                                         "'{1}'").format(path, reqpath))
+            try:
+                value = self.context()._getattr(reqpath,
+                                                force_permissive=True)
+            except PropertiesOptionError as err:
+                if not require.transitive:
+                    continue
+                properties = err.proptype
+                if require.same_action and require.action not in properties:
+                    raise RequirementError(_("option '{0}' has "
+                                             "requirement's property "
+                                             "error: "
+                                             "{1} {2}").format(opt.impl_getname(),
+                                                               reqpath,
+                                                               properties))
+                # transitive action, force expected
+                value = expected[0]
+                inverse = False
+            if not inverse and value in expected or \
+                    inverse and value not in expected:
+                calc_properties.add(require.action)
+                # the calculation cannot be carried out
+                #break
         return calc_properties
 
-    def _get_opt_path(self, opt):
+    def _get_path_by_opt(self, opt):
+        """just a wrapper to get path in optiondescription's cache
+
+        :param opt: `Option`'s object
+        :returns: path
+        """
         return self.context().cfgimpl_get_description().impl_get_path_by_opt(opt)
+
+    def get_modified_properties(self):
+        return self._p_.get_modified_properties()
+
+    def get_modified_permissives(self):
+        return self._p_.get_modified_permissives()
+
+    def __getstate__(self):
+        return {'_p_': self._p_, '_owner': str(self._owner)}
+
+    def _impl_setstate(self, storage):
+        self._p_._storage = storage
+
+    def __setstate__(self, states):
+        self._p_ = states['_p_']
+        try:
+            self._owner = getattr(owners, states['_owner'])
+        except AttributeError:
+            owners.addowner(states['_owner'])
+            self._owner = getattr(owners, states['_owner'])