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