adds an extend API for the settings
[tiramisu.git] / doc / config.txt
1 .. default-role:: literal
2
3 ===============================
4 Options handling basics
5 ===============================
6
7 Tiramisu is made of almost three main objects :
8
9 - :class:`tiramisu.option.Option` stands for the option types
10 - :class:`tiramisu.option.OptionDescription` is the shema, the option's structure
11 - :class:`tiramisu.config.Config` which is the whole configuration entry point
12
13 Accessing the `Option`'s
14 -------------------------
15
16 The :class:`~tiramisu.config.Config` object attribute access notation stands for 
17 the value of the configuration's :class:`~tiramisu.option.Option`.
18 :class:`~tiramisu.config.Config`'s object attribute is the name of the option, 
19 and the value is the value accessed by the `__getattr__` attribute access 
20 mechanism.
21
22 If the attribute of the `Config` called by `__getattr__` has not been set before
23 (by the classic `__setattr__` mechanism), the default value of the `Option`
24 object is returned, and if no `Option` has been declared in the
25 `OptionDescription` (that is the schema of the configuration), an
26 `AttributeError` is raised.
27
28 ::
29
30     >>> from tiramisu.config import Config 
31     >>> from tiramisu.option import BoolOption, OptionDescription
32     >>> 
33     >>> gcdummy = BoolOption('dummy', 'dummy', default=False)
34     >>> gcdummy.impl_getdefault()
35     False
36     >>> cfg.dummy
37     False
38     >>> descr = OptionDescription('tiramisu', '', [gcdummy])
39     >>> cfg = Config(descr)
40     >>> cfg.dummy = True
41     >>> cfg.dummy
42     True
43     >>> cfg.idontexist
44     AttributeError: 'OptionDescription' object has no attribute 'idontexist'
45
46 The `Option` objects (in this case the :class:`~tiramisu.option.BoolOption`), 
47 are organized into a tree into nested 
48 :class:`~tiramisu.option.OptionDescription` objects. 
49
50 .. image:: config.png
51
52 Every option has a name, as does every option group. The parts 
53 of the full name of the option are separated by dots: e.g. 
54 ``cfg.optgroup.optname``.
55
56 Let's make the protocol of accessing a `Config`'s attribute explicit
57 (because explicit is better than implicit):
58
59 1. If the option has not been declared, an `AttributeError` is raised,
60
61 2. If an option is declared, but neither a value nor a default value has
62    been set, the returned value is `None`,
63
64 3. If an option is declared and a default value has been set, but no value
65    has been set, the returned value is the default value of the option,
66
67 4. If an option is declared, and a value has been set, the returned value is
68    the value of the option.
69
70 But there are special exceptions. We will see later on that an option can be a
71 :term:`mandatory option`. A mandatory option is an option that must have a value 
72 defined.
73
74 Setting the value of an option
75 ------------------------------
76
77 An important part of the setting's configuration consists of setting the
78 value's option. There are different ways of setting values,
79 the first one is of course the `__setattr__` method
80
81 ::
82
83     cfg.name = value
84
85 And if you wanna come back to a default value, use the builtin `del()` function::
86
87     del(cfg.name)
88
89 .. module:: tiramisu.config
90
91 .. _`tree`:
92
93 The handling of options
94 ~~~~~~~~~~~~~~~~~~~~~~~~~~
95
96 The handling of options is split into two parts: the description of
97 which options are available, what their possible values and defaults are
98 and how they are organized into a tree. A specific choice of options is
99 bundled into a configuration object which has a reference to its option
100 description (and therefore makes sure that the configuration values
101 adhere to the option description).
102
103 Common manipulations
104 ------------------------
105
106 Let's perform some common manipulation on some options
107
108 >>> from tiramisu.config import Config
109 >>> from tiramisu.option import UnicodeOption, OptionDescription
110 >>> #
111 >>> var1 = UnicodeOption('var1', 'first variable')
112 >>> var2 = UnicodeOption('var2', '', u'value')
113 >>> #
114 >>> od1 = OptionDescription('od1', 'first OD', [var1, var2])
115 >>> rootod = OptionDescription('rootod', '', [od1])
116
117 let's set somme access rules on the main namespace
118
119 >>> c = Config(rootod)
120 >>> c.read_write()
121
122 let's travel the namespaces
123
124 >>> print c
125 [od1]
126 >>> print c.od1
127 var1 = None
128 var2 = value
129 >>> print c.od1.var1
130 None
131 >>> print c.od1.var2
132 value
133
134 let's modify a value (be careful to the value's type...)
135
136 >>> c.od1.var1 = 'value'
137 Traceback (most recent call last):
138 ValueError: invalid value value for option var1
139 >>> c.od1.var1 = u'value'
140 >>> print c.od1.var1
141 value
142 >>> c.od1.var2 = u'value2'
143 >>> print c.od1.var2
144 value2
145
146 let's come back to the default value
147
148 >>> del(c.od1.var2)
149 >>> print c.od1.var2
150 value
151
152 The value is saved in a :class:`~tiramisu.value.Value` object. It is on this 
153 object that we have to trigger the `reset`, which take the option itself 
154 (`var2`) as a parameter.
155
156 On the other side, in the `read_only` mode, it is not possible to modify the value
157
158 >>> c.read_only()
159 >>> c.od1.var2 = u'value2'
160 Traceback (most recent call last):
161 tiramisu.error.PropertiesOptionError: cannot change the value for option var2 this option is frozen
162
163
164 let's retrieve the option `var1` description
165
166 >>> var1.impl_get_information('doc')
167 'first variable'
168
169 And if the option has been lost, it is possible to retrieve it again:
170
171 >>> c.unwrap_from_path('od1.var1').impl_get_information('doc')
172 'first variable'
173
174 Searching for an option
175 ~~~~~~~~~~~~~~~~~~~~~~~~~~
176
177 In an application, knowing the path of an option is not always feasible. 
178 That's why a tree of options can easily be searched. First, let's build such a tree::
179
180 >>> var1 = UnicodeOption('var1', '')
181 >>> var2 = UnicodeOption('var2', '')
182 >>> var3 = UnicodeOption('var3', '')
183 >>> od1 = OptionDescription('od1', '', [var1, var2, var3])
184 >>> var4 = UnicodeOption('var4', '')
185 >>> var5 = UnicodeOption('var5', '')
186 >>> var6 = UnicodeOption('var6', '')
187 >>> var7 = UnicodeOption('var1', '', u'value')
188 >>> od2 = OptionDescription('od2', '', [var4, var5, var6, var7])
189 >>> rootod = OptionDescription('rootod', '', [od1, od2])
190 >>> c = Config(rootod)
191 >>> c.read_write()
192
193 Second, let's find an option by it's name::
194
195     >>> print c.find(byname='var1')
196     [<tiramisu.option.UnicodeOption object at 0x7ff1bf7d6ef0>, 
197     <tiramisu.option.UnicodeOption object at 0x7ff1b90c7290>] 
198     
199 If the option name is unique, the search can be stopped once one matched option 
200 has been found:
201
202     >>> print c.find_first(byname='var1')
203     <tiramisu.option.UnicodeOption object at 0x7ff1bf7d6ef0>
204
205 Instead of the option's object, the value or path can be retrieved:
206
207     >>> print c.find(byname='var1', type_='value')
208     [None, u'value']
209     >>> print c.find(byname='var1', type_='path')
210     ['od1.var1', 'od2.var1']
211     
212 Finaly, a search can be performed on the values, the type or even a combination 
213 of all these criteria:
214
215
216     >>> print c.find(byvalue=u'value', type_='path')
217     ['od2.var1']
218     >>> print c.find(bytype=UnicodeOption, type_='path')
219     ['od1.var1', 'od1.var2', 'od1.var3', 'od2.var4', 'od2.var5', 'od2.var6', 'od2.var1']
220     >>> print c.find(byvalue=u'value', byname='var1', bytype=UnicodeOption, type_='path')
221     ['od2.var1']
222     
223 The search can be performed in a subtree:
224
225 >>> print c.od1.find(byname='var1', type_='path')
226 ['od1.var1']
227
228 In a root tree or in a subtree, all option can be retrieved in a dict container:
229
230     >>> print c.make_dict()
231     {'od2.var4': None, 'od2.var5': None, 'od2.var6': None, 'od2.var1': u'value', 
232     'od1.var1': None, 'od1.var3': None, 'od1.var2': None}
233     
234 If the organisation in a tree is not important, 
235 :meth:`~config.SubConfig.make_dict()` results can be flattened
236
237 >>> print c.make_dict(flatten=True)
238 {'var5': None, 'var4': None, 'var6': None, 'var1': u'value', 'var3': None, 
239 'var2': None}
240
241 .. note:: be carefull with this `flatten` parameter, here we have just lost 
242                two options named `var1`
243
244 One can export only interesting parts of a tree of options into a dict, for 
245 example the options that are in the same group that a given `var1` option::
246
247     >>> print c.make_dict(withoption='var1')
248     {'od2.var4': None, 'od2.var5': None, 'od2.var6': None, 'od2.var1': u'value', 
249     'od1.var1': None, 'od1.var3': None, 'od1.var2': None}
250     >>> print c.make_dict(withoption='var1', withvalue=u'value')
251     {'od2.var4': None, 'od2.var5': None, 'od2.var6': None, 'od2.var1': u'value'}
252     
253 and of course, :meth:`~config.SubConfig.make_dict()` can be called in a subtree:
254
255 >>> print c.od1.make_dict(withoption='var1')
256 {'var1': None, 'var3': None, 'var2': None}
257
258 The owners
259 ~~~~~~~~~~~
260
261 .. glossary::
262
263     owner
264
265         When a value is set on an option, an owner is set too, that's why one can know 
266         at any time if a value is a default value or not. Let's create a tree::
267
268             >>> var1 = UnicodeOption('var1', '', u'oui')
269             >>> od1 = OptionDescription('od1', '', [var1])
270             >>> rootod = OptionDescription('rootod', '', [od1])
271             >>> c = Config(rootod)
272             >>> c.read_write()
273             
274 Then let's retrieve the owner associated to an option::
275
276    >>> print c.getowner(var1)
277    default
278    >>> c.od1.var1 = u'no'
279    >>> print c.getowner(var1)
280    user
281    >>> del(c.var1)
282    >>> print c.getowner(var1)
283    default
284
285 You can create your own owner, for example to distinguish modification made by
286 one user to an other one's.
287
288    >>> from tiramisu.setting import owners
289    >>> owners.addowner('toto')
290    >>> c.cfgimpl_get_settings().setowner(owners.toto)
291    >>> print c.getowner(var1)
292    default
293    >>> c.od1.var1 = u'no'
294    >>> print c.getowner(var1)
295    toto
296  
297 The properties
298 ~~~~~~~~~~~~~~
299
300 A property is an information on an option's state. 
301 Let's create options with properties::
302
303     >>> var1 = UnicodeOption('var1', '', u'value', properties=('hidden',))
304     >>> var2 = UnicodeOption('var2', '', properties=('mandatory',))
305     >>> var3 = UnicodeOption('var3', '', u'value', properties=('frozen', 'unknown'))
306     >>> var4 = UnicodeOption('var4', '', u'value')
307     >>> od1 = OptionDescription('od1', '', [var1, var2, var3])
308     >>> od2 = OptionDescription('od2', '', [var4], properties=('hidden',))
309     >>> rootod = OptionDescription('rootod', '', [od1, od2])
310     >>> c = Config(rootod)
311     >>> c.read_write()
312     
313 A hidden value is a value that cannot be accessed in read/write mode. This 
314 option cannot be modified any more. Let's try to access to an option's value 
315 with a hidden option::
316
317     >>> print c.od1.var1
318     Traceback (most recent call last):
319     tiramisu.error.PropertiesOptionError: trying to access to an option named: var1 
320     with properties ['hidden']
321     >>> c.read_only()
322     >>> print c.od1.var1
323     value
324     
325 A mandatory option is an option with a value that shall not be `None`. The 
326 value has to be defined. Accessing to such an option is easy in read/write 
327 mode. But in read only mode, an error is raised if no value has been defined:: 
328
329     >>> c.read_write()
330     >>> print c.od1.var2
331     None
332     >>> c.read_only()
333     >>> print c.od1.var2
334     Traceback (most recent call last):
335     tiramisu.error.PropertiesOptionError: trying to access to an option named: var2 
336     with properties ['mandatory']
337     >>> c.read_write()
338     >>> c.od1.var2 = u'value'
339     >>> c.read_only()
340     >>> print c.od1.var2
341     value
342     
343 A frozen option, is an option that cannot be modified by a user. 
344 Let's try to modify a frozen option::
345
346     >>> c.read_write()
347     >>> print c.od1.var3
348     value
349     >>> c.od1.var3 = u'value2'
350     Traceback (most recent call last):
351     tiramisu.error.PropertiesOptionError: cannot change the value for option var3 this option is frozen
352     >>> c.read_only()
353     >>> print c.od1.var3
354     value
355     
356 Tiramisu allows us to use user defined properties. Let's define and use one in 
357 read/write or read only mode::
358
359     >>> c.cfgimpl_get_settings().append('unknown')
360     >>> print c.od1.var3
361     Traceback (most recent call last):
362     tiramisu.error.PropertiesOptionError: trying to access to an option named: 
363     var3 with properties ['unknown']
364     >>> c.cfgimpl_get_settings().remove('unknown')
365     >>> print c.od1.var3
366     value 
367
368 Many properties can be defined at the same time on an option::
369
370     >>> c.cfgimpl_get_settings().extend(['unknown1', 'unknown2'])
371     
372 Properties can also be defined on an option group (that is, on an 
373 :term:`option description`) let's hide a group and try to access to it::
374
375     >>> c.read_write()
376     >>> print c.od2.var4
377     Traceback (most recent call last):
378     tiramisu.error.PropertiesOptionError: trying to access to an option named: od2 
379     with properties ['hidden']
380     >>> c.read_only()
381     >>> print c.od2.var4
382     value
383     
384 Furthermore, let's retrieve the properties, delete and add the `hidden` property::
385
386     >>> c.read_write()
387     >>> c.cfgimpl_get_settings()[rootod.od1.var1]
388     ['hidden']
389     >>> print c.od1.var1
390     Traceback (most recent call last):
391     tiramisu.error.PropertiesOptionError: trying to access to an option named: 
392     var1 with properties ['hidden']
393     >>> c.cfgimpl_get_settings()[rootod.od1.var1].remove('hidden')
394     >>> c.cfgimpl_get_settings()[rootod.od1.var1]
395     []
396     >>> print c.od1.var1
397     value
398     >>> c.cfgimpl_get_settings()[rootod.od1.var1].append('hidden')
399     >>> c.cfgimpl_get_settings()[rootod.od1.var1]
400     ['hidden']
401     >>> print c.od1.var1
402     Traceback (most recent call last):
403     tiramisu.error.PropertiesOptionError: trying to access to an option named: 
404     var1 with properties ['hidden']
405
406
407 .. _multi-option:
408     
409 The multi-options
410 ~~~~~~~~~~~~~~~~~~~~~
411
412 .. glossary:: 
413
414     multi-option
415
416         Multi-options are normal options that have list of values (multiple values) 
417         instead of values::
418         
419             >>> var1 = UnicodeOption('var1', '', [u'val1', u'val2'], multi=True)
420             >>> od1 = OptionDescription('od1', '', [var1])
421             >>> rootod = OptionDescription('rootod', '', [od1])
422             >>> c = Config(rootod)
423             >>> c.read_write()
424             
425 A multi-option's value can be manipulated like a list::
426
427     >>> print c.od1.var1
428     [u'val1', u'val2']
429     >>> c.od1.var1 = [u'var1']
430     >>> print c.od1.var1
431     [u'var1']
432     >>> c.od1.var1.append(u'val3')
433     >>> print c.od1.var1
434     [u'var1', u'val3']
435     >>> c.od1.var1.pop(1)
436     u'val3'
437     >>> print c.od1.var1
438     [u'var1']
439
440 But it is not possible to set a value to a multi-option which is not a list::    
441
442     >>> c.od1.var1 = u'error'
443     Traceback (most recent call last):
444     ValueError: invalid value error for option var1 which must be a list
445     
446
447 The master/slave groups
448 ~~~~~~~~~~~~~~~~~~~~~~~~~
449
450
451 .. glossary:: 
452
453     master/slave 
454
455         A master/slave group is an :class:`~tiramisu.option.OptionDescription` and the 
456         options that lives inside.
457         
458         Inside this group, a special option, named master option, has the same name as 
459         the group. The group (the option description) is set to type `master`. 
460         All options in a master group is a multi-option (see :ref:`multi-option`).
461         The slave options have a `default_multi` attribute set to `True`::
462         
463                 >>> from tiramisu.setting import groups
464                 >>> from tiramisu.config import Config
465                 >>> from tiramisu.option import UnicodeOption, OptionDescription
466                 >>>
467                 >>> var1 = UnicodeOption('master', '', multi=True)
468                 >>> var2 = UnicodeOption('slave1', '', multi=True)
469                 >>> var3 = UnicodeOption('slave2', '', multi=True, default_multi=u"default")
470                 >>>
471                 >>> od1 = OptionDescription('master', '', [var1, var2, var3])
472                 >>> od1.impl_set_group_type(groups.master)
473                 >>>
474                 >>> rootod = OptionDescription('rootod', '', [od1])
475                 >>> c = Config(rootod)
476                 >>> c.read_write()
477                 
478 The length of the lists can be modified::
479
480     >>> print c.master
481     master = []
482     slave1 = []
483     slave2 = []
484     >>> c.master.master.append(u'oui')
485     >>> print c.master
486     master = [u'oui']
487     slave1 = [None]
488     slave2 = [u'default']
489     >>> c.master.master = [u'non']
490     >>> print c.master
491     master = [u'non']
492     slave1 = [None]
493     slave2 = [u'default']
494     >>>
495     >>> c.master.master = [u'oui', u'non']
496     >>> print c.master
497     master = [u'oui', u'non']
498     slave1 = [None, None]
499     slave2 = [u'default', u'default']
500
501 But it is forbidden to change the lenght of a slave::
502
503     >>> c.master.slave1[0] = u'super'
504     >>> print c.master
505     master = [u'oui', u'non']
506     slave1 = [u'super', None]
507     slave2 = [u'default', u'default']
508     >>> c.master.slave1 = [u'new1', u'new2']
509     >>> print c.master
510     master = [u'oui', u'non']
511     slave1 = [u'new1', u'new2']
512     slave2 = [u'default', u'default']
513     >>> c.master.slave1 = [u'new1']
514     Traceback (most recent call last):
515     tiramisu.error.SlaveError: invalid len for the slave: slave1 which has master.master as master
516     >>> c.master.slave1 = [u'new1', u'new2', u'new3']
517     tiramisu.error.SlaveError: invalid len for the slave: slave1 which has master.master as master
518     
519 you have to call the `pop` function on the master::
520
521     >>> c.master.master = [u'oui']
522     Traceback (most recent call last):
523     tiramisu.error.SlaveError: invalid len for the master: master which has slave1 as slave with greater len
524     >>> c.master.master.pop(0)
525     u'oui'
526     >>> print c.master
527     master = [u'non']
528     slave1 = [u'new2']
529     slave2 = [u'default']
530              
531 Configuration's interesting methods 
532 ------------------------------------------
533
534 A `Config` object is informed by an `option.OptionDescription`
535 instance. The attributes of the ``Config`` objects are the names of the
536 children of the ``OptionDescription``.
537
538 Here are the (useful) methods on ``Config`` (or `SubConfig`).
539
540 .. currentmodule:: tiramisu.config
541
542 .. class:: Config
543
544 .. autoclass:: SubConfig
545     :members: find, find_first, __iter__, iter_groups, iter_all, make_dict
546
547     .. automethod:: __init__
548
549     .. rubric:: Summary
550
551     .. autosummary::
552
553        find
554        find_first
555
556        __iter__
557        iter_groups
558        iter_all
559
560        make_dict
561
562     .. rubric:: Methods
563
564
565 A :class:`~config.CommonConfig` is a abstract base class. A
566 :class:`~config.SubConfig` is an just in time created objects that wraps an
567 ::class:`~option.OptionDescription`. A SubConfig differs from a Config in the
568 fact that a config is a root object and has an environnement, a context which
569 defines the different properties, access rules, vs... There is generally only
570 one Config, and many SubConfigs.