.. default-role:: literal

=======================
Configuration Handling
=======================

:module: :api:`config.py`
:tests:  - :api:`test_config.py`
         - :api:`test_option_setting.py`
             
Main Assumption
===============

Configuration option objects :api:`config.Config()` are produced at the 
entry points and handed down to where they are actually used. This keeps 
configuration local but available everywhere and consistent.

`Config` and `Option` objects
==============================

Configuration option objects can be created in different ways. Let's perform
very basic `Config` object manipulations:

::

    >>> from tiramisu.config import Config
    >>> from tiramisu.option import OptionDescription, BoolOption
    >>> descr = OptionDescription("optgroup", "", [
    ...     BoolOption("bool", "", default=False)])
    >>>
    >>> config = Config(descr)
    >>> config.bool
    False
    >>> config.bool = True
    >>> config.bool
    True

Take a look at :api:`test_config.test_base_config()` or 
:api:`test_config.test_base_config_and_groups()`.


Accessing the configuration `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__`
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'

The configuration `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. 
``config.optgroup.optname``.

**Can you repeat it, what is the protocol of accessing a config's attribute ?**

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.

What if a value has been set and `None` is to be returned again ? Don't
worry, an option value can be "reseted" just by the affectation of the special
value `None`. An option accepts a type value as a setting, but also `None` as 
a possible value.

If you do not want to use the pythonic way, that is the attribute access 
way to obtain the value of the configuration option, you can also search 
for it recursively in the whole config namespaces with the ``get()`` 
method :

::

    >>> config.get('bool')
    True
    

To find the right option, `get()` searches recursively into the whole 
tree. For example, to find an option which is in the `gc` namespace 
there are two possibilites.

If you know the path:

::

    >>> config.gc.dummy
    False

If you don't remember the path:

::

    >>> config.get('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,
the first one is of course the `__setattr__` method 

::

    cfg.name = value

wich has the same effect that the "global" `set()` method : it expects that 
the value owner is the default :ref:`glossary#valueowner`

::

     cfg.set(name=value)

The global `setoption()` method of the config objects can set a value with a specific owner 

::

    cfg.setoption('name', value, 'owner')


Finally, the local `setoption()` method directly in the `Option` object can be
used. While the `Option` object refers to his parent, the config knows that the
value has been changed and no bad side effect won't occur

::

    >>> booloption = BoolOption('bool', 'Test boolean option', default=True)
    >>> descr = OptionDescription('descr', '', [booloption])
    >>> cfg = Config(descr)
    >>> booloption.setoption(cfg, False, 'owner')
    >>> cfg.bool 
    >>> False

