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