master/slave, multi... in the glossary
authorgwen <gremond@cadoles.com>
Tue, 27 Aug 2013 14:35:15 +0000 (16:35 +0200)
committergwen <gremond@cadoles.com>
Tue, 27 Aug 2013 14:35:15 +0000 (16:35 +0200)
doc/config.txt
doc/consistency.txt

index d9a1f5a..17bddf3 100644 (file)
@@ -267,15 +267,19 @@ and of course, :meth:`~config.SubConfig.make_dict()` can be called in a subtree:
 the owners
 ~~~~~~~~~~~
 
-When a value is set on an option, an owner is set too, that's why one can know 
-at any time if a value is a default value or not. Let's create a tree::
+.. glossary::
 
-    >>> var1 = UnicodeOption('var1', '', u'oui')
-    >>> od1 = OptionDescription('od1', '', [var1])
-    >>> rootod = OptionDescription('rootod', '', [od1])
-    >>> c = Config(rootod)
-    >>> c.read_write()
-    
+    owner
+
+        When a value is set on an option, an owner is set too, that's why one can know 
+        at any time if a value is a default value or not. Let's create a tree::
+
+            >>> var1 = UnicodeOption('var1', '', u'oui')
+            >>> od1 = OptionDescription('od1', '', [var1])
+            >>> rootod = OptionDescription('rootod', '', [od1])
+            >>> c = Config(rootod)
+            >>> c.read_write()
+            
 Then let's retrieve the owner associated to an option::
 
     >>> print c.getowner('var1')
@@ -399,184 +403,135 @@ Furthermore, let's retrieve the properties, delete and add the `hidden` property
     tiramisu.error.PropertiesOptionError: trying to access to an option named: 
     var1 with properties ['hidden']
 
-The requirements
-~~~~~~~~~~~~~~~~~~
 
-Let's create an option wich has requirements::
+.. _multi-option:
+    
+The multi-options
+~~~~~~~~~~~~~~~~~~~~~
 
-    >>> from tiramisu.option import *
-    >>> from tiramisu.config import *
-    >>> var2 = UnicodeOption('var2', '', u'oui')
-    >>> var1 = UnicodeOption('var1', '', u'value', requires=[{'option':var2, 'expected':u'non', 'action':'hidden'}])
-    >>> var3 = UnicodeOption('var3', '', u'value', requires=[{'option':var2, 'expected':u'non', 'action':'hidden'}, {'option':var2, 'expected':u'non', 'action':'disabled'}])
-    >>> var4 = UnicodeOption('var4', '', u'oui')
-    >>> od1 = OptionDescription('od1', '', [var1, var2, var3])
-    >>> od2 = OptionDescription('od2', '', [var4], requires=[{'option':od1.var2, 'expected':u'oui', 'action':'hidden', 'inverse':True}])
-    >>> rootod = OptionDescription('rootod', '', [od1, od2])
-    >>> c = Config(rootod)
-    >>> c.read_write()
+.. glossary:: 
 
-The requirement here is the dict `{'option':var2, 'expected':u'non', 
-'action':'hidden'}` wich means that is the option `'od1.var2'` is set to 
-`'non'`, the option `'od1.var1'` is gonna be hidden. On the other hand, if the 
-option `'od1.var2'` is different from `'non'`, the option `'od1.var1'` is not 
-hidden any more::
+    multi-option
+
+        Multi-options are normal options that have list of values (multiple values) 
+        instead of values::
+        
+            >>> var1 = UnicodeOption('var1', '', [u'val1', u'val2'], multi=True)
+            >>> od1 = OptionDescription('od1', '', [var1])
+            >>> rootod = OptionDescription('rootod', '', [od1])
+            >>> c = Config(rootod)
+            >>> c.read_write()
+            
+A multi-option's value can be manipulated like a list::
 
-    >>> print c.cfgimpl_get_settings()[rootod.od1.var1]
-    []
     >>> print c.od1.var1
-    value
-    >>> print c.od1.var2
-    oui
-    >>> c.od1.var2 = u'non'
-    >>> print c.cfgimpl_get_settings()[rootod.od1.var1]
-    ['hidden']
+    [u'val1', u'val2']
+    >>> c.od1.var1 = [u'var1']
     >>> print c.od1.var1
-    Traceback (most recent call last):
-    [...]
-    tiramisu.error.PropertiesOptionError: trying to access to an option named: 
-    var1 with properties ['hidden']
-    >>> c.od1.var2 = u'oui'
-    >>> print c.cfgimpl_get_settings()[rootod.od1.var1]
-    []
+    [u'var1']
+    >>> c.od1.var1.append(u'val3')
     >>> print c.od1.var1
-    value
-    
-The requirement on `od2` is `{'option':od1.var2, 'expected':u'oui', 
-'action':'hidden', 'inverse':True}`, which means that if the option `od1.var2` 
-is set to `oui`, the option is not hidden (because of the `True` at the end of 
-the tuple wich means 'inverted', take a look at the :doc:`consistency` 
-document.)::
+    [u'var1', u'val3']
+    >>> c.od1.var1.pop(1)
+    u'val3'
+    >>> print c.od1.var1
+    [u'var1']
 
-    >>> print c.od2.var4
-    oui
-    >>> c.od1.var2 = u'non'
-    >>> print c.od2.var4
+But it is not possible to set a value to a multi-option wich is not a list::    
+
+    >>> c.od1.var1 = u'error'
     Traceback (most recent call last):
     [...]
-    tiramisu.error.PropertiesOptionError: trying to access to an option named: od2 with properties ['hidden']
-    >>> c.od1.var2 = u'oui'
-    >>> print c.od2.var4
-    oui
+    ValueError: invalid value error for option var1 which must be a list
     
-Requirements can be accumulated 
-
-    >>> print c.cfgimpl_get_settings()[rootod.od1.var3]
-    []
-    >>> c.od1.var2 = u'non'
-    >>> print c.cfgimpl_get_settings()[rootod.od1.var3]
-    ['disabled', 'hidden']
-    >>> c.od1.var2 = u'oui'
-    >>> print c.cfgimpl_get_settings()[rootod.od1.var3]
-    []
-
-Requirements can be accumulated for different or identical properties (inverted 
-or not)::
 
-    >>> a = UnicodeOption('var3', '', u'value', requires=[{'option':od1.var2, 
-        'expected':'non', 'action':'hidden'}, {'option':od1.var1, 'expected':'oui', 
-        'action':'hidden'}])
-    >>> a = UnicodeOption('var3', '', u'value', requires=[{'option':od1.var2, 
-        'expected':'non', 'action':'hidden'}, {'option':od1.var1, 'excepted':'oui', 
-        'action':'disabled', 'inverse':True}])
-    
-But it is not possible to have inverted requirements on the same property. 
-Here is an impossible situation::
-
-    >>> a = UnicodeOption('var3', '', u'value', requires=[{'option':od1.var2, 
-        'expected':'non', 'action':'hidden'}, {'option':od1.var1, 'expected':'oui', 
-        'hidden', True}])
-    
+The master/slave groups
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+.. glossary:: 
+
+    master/slave 
+
+        A master/slave group is an :class:`~tiramisu.option.OptionDescription` and the 
+        options that lives inside.
+        
+        Inside this group, a special option, named master option, has the same name as 
+        the group. The group (the option description) is set to type `master`. 
+        All options in a master group is a multi-option (see :ref:`multi-option`).
+        The slave options have a `default_multi` attribute set to `True`::
+        
+                >>> from tiramisu.setting import groups
+                >>> from tiramisu.config import Config
+                >>> from tiramisu.option import UnicodeOption, OptionDescription
+                >>>
+                >>> var1 = UnicodeOption('master', '', multi=True)
+                >>> var2 = UnicodeOption('slave1', '', multi=True)
+                >>> var3 = UnicodeOption('slave2', '', multi=True, default_multi=u"default")
+                >>>
+                >>> od1 = OptionDescription('master', '', [var1, var2, var3])
+                >>> od1.impl_set_group_type(groups.master)
+                >>>
+                >>> rootod = OptionDescription('rootod', '', [od1])
+                >>> c = Config(rootod)
+                >>> c.read_write()
+                
+The length of the lists can be modified::
+
+    >>> print c.master
+    master = []
+    slave1 = []
+    slave2 = []
+    >>> c.master.master.append(u'oui')
+    >>> print c.master
+    master = [u'oui']
+    slave1 = [None]
+    slave2 = [u'default']
+    >>> c.master.master = [u'non']
+    >>> print c.master
+    master = [u'non']
+    slave1 = [None]
+    slave2 = [u'default']
+    >>>
+    >>> c.master.master = [u'oui', u'non']
+    >>> print c.master
+    master = [u'oui', u'non']
+    slave1 = [None, None]
+    slave2 = [u'default', u'default']
+
+But it is forbidden to change the lenght of a slave::
+
+    >>> c.master.slave1[0] = u'super'
+    >>> print c.master
+    master = [u'oui', u'non']
+    slave1 = [u'super', None]
+    slave2 = [u'default', u'default']
+    >>> c.master.slave1 = [u'new1', u'new2']
+    >>> print c.master
+    master = [u'oui', u'non']
+    slave1 = [u'new1', u'new2']
+    slave2 = [u'default', u'default']
+    >>> c.master.slave1 = [u'new1']
     Traceback (most recent call last):
     [...]
-    ValueError: inconsistency in action types for option: var3 action: hidden
-            
-The calculations
-~~~~~~~~~~~~~~~~~
-
-Let's create four calculation functions::
-
-    def return_calc():
-        #return an unicode value
-        return u'calc'
-    
-    def return_value(value):
-       return value
-    
-    def return_value_param(param=u''):
-       return param
-    
-    def return_no_value_if_non(value):
-        #if value is not u'non' return value
-        if value == u'non':
-            return None
-        else:
-            return value
-
-Then we create four options using theses functions::
-
-    >>> var1 = UnicodeOption('var1', '', callback=return_calc)
-    >>> var2 = UnicodeOption('var2', '', callback=return_value, callback_params={'': (u'value',)})
-    >>> var3 = UnicodeOption('var3', '', callback=return_value_param, callback_params={'param': (u'value_param',)})
-    >>> var4 = UnicodeOption('var4', '', callback=return_no_value_if_non, callback_params={'': (('od1.var5', False),)})
-    >>> var5 = UnicodeOption('var5', '', u'oui')
-    >>> od1 = OptionDescription('od1', '', [var1, var2, var3, var4, var5])
-    >>> rootod = OptionDescription('rootod', '', [od1])
-    >>> c = Config(rootod)
-    >>> c.read_write()
-
-The first option `var1` returns the result of the `return_calc` function, wich 
-is `u'calc'`::
-
-    >>> print c.od1.var1
-    calc
-
-The second option `var2` returns the result of the `return_value` fucntion, 
-wich is `value`. The parameter `u'value'` is passed to this function::
-
-    >>> print c.od1.var2
-    value
-
-The third option `var3` returns the result of the function `return_value_param`
-with the named parameter `param` and the value `u'value_param'`::
-
-    >>> print c.od1.var3
-    value_param
-
-The fourth option `var4` returns the reslut of the function `return_no_value_if_non` 
-that is the value of `od1.var5` exceptif the value is  u`non`::
-
-    >>> print c.od1.var4
-    oui
-    >>> c.od1.var5 = u'new'
-    >>> print c.od1.var4
-    new
-    >>> c.od1.var5 = u'non'
-    >>> print c.od1.var4
-    None
-    
-The calculation replaces the default value. 
-If we modify the value, the calculation is not carried out any more::
-
-    >>> print c.od1.var1
-    calc
-    >>> c.od1.var1 = u'new_value'
-    >>> print c.od1.var1
-    new_value
+    tiramisu.error.SlaveError: invalid len for the slave: slave1 which has master.master as master
+    >>> c.master.slave1 = [u'new1', u'new2', u'new3']
+    [...]
+    tiramisu.error.SlaveError: invalid len for the slave: slave1 which has master.master as master
     
-To force the calculation to be carried out in some cases, one must add the 
-`frozen` and the `force_default_on_freeze` properties::
+you have to call the `pop` function on the master::
 
-    >>> c.cfgimpl_get_settings()[rootod.od1.var1].append('frozen')
-    >>> c.cfgimpl_get_settings()[rootod.od1.var1].append('force_default_on_freeze')
-    >>> print c.od1.var1
-    calc
-    >>> c.cfgimpl_get_settings()[rootod.od1.var1].remove('frozen')
-    >>> c.cfgimpl_get_settings()[rootod.od1.var1].remove('force_default_on_freeze')
-    >>> print c.od1.var1
-    new_value
-    
-     
+    >>> c.master.master = [u'oui']
+    Traceback (most recent call last):
+    [...]
+    tiramisu.error.SlaveError: invalid len for the master: master which has slave1 as slave with greater len
+    >>> c.master.master.pop(0)
+    u'oui'
+    >>> print c.master
+    master = [u'non']
+    slave1 = [u'new2']
+    slave2 = [u'default']
+             
 Configuration's interesting methods 
 ------------------------------------------
 
index 47561ff..7925fa3 100644 (file)
@@ -72,6 +72,96 @@ callback's action name (`hide`, `show`...), wich is a
 :class:`~setting.Property()`. Requirements are validated in 
 :class:`setting.Setting`.
 
+
+Let's create an option wich has requirements::
+
+    >>> from tiramisu.option import *
+    >>> from tiramisu.config import *
+    >>> var2 = UnicodeOption('var2', '', u'oui')
+    >>> var1 = UnicodeOption('var1', '', u'value', requires=[{'option':var2, 'expected':u'non', 'action':'hidden'}])
+    >>> var3 = UnicodeOption('var3', '', u'value', requires=[{'option':var2, 'expected':u'non', 'action':'hidden'}, {'option':var2, 'expected':u'non', 'action':'disabled'}])
+    >>> var4 = UnicodeOption('var4', '', u'oui')
+    >>> od1 = OptionDescription('od1', '', [var1, var2, var3])
+    >>> od2 = OptionDescription('od2', '', [var4], requires=[{'option':od1.var2, 'expected':u'oui', 'action':'hidden', 'inverse':True}])
+    >>> rootod = OptionDescription('rootod', '', [od1, od2])
+    >>> c = Config(rootod)
+    >>> c.read_write()
+
+The requirement here is the dict `{'option':var2, 'expected':u'non', 
+'action':'hidden'}` wich means that is the option `'od1.var2'` is set to 
+`'non'`, the option `'od1.var1'` is gonna be hidden. On the other hand, if the 
+option `'od1.var2'` is different from `'non'`, the option `'od1.var1'` is not 
+hidden any more::
+
+    >>> print c.cfgimpl_get_settings()[rootod.od1.var1]
+    []
+    >>> print c.od1.var1
+    value
+    >>> print c.od1.var2
+    oui
+    >>> c.od1.var2 = u'non'
+    >>> print c.cfgimpl_get_settings()[rootod.od1.var1]
+    ['hidden']
+    >>> print c.od1.var1
+    Traceback (most recent call last):
+    [...]
+    tiramisu.error.PropertiesOptionError: trying to access to an option named: 
+    var1 with properties ['hidden']
+    >>> c.od1.var2 = u'oui'
+    >>> print c.cfgimpl_get_settings()[rootod.od1.var1]
+    []
+    >>> print c.od1.var1
+    value
+    
+The requirement on `od2` is `{'option':od1.var2, 'expected':u'oui', 
+'action':'hidden', 'inverse':True}`, which means that if the option `od1.var2` 
+is set to `oui`, the option is not hidden (because of the `True` at the end of 
+the tuple wich means 'inverted', take a look at the :doc:`consistency` 
+document.)::
+
+    >>> print c.od2.var4
+    oui
+    >>> c.od1.var2 = u'non'
+    >>> print c.od2.var4
+    Traceback (most recent call last):
+    [...]
+    tiramisu.error.PropertiesOptionError: trying to access to an option named: od2 with properties ['hidden']
+    >>> c.od1.var2 = u'oui'
+    >>> print c.od2.var4
+    oui
+    
+Requirements can be accumulated 
+
+    >>> print c.cfgimpl_get_settings()[rootod.od1.var3]
+    []
+    >>> c.od1.var2 = u'non'
+    >>> print c.cfgimpl_get_settings()[rootod.od1.var3]
+    ['disabled', 'hidden']
+    >>> c.od1.var2 = u'oui'
+    >>> print c.cfgimpl_get_settings()[rootod.od1.var3]
+    []
+
+Requirements can be accumulated for different or identical properties (inverted 
+or not)::
+
+    >>> a = UnicodeOption('var3', '', u'value', requires=[{'option':od1.var2, 
+        'expected':'non', 'action':'hidden'}, {'option':od1.var1, 'expected':'oui', 
+        'action':'hidden'}])
+    >>> a = UnicodeOption('var3', '', u'value', requires=[{'option':od1.var2, 
+        'expected':'non', 'action':'hidden'}, {'option':od1.var1, 'excepted':'oui', 
+        'action':'disabled', 'inverse':True}])
+    
+But it is not possible to have inverted requirements on the same property. 
+Here is an impossible situation::
+
+    >>> a = UnicodeOption('var3', '', u'value', requires=[{'option':od1.var2, 
+        'expected':'non', 'action':'hidden'}, {'option':od1.var1, 'expected':'oui', 
+        'hidden', True}])
+    
+    Traceback (most recent call last):
+    [...]
+    ValueError: inconsistency in action types for option: var3 action: hidden
+            
 Validation upon a whole configuration object
 ----------------------------------------------
 
@@ -87,6 +177,36 @@ Other hooks are availables to validate upon a whole configuration at any time,
 for example the consistency between two options (typically, an 
 :class:`IPOption` and a :class:`NetworkOption`).
 
+Let's define validator (wich is a normal python function)::
+
+    >>> def valid_a(value, letter=''):
+    ...     return value.startswith(letter)
+    
+Here is an option wich uses this validator::
+
+    >>> var1 = UnicodeOption('var1', '', u'oui', validator=valid_a, validator_args={'letter': 'o'})
+    >>>
+    >>> od1 = OptionDescription('od1', '', [var1])
+    >>>
+    >>> rootod = OptionDescription('rootod', '', [od1])
+    >>>
+    >>> c = Config(rootod)
+    >>> c.read_write()
+    
+The validation is applied at the modification time::
+
+    >>> c.od1.var1 = u'non'
+    Traceback (most recent call last):
+    [...]
+    ValueError: invalid value non for option var1
+    >>> c.od1.var1 = u'oh non'
+    
+    Il est possible de d├ęsactiver la validation :
+    
+    >>> c.cfgimpl_get_settings().remove('validator')
+    >>> c.od1.var1 = u'non'
+    
+
 Values that are calculated
 --------------------------------
 
@@ -111,3 +231,84 @@ attribute.
         attribute `force_store_value` enabled is considered to be modified at 
         the first calculation
 
+Let's create four calculation functions::
+
+    def return_calc():
+        #return an unicode value
+        return u'calc'
+    
+    def return_value(value):
+       return value
+    
+    def return_value_param(param=u''):
+       return param
+    
+    def return_no_value_if_non(value):
+        #if value is not u'non' return value
+        if value == u'non':
+            return None
+        else:
+            return value
+
+Then we create four options using theses functions::
+
+    >>> var1 = UnicodeOption('var1', '', callback=return_calc)
+    >>> var2 = UnicodeOption('var2', '', callback=return_value, callback_params={'': (u'value',)})
+    >>> var3 = UnicodeOption('var3', '', callback=return_value_param, callback_params={'param': (u'value_param',)})
+    >>> var4 = UnicodeOption('var4', '', callback=return_no_value_if_non, callback_params={'': (('od1.var5', False),)})
+    >>> var5 = UnicodeOption('var5', '', u'oui')
+    >>> od1 = OptionDescription('od1', '', [var1, var2, var3, var4, var5])
+    >>> rootod = OptionDescription('rootod', '', [od1])
+    >>> c = Config(rootod)
+    >>> c.read_write()
+
+The first option `var1` returns the result of the `return_calc` function, wich 
+is `u'calc'`::
+
+    >>> print c.od1.var1
+    calc
+
+The second option `var2` returns the result of the `return_value` fucntion, 
+wich is `value`. The parameter `u'value'` is passed to this function::
+
+    >>> print c.od1.var2
+    value
+
+The third option `var3` returns the result of the function `return_value_param`
+with the named parameter `param` and the value `u'value_param'`::
+
+    >>> print c.od1.var3
+    value_param
+
+The fourth option `var4` returns the reslut of the function `return_no_value_if_non` 
+that is the value of `od1.var5` exceptif the value is  u`non`::
+
+    >>> print c.od1.var4
+    oui
+    >>> c.od1.var5 = u'new'
+    >>> print c.od1.var4
+    new
+    >>> c.od1.var5 = u'non'
+    >>> print c.od1.var4
+    None
+    
+The calculation replaces the default value. 
+If we modify the value, the calculation is not carried out any more::
+
+    >>> print c.od1.var1
+    calc
+    >>> c.od1.var1 = u'new_value'
+    >>> print c.od1.var1
+    new_value
+    
+To force the calculation to be carried out in some cases, one must add the 
+`frozen` and the `force_default_on_freeze` properties::
+
+    >>> c.cfgimpl_get_settings()[rootod.od1.var1].append('frozen')
+    >>> c.cfgimpl_get_settings()[rootod.od1.var1].append('force_default_on_freeze')
+    >>> print c.od1.var1
+    calc
+    >>> c.cfgimpl_get_settings()[rootod.od1.var1].remove('frozen')
+    >>> c.cfgimpl_get_settings()[rootod.od1.var1].remove('force_default_on_freeze')
+    >>> print c.od1.var1
+    new_value