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