tiramisu/doc/config.txt

575 lines
18 KiB
Plaintext
Raw Normal View History

.. default-role:: literal
2012-11-20 17:14:58 +01:00
===============================
2013-08-23 11:42:22 +02:00
Options handling basics
2012-11-20 17:14:58 +01:00
===============================
2012-10-05 16:00:07 +02:00
2013-05-21 18:42:56 +02:00
Tiramisu is made of almost three main objects :
- :class:`tiramisu.config.Config` witch is the whole configuration entry point
2013-05-21 18:42:56 +02:00
- :class:`tiramisu.option.Option` stands for the option types
- :class:`tiramisu.option.OptionDescription` is the shema, the option's structure
2013-08-23 11:42:22 +02:00
Accessing the `Option`'s
-------------------------
The `Config` object attribute access notation stands for the value of the
configuration's `Option`. That is, the `Config`'s object attribute is the name
of the `Option`, and the value is the value accessed by the `__getattr__`
2012-10-05 16:00:07 +02:00
attribute access mechanism.
If the attribute of the `Config` called by `__getattr__` has not been set before
(by the classic `__setattr__` mechanism), the default value of the `Option`
object is returned, and if no `Option` has been declared in the
`OptionDescription` (that is the schema of the configuration), an
`AttributeError` is raised.
::
>>> gcdummy = BoolOption('dummy', 'dummy', default=False)
>>> gcdummy._name
'dummy'
>>> gcdummy.getdefault()
False
>>> descr = OptionDescription('tiramisu', '', [gcdummy])
>>> cfg = Config(descr)
>>> cfg.dummy
False
>>> cfg.dummy = True
>>> cfg.dummy
True
>>> cfg.idontexist
AttributeError: 'OptionDescription' object has no attribute 'idontexist'
2013-08-23 11:42:22 +02:00
The `Option` objects (in this case the `BoolOption`), are organized into a tree
into nested `OptionDescription` objects. Every option has a name, as does every
option group. The parts of the full name of the option are separated by dots:
e.g. ``cfg.optgroup.optname``.
2013-05-17 12:11:14 +02:00
Let's make the protocol of accessing a config's attribute explicit
(because explicit is better than implicit):
1. If the option has not been declared, an `AttributeError` is raised,
2. If an option is declared, but neither a value nor a default value has
been set, the returned value is `None`,
3. If an option is declared and a default value has been set, but no value
has been set, the returned value is the default value of the option,
4. If an option is declared, and a value has been set, the returned value is
the value of the option.
2013-08-23 11:42:22 +02:00
But there are special exceptions. We will see later on that an option can be a
:term:`mandatory option`. A mandatory option is an option that must have a defined value.
If no value have been set yet, the value is `None`.
When the option is called to retrieve a value, an exception is raised.
2013-08-21 17:21:09 +02:00
What if a value has been set and `None` is to be returned again ? Don't
2012-11-20 17:14:58 +01:00
worry, an option value can be "reseted" with the help of the `option.Option.reset()`
2012-11-14 11:30:11 +01:00
method.
If you know the path:
::
>>> config.gc.dummy
False
Setting the values of the options
----------------------------------------
An important part of the setting of the configuration consists of setting the
values of the configuration options. There are different ways of setting values,
2012-10-05 16:00:07 +02:00
the first one is of course the `__setattr__` method
::
cfg.name = value
2013-05-23 17:51:50 +02:00
And if you wanna come back to a default value, use the builtin `del()` function::
2013-05-21 18:42:56 +02:00
del(cfg.name)
2013-05-17 12:11:14 +02:00
.. module:: tiramisu.config
2013-08-22 12:17:10 +02:00
.. _`tree`:
The handling of options
~~~~~~~~~~~~~~~~~~~~~~~~~~
2013-05-17 12:11:14 +02:00
The handling of options is split into two parts: the description of
which options are available, what their possible values and defaults are
and how they are organized into a tree. A specific choice of options is
bundled into a configuration object which has a reference to its option
description (and therefore makes sure that the configuration values
adhere to the option description).
2013-08-23 11:42:22 +02:00
Common manipulations
------------------------
Let's perform some common manipulation on some options:
>>> from tiramisu.config import Config
>>> from tiramisu.option import UnicodeOption, OptionDescription
>>>
>>> var1 = UnicodeOption('var1', 'first variable')
>>> var2 = UnicodeOption('var2', '', u'value')
>>>
>>> od1 = OptionDescription('od1', 'first OD', [var1, var2])
>>> rootod = OptionDescription('rootod', '', [od1])
let's set somme access rules on the main namespace
>>> c = Config(rootod)
>>> c.read_write()
let's travel the namespaces
>>> print c
[od1]
>>> print c.od1
var1 = None
var2 = value
>>> print c.od1.var1
None
>>> print c.od1.var2
value
let's modify a value (careful to the value's type...)
>>> c.od1.var1 = 'value'
Traceback (most recent call last):
[...]
ValueError: invalid value value for option var1
>>> c.od1.var1 = u'value'
>>> print c.od1.var1
value
>>> c.od1.var2 = u'value2'
>>> print c.od1.var2
value2
let's come back to the default value
>>> del(c.od1.var2)
>>> print c.od1.var2
value
2013-08-26 17:14:14 +02:00
The value is saved in a :class:`~tiramisu.value.Value` object. It is on this
object that we have to trigger the `reset`, wich take the option itself
(`var2`) as a parameter.
2013-08-23 11:42:22 +02:00
2013-08-26 17:14:14 +02:00
On the other side, in the `read_only` mode, it is not possible to modify the value::
2013-08-23 11:42:22 +02:00
2013-08-26 17:14:14 +02:00
>>> c.read_only()
>>> c.od1.var2 = u'value2'
Traceback (most recent call last):
[...]
tiramisu.error.PropertiesOptionError:
cannot change the value to var2
for option ['frozen'] this option is frozen
let's retrieve the option `var1` description
2013-08-23 11:42:22 +02:00
2013-08-26 17:14:14 +02:00
>>> var1.impl_get_information('doc')
'first variable'
2013-08-23 11:42:22 +02:00
2013-08-26 17:14:14 +02:00
And if the option has been lost, it is possible to retrieve it again:
>>> c.unwrap_from_path('od1.var1').impl_get_information('doc')
'first variable'
Searching for an option
~~~~~~~~~~~~~~~~~~~~~~~~~~
In an application, knowing the path of an option is not always feasible.
That's why a tree of options can easily be searched. First, let's build such a tree::
>>> var1 = UnicodeOption('var1', '')
>>> var2 = UnicodeOption('var2', '')
>>> var3 = UnicodeOption('var3', '')
>>> od1 = OptionDescription('od1', '', [var1, var2, var3])
>>> var4 = UnicodeOption('var4', '')
>>> var5 = UnicodeOption('var5', '')
>>> var6 = UnicodeOption('var6', '')
>>> var7 = UnicodeOption('var1', '', u'value')
>>> od2 = OptionDescription('od2', '', [var4, var5, var6, var7])
>>> rootod = OptionDescription('rootod', '', [od1, od2])
>>> c = Config(rootod)
>>> c.read_write()
Second, let's find an option by his name::
>>> print c.find(byname='var1')
[<tiramisu.option.UnicodeOption object at 0x7ff1bf7d6ef0>,
<tiramisu.option.UnicodeOption object at 0x7ff1b90c7290>]
If the option name is unique, the search can be stopped once one matched option
has been found:
>>> print c.find_first(byname='var1')
<tiramisu.option.UnicodeOption object at 0x7ff1bf7d6ef0>
Instead of the option's object, the value or path can be retrieved:
>>> print c.find(byname='var1', type_='value')
[None, u'value']
>>> print c.find(byname='var1', type_='path')
['od1.var1', 'od2.var1']
Finaly, a search can be performed on the values, the type or even a combination
of all these criteria:
>>> print c.find(byvalue=u'value', type_='path')
['od2.var1']
>>> print c.find(bytype=UnicodeOption, type_='path')
['od1.var1', 'od1.var2', 'od1.var3', 'od2.var4', 'od2.var5', 'od2.var6', 'od2.var1']
>>> print c.find(byvalue=u'value', byname='var1', bytype=UnicodeOption, type_='path')
['od2.var1']
The search can be performed in a subtree:
>>> print c.od1.find(byname='var1', type_='path')
['od1.var1']
In a root tree or in a subtree, all option can be retrieved in a dict container:
>>> print c.make_dict()
{'od2.var4': None, 'od2.var5': None, 'od2.var6': None, 'od2.var1': u'value',
'od1.var1': None, 'od1.var3': None, 'od1.var2': None}
If the organisation in a tree is not important,
:meth:`~config.SubConfig.make_dict()` results can be flattened
>>> print c.make_dict(flatten=True)
{'var5': None, 'var4': None, 'var6': None, 'var1': u'value', 'var3': None,
'var2': None}
.. note:: carefull with this `flatten` parameter, here we have just lost
two options named `var1`
One can export only interesting parts of a tree of options into a dict, for
example the options that are in the same group that a given `var1` option::
>>> print c.make_dict(withoption='var1')
{'od2.var4': None, 'od2.var5': None, 'od2.var6': None, 'od2.var1': u'value',
'od1.var1': None, 'od1.var3': None, 'od1.var2': None}
>>> print c.make_dict(withoption='var1', withvalue=u'value')
{'od2.var4': None, 'od2.var5': None, 'od2.var6': None, 'od2.var1': u'value'}
and of course, :meth:`~config.SubConfig.make_dict()` can be called in a subtree:
>>> print c.od1.make_dict(withoption='var1')
{'var1': None, 'var3': None, 'var2': None}
the owners
~~~~~~~~~~~
2013-08-27 16:35:15 +02:00
.. glossary::
2013-08-26 17:14:14 +02:00
2013-08-27 16:35:15 +02:00
owner
When a value is set on an option, an owner is set too, that's why one can know
at any time if a value is a default value or not. Let's create a tree::
>>> var1 = UnicodeOption('var1', '', u'oui')
>>> od1 = OptionDescription('od1', '', [var1])
>>> rootod = OptionDescription('rootod', '', [od1])
>>> c = Config(rootod)
>>> c.read_write()
2013-08-26 17:14:14 +02:00
Then let's retrieve the owner associated to an option::
>>> print c.getowner('var1')
default
>>> c.od1.var1 = u'non'
>>> print c.getowner('var1')
user
>>> del(c.var1)
>>> print c.getowner('var1')
default
the properties
~~~~~~~~~~~~~~~~
A property is an information on an option's state.
Let's create options with properties::
>>> var1 = UnicodeOption('var1', '', u'value', properties=('hidden',))
>>> var2 = UnicodeOption('var2', '', properties=('mandatory',))
>>> var3 = UnicodeOption('var3', '', u'value', properties=('frozen', 'inconnu'))
>>> var4 = UnicodeOption('var4', '', u'value')
>>> od1 = OptionDescription('od1', '', [var1, var2, var3])
>>> od2 = OptionDescription('od2', '', [var4], properties=('hidden',))
>>> rootod = OptionDescription('rootod', '', [od1, od2])
>>> c = Config(rootod)
>>> c.read_write()
A hidden value is a value that cannot be accessed in read/write mode. This
option cannot be modified any more. Let's try to access to an option's value
with a hidden option::
>>> print c.od1.var1
Traceback (most recent call last):
[...]
tiramisu.error.PropertiesOptionError: trying to access to an option named: var1
with properties ['hidden']
>>> c.read_only()
>>> print c.od1.var1
value
A mandatory option is an option with a value that shall not be `None`. The
value has to be defined. Accessing to such an option is easy in read/write
mode. But in read only mode, an error is raised if no value has been defined::
>>> c.read_write()
>>> print c.od1.var2
None
>>> c.read_only()
>>> print c.od1.var2
Traceback (most recent call last):
[...]
tiramisu.error.PropertiesOptionError: trying to access to an option named: var2
with properties ['mandatory']
>>> c.read_write()
>>> c.od1.var2 = u'value'
>>> c.read_only()
>>> print c.od1.var2
value
A frozen option, is an option that cannot be modified by a user.
Let's try to modify a frozen option::
>>> c.read_write()
>>> print c.od1.var3
value
>>> c.od1.var3 = u'value2'
Traceback (most recent call last):
[...]
tiramisu.error.PropertiesOptionError: cannot change the value for option var3 this option is frozen
>>> c.read_only()
>>> print c.od1.var3
value
Tiramisu allows us to use user defined properties. Let's define and use one in
read/write or read only mode::
>>> c.cfgimpl_get_settings().append('inconnu')
>>> print c.od1.var3
Traceback (most recent call last):
[...]
tiramisu.error.PropertiesOptionError: trying to access to an option named:
var3 with properties ['inconnu']
>>> c.cfgimpl_get_settings().remove('inconnu')
>>> print c.od1.var3
value
2013-08-27 15:54:10 +02:00
Properties can also be defined on an option group, (that is, on an
:term:`option description`), let's hide a group and try to access to it::
2013-08-26 17:14:14 +02:00
>>> c.read_write()
>>> print c.od2.var4
Traceback (most recent call last):
[...]
tiramisu.error.PropertiesOptionError: trying to access to an option named: od2
with properties ['hidden']
>>> c.read_only()
>>> print c.od2.var4
value
Furthermore, let's retrieve the properties, delete and add the `hidden` property::
>>> c.read_write()
>>> c.cfgimpl_get_settings()[rootod.od1.var1]
['hidden']
>>> print c.od1.var1
Traceback (most recent call last):
[...]
tiramisu.error.PropertiesOptionError: trying to access to an option named:
var1 with properties ['hidden']
>>> c.cfgimpl_get_settings()[rootod.od1.var1].remove('hidden')
>>> c.cfgimpl_get_settings()[rootod.od1.var1]
[]
>>> print c.od1.var1
value
>>> c.cfgimpl_get_settings()[rootod.od1.var1].append('hidden')
>>> c.cfgimpl_get_settings()[rootod.od1.var1]
['hidden']
>>> print c.od1.var1
Traceback (most recent call last):
[...]
tiramisu.error.PropertiesOptionError: trying to access to an option named:
var1 with properties ['hidden']
2013-08-27 16:35:15 +02:00
.. _multi-option:
The multi-options
~~~~~~~~~~~~~~~~~~~~~
2013-08-26 17:14:14 +02:00
2013-08-27 16:35:15 +02:00
.. glossary::
2013-08-26 17:14:14 +02:00
2013-08-27 16:35:15 +02:00
multi-option
Multi-options are normal options that have list of values (multiple values)
instead of values::
>>> var1 = UnicodeOption('var1', '', [u'val1', u'val2'], multi=True)
>>> od1 = OptionDescription('od1', '', [var1])
>>> rootod = OptionDescription('rootod', '', [od1])
>>> c = Config(rootod)
>>> c.read_write()
A multi-option's value can be manipulated like a list::
2013-08-26 17:14:14 +02:00
>>> print c.od1.var1
2013-08-27 16:35:15 +02:00
[u'val1', u'val2']
>>> c.od1.var1 = [u'var1']
2013-08-26 17:14:14 +02:00
>>> print c.od1.var1
2013-08-27 16:35:15 +02:00
[u'var1']
>>> c.od1.var1.append(u'val3')
2013-08-26 17:14:14 +02:00
>>> print c.od1.var1
2013-08-27 16:35:15 +02:00
[u'var1', u'val3']
>>> c.od1.var1.pop(1)
u'val3'
>>> print c.od1.var1
[u'var1']
2013-08-26 17:14:14 +02:00
2013-08-27 16:35:15 +02:00
But it is not possible to set a value to a multi-option wich is not a list::
>>> c.od1.var1 = u'error'
2013-08-26 17:14:14 +02:00
Traceback (most recent call last):
[...]
2013-08-27 16:35:15 +02:00
ValueError: invalid value error for option var1 which must be a list
2013-08-26 17:14:14 +02:00
2013-08-27 16:35:15 +02:00
The master/slave groups
~~~~~~~~~~~~~~~~~~~~~~~~~
.. glossary::
master/slave
A master/slave group is an :class:`~tiramisu.option.OptionDescription` and the
options that lives inside.
Inside this group, a special option, named master option, has the same name as
the group. The group (the option description) is set to type `master`.
All options in a master group is a multi-option (see :ref:`multi-option`).
The slave options have a `default_multi` attribute set to `True`::
>>> from tiramisu.setting import groups
>>> from tiramisu.config import Config
>>> from tiramisu.option import UnicodeOption, OptionDescription
>>>
>>> var1 = UnicodeOption('master', '', multi=True)
>>> var2 = UnicodeOption('slave1', '', multi=True)
>>> var3 = UnicodeOption('slave2', '', multi=True, default_multi=u"default")
>>>
>>> od1 = OptionDescription('master', '', [var1, var2, var3])
>>> od1.impl_set_group_type(groups.master)
>>>
>>> rootod = OptionDescription('rootod', '', [od1])
>>> c = Config(rootod)
>>> c.read_write()
The length of the lists can be modified::
>>> print c.master
master = []
slave1 = []
slave2 = []
>>> c.master.master.append(u'oui')
>>> print c.master
master = [u'oui']
slave1 = [None]
slave2 = [u'default']
>>> c.master.master = [u'non']
>>> print c.master
master = [u'non']
slave1 = [None]
slave2 = [u'default']
>>>
>>> c.master.master = [u'oui', u'non']
>>> print c.master
master = [u'oui', u'non']
slave1 = [None, None]
slave2 = [u'default', u'default']
But it is forbidden to change the lenght of a slave::
>>> c.master.slave1[0] = u'super'
>>> print c.master
master = [u'oui', u'non']
slave1 = [u'super', None]
slave2 = [u'default', u'default']
>>> c.master.slave1 = [u'new1', u'new2']
>>> print c.master
master = [u'oui', u'non']
slave1 = [u'new1', u'new2']
slave2 = [u'default', u'default']
>>> c.master.slave1 = [u'new1']
2013-08-26 17:14:14 +02:00
Traceback (most recent call last):
[...]
2013-08-27 16:35:15 +02:00
tiramisu.error.SlaveError: invalid len for the slave: slave1 which has master.master as master
>>> c.master.slave1 = [u'new1', u'new2', u'new3']
[...]
tiramisu.error.SlaveError: invalid len for the slave: slave1 which has master.master as master
2013-08-27 15:54:10 +02:00
2013-08-27 16:35:15 +02:00
you have to call the `pop` function on the master::
2013-08-27 15:54:10 +02:00
2013-08-27 16:35:15 +02:00
>>> c.master.master = [u'oui']
Traceback (most recent call last):
[...]
tiramisu.error.SlaveError: invalid len for the master: master which has slave1 as slave with greater len
>>> c.master.master.pop(0)
u'oui'
>>> print c.master
master = [u'non']
slave1 = [u'new2']
slave2 = [u'default']
2013-08-26 17:14:14 +02:00
Configuration's interesting methods
------------------------------------------
2013-05-17 12:11:14 +02:00
A `Config` object is informed by an `option.OptionDescription`
instance. The attributes of the ``Config`` objects are the names of the
children of the ``OptionDescription``.
2013-05-23 17:51:50 +02:00
Here are the (useful) methods on ``Config`` (or `SubConfig`).
2013-05-17 12:11:14 +02:00
.. currentmodule:: tiramisu.config
2013-05-17 12:11:14 +02:00
2013-05-23 17:51:50 +02:00
.. class:: Config
2013-05-17 12:11:14 +02:00
2013-05-23 17:51:50 +02:00
.. autoclass:: SubConfig
:members: find, find_first, __iter__, iter_groups, iter_all, make_dict
2013-05-17 12:11:14 +02:00
2013-05-23 17:51:50 +02:00
.. automethod:: __init__
2013-08-23 11:42:22 +02:00
2013-05-23 17:51:50 +02:00
.. rubric:: Summary
2013-05-17 12:11:14 +02:00
.. autosummary::
2013-08-23 11:42:22 +02:00
2013-05-23 17:51:50 +02:00
find
find_first
2013-05-17 12:11:14 +02:00
2013-05-23 17:51:50 +02:00
__iter__
iter_groups
iter_all
2013-05-17 12:11:14 +02:00
2013-05-23 17:51:50 +02:00
make_dict
2013-05-21 18:42:56 +02:00
2013-05-23 17:51:50 +02:00
.. rubric:: Methods
2013-05-21 18:42:56 +02:00
2013-08-23 11:42:22 +02:00
A :class:`~config.CommonConfig` is a abstract base class. A
:class:`~config.SubConfig` is an just in time created objects that wraps an
::class:`~option.OptionDescription`. A SubConfig differs from a Config in the
::fact that a config is a root object and has an environnement, a context wich
::defines the different properties, access rules, vs... There is generally only
2013-08-21 17:21:09 +02:00
::one Config, and many SubConfigs.