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