Merge branch 'master' into orm
[tiramisu.git] / tiramisu / setting.py
index 684ec74..fc4241d 100644 (file)
@@ -24,7 +24,7 @@ from time import time
 from copy import copy
 import weakref
 from tiramisu.error import (RequirementError, PropertiesOptionError,
-                            ConstError)
+                            ConstError, ConfigError)
 from tiramisu.i18n import _
 
 
@@ -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 \
+        """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._calc_properties:
             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):
@@ -298,6 +328,17 @@ class Settings(object):
         self.context = weakref.ref(context)
         self._p_ = storage
 
+    def _getcontext(self):
+        """context could be None, we need to test it
+        context is None only if all reference to `Config` object is deleted
+        (for example we delete a `Config` and we manipulate a reference to
+        old `SubConfig`, `Values`, `Multi` or `Settings`)
+        """
+        context = self.context()
+        if context is None:
+            raise ConfigError(_('the context does not exist anymore'))
+        return context
+
     #____________________________________________________________
     # properties methods
     def __contains__(self, propname):
@@ -322,16 +363,16 @@ class Settings(object):
             raise ValueError(_('opt and all_properties must not be set '
                                'together in reset'))
         if all_properties:
-            self._p_.reset_all_propertives()
+            self._p_.reset_all_properties()
         else:
             if opt is not None and _path is None:
                 _path = self._get_path_by_opt(opt)
             self._p_.reset_properties(_path)
-        self.context().cfgimpl_reset_cache()
+        self._getcontext().cfgimpl_reset_cache()
 
     def _getproperties(self, opt=None, path=None, is_apply_req=True):
         if opt is None:
-            props = self._p_.getproperties(path, default_properties)
+            props = copy(self._p_.getproperties(path, default_properties))
         else:
             if path is None:
                 raise ValueError(_('if opt is not None, path should not be'
@@ -342,8 +383,8 @@ class Settings(object):
                     ntime = int(time())
                 is_cached, props = self._p_.getcache(path, ntime)
                 if is_cached:
-                    return props
-            props = self._p_.getproperties(path, opt._properties)
+                    return copy(props)
+            props = copy(self._p_.getproperties(path, opt._properties))
             if is_apply_req:
                 props |= self.apply_requires(opt, path)
             if 'cache' in self:
@@ -374,13 +415,14 @@ 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)
-        self.context().cfgimpl_reset_cache()
+            #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)
+            self._p_.setproperties(path, properties)
+        self._getcontext().cfgimpl_reset_cache()
 
     #____________________________________________________________
     def validate_properties(self, opt_or_descr, is_descr, is_write, path,
@@ -405,11 +447,13 @@ class Settings(object):
                          (typically with the `frozen` property)
         """
         # opt properties
-        properties = copy(self._getproperties(opt_or_descr, path))
+        properties = self._getproperties(opt_or_descr, path)
+        self_properties = 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:
@@ -426,7 +470,7 @@ class Settings(object):
             properties -= frozenset(('mandatory', 'frozen'))
         else:
             if 'mandatory' in properties and \
-                    not self.context().cfgimpl_get_values()._isempty(
+                    not self._getcontext().cfgimpl_get_values()._isempty(
                         opt_or_descr, value):
                 properties.remove('mandatory')
             if is_write and 'everything_frozen' in self_properties:
@@ -440,12 +484,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):
@@ -549,6 +593,7 @@ class Settings(object):
 
         # filters the callbacks
         calc_properties = set()
+        context = self._getcontext()
         for requires in opt._requires:
             for require in requires:
                 option, expected, action, inverse, \
@@ -560,8 +605,7 @@ class Settings(object):
                                              " '{0}' with requirement on: "
                                              "'{1}'").format(path, reqpath))
                 try:
-                    value = self.context()._getattr(reqpath,
-                                                    force_permissive=True)
+                    value = context._getattr(reqpath, force_permissive=True)
                 except PropertiesOptionError as err:
                     if not transitive:
                         continue
@@ -590,7 +634,7 @@ class Settings(object):
         :param opt: `Option`'s object
         :returns: path
         """
-        return self.context().cfgimpl_get_description().impl_get_path_by_opt(opt)
+        return self._getcontext().cfgimpl_get_description().impl_get_path_by_opt(opt)
 
     def get_modified_properties(self):
         return self._p_.get_modified_properties()