new api documentation
[tiramisu.git] / tiramisu / setting.py
index abeb4a7..7bb7360 100644 (file)
@@ -21,6 +21,7 @@
 # the whole pypy projet is under MIT licence
 # ____________________________________________________________
 from time import time
+from copy import copy
 from tiramisu.error import RequirementRecursionError, PropertiesOptionError
 from tiramisu.i18n import _
 
@@ -144,6 +145,7 @@ populate_multitypes()
 
 
 class Property(object):
+    "a property is responsible of the option's value access rules"
     __slots__ = ('_setting', '_properties', '_opt')
 
     def __init__(self, setting, prop, opt=None):
@@ -152,22 +154,21 @@ class Property(object):
         self._properties = prop
 
     def append(self, propname):
-        if not propname in self._properties:
-            self._properties.append(propname)
-            self._setting._set_properties(self._properties, self._opt)
+        self._properties.add(propname)
+        self._setting._set_properties(self._properties, self._opt)
         self._setting.context.cfgimpl_reset_cache()
 
     def remove(self, propname):
         if propname in self._properties:
             self._properties.remove(propname)
             self._setting._set_properties(self._properties, self._opt)
-        self._setting.context.cfgimpl_reset_cache()
+            self._setting.context.cfgimpl_reset_cache()
 
     def __contains__(self, propname):
         return propname in self._properties
 
     def __repr__(self):
-        return str(self._properties)
+        return str(list(self._properties))
 
 
 #____________________________________________________________
@@ -178,7 +179,7 @@ class Setting(object):
     def __init__(self, context):
         # properties attribute: the name of a property enables this property
         # key is None for global properties
-        self._properties = {None: ['expire']}
+        self._properties = {None: set(('expire', 'validator'))}
         # permissive properties
         self._permissives = {}
         # generic owner
@@ -192,7 +193,7 @@ class Setting(object):
         return propname in self._get_properties()
 
     def __repr__(self):
-        return str(self._get_properties())
+        return str(list(self._get_properties()))
 
     def __getitem__(self, opt):
         return Property(self, self._get_properties(opt), opt)
@@ -201,20 +202,19 @@ class Setting(object):
         raise ValueError('you must only append/remove properties')
 
     def _get_properties(self, opt=None, is_apply_req=True):
-        if opt is not None and opt in self._cache:
-            exp = time()
-            props, created = self._cache[opt]
-            if exp < created:
-                return props
         if opt is None:
-            default = []
+            props = self._properties.get(opt, set())
         else:
+            exp = None
+            if opt in self._cache:
+                exp = time()
+                props, created = self._cache[opt]
+                if exp < created:
+                    return props
             if is_apply_req:
                 apply_requires(opt, self.context)
-            default = list(opt._properties)
-        props = self._properties.get(opt, default)
-        if opt is not None:
-            self._set_cache(opt, props)
+            props = self._properties.get(opt, opt._properties)
+            self._set_cache(opt, props, exp)
         return props
 
     def append(self, propname):
@@ -238,69 +238,54 @@ class Setting(object):
             else:
                 self._properties[opt] = properties
 
-    def _validate_frozen(self, opt, value, is_write):
-        if not is_write:
-            return False
-        if 'permissive' in self and 'frozen' in self._get_permissive():
-            return False
-        if 'everything_frozen' in self or (
-                'frozen' in self and 'frozen' in self[opt]):
-            return True
-        return False
-
-    def _validate_mandatory(self, opt, value, force_properties):
-        if 'permissive' in self and 'mandatory' in self._get_permissive():
-            return False
-        check_mandatory = 'mandatory' in self
-        if force_properties is not None:
-            check_mandatory = ('mandatory' in force_properties or
-                               check_mandatory)
-        if check_mandatory and 'mandatory' in self[opt] and \
-                self.context.cfgimpl_get_values()._is_empty(opt, value):
-            return True
-        return False
-
-    def _calc_properties(self, opt_or_descr, force_permissive, force_properties):
-        properties = set(self._get_properties(opt_or_descr))
-        #remove this properties, those properties are validate in after
-        properties = properties - set(['mandatory', 'frozen'])
-        set_properties = set(self._get_properties())
-        if force_properties is not None:
-            set_properties.update(set(force_properties))
-        properties = properties & set_properties
-        if force_permissive is True or 'permissive' in self:
-            properties = properties - set(self._get_permissive())
-        properties = properties - set(self._get_permissive(opt_or_descr))
-        return list(properties)
-
     #____________________________________________________________
     def validate_properties(self, opt_or_descr, is_descr, is_write,
                             value=None, force_permissive=False,
                             force_properties=None):
-        properties = self._calc_properties(opt_or_descr, force_permissive,
-                                           force_properties)
-        raise_text = _("trying to access"
-                       " to an option named: {0} with properties"
-                       " {1}")
-        if not is_descr:
-            if self._validate_mandatory(opt_or_descr, value, force_properties):
-                properties.append('mandatory')
-            if self._validate_frozen(opt_or_descr, value, is_write):
-                properties.append('frozen')
-                raise_text = _('cannot change the value for '
-                               'option {0} this option is frozen')
-        if properties != []:
-            raise PropertiesOptionError(raise_text.format(opt_or_descr._name,
-                                                          str(properties)),
-                                        properties)
+        #opt properties
+        properties = copy(self._get_properties(opt_or_descr))
+        #remove opt permissive
+        properties -= self._get_permissive(opt_or_descr)
+        #remove global permissive if need
+        self_properties = copy(self._get_properties())
+        if force_permissive is True or 'permissive' in self_properties:
+            properties -= self._get_permissive()
+
+        #global properties
+        if force_properties is not None:
+            self_properties.update(force_properties)
+
+        #calc properties
+        properties &= self_properties
+        #mandatory and frozen are special properties
+        if is_descr:
+            properties -= frozenset(('mandatory', 'frozen'))
+        else:
+            if 'mandatory' in properties and \
+                    not self.context.cfgimpl_get_values()._is_empty(opt_or_descr,
+                                                                    value):
+                properties.remove('mandatory')
+            if is_write and 'everything_frozen' in self_properties:
+                properties.add('frozen')
+            elif 'frozen' in properties and not is_write:
+                properties.remove('frozen')
+
+        if properties != frozenset():
+            if 'frozen' in properties:
+                raise_text = 'cannot change the value for option {0} this option is frozen'
+            else:
+                raise_text = "trying to access to an option named: {0} with properties {1}"
+            raise PropertiesOptionError(_(raise_text).format(opt_or_descr._name,
+                                        str(properties)),
+                                        list(properties))
 
     def _get_permissive(self, opt=None):
-        return self._permissives.get(opt, [])
+        return self._permissives.get(opt, frozenset())
 
     def set_permissive(self, permissive, opt=None):
         if not isinstance(permissive, tuple):
             raise TypeError(_('permissive must be a tuple'))
-        self._permissives[opt] = permissive
+        self._permissives[opt] = frozenset(permissive)
 
     #____________________________________________________________
     def setowner(self, owner):
@@ -327,10 +312,11 @@ class Setting(object):
         "convenience method to freeze, hidde and disable"
         self._read(rw_remove, rw_append)
 
-    def _set_cache(self, opt, props):
+    def _set_cache(self, opt, props, exp):
         if 'expire' in self:
+            if exp is None:
+                exp = time()
             self._cache[opt] = (props, time() + expires_time)
-            pass
 
     def reset_cache(self, only_expired):
         if only_expired:
@@ -359,15 +345,17 @@ def apply_requires(opt, config):
         settings = config.cfgimpl_get_settings()
         setting = Property(settings, settings._get_properties(opt, False), opt)
         trigger_actions = build_actions(opt._requires)
-        optpath = config.cfgimpl_get_context().cfgimpl_get_description().optimpl_get_path_by_opt(opt)
+        descr = config.cfgimpl_get_context().cfgimpl_get_description()
+        optpath = descr.impl_get_path_by_opt(opt)
         for requires in trigger_actions.values():
             matches = False
             for require in requires:
                 if len(require) == 3:
-                    path, expected, action = require
+                    option, expected, action = require
                     inverse = False
                 elif len(require) == 4:
-                    path, expected, action, inverse = require
+                    option, expected, action, inverse = require
+                path = descr.impl_get_path_by_opt(option)
                 if path == optpath or path.startswith(optpath + '.'):
                     raise RequirementRecursionError(_("malformed requirements "
                                                     "imbrication detected for option: '{0}' "