first revision
authorgwen <gremond@cadoles.com>
Sun, 13 May 2012 18:48:51 +0000 (20:48 +0200)
committergwen <gremond@cadoles.com>
Sun, 13 May 2012 18:48:51 +0000 (20:48 +0200)
86 files changed:
__init__.py [new file with mode: 0644]
autolib.py [new file with mode: 0644]
config.py [new file with mode: 0644]
doc/Changelog [new file with mode: 0644]
doc/Makefile [new file with mode: 0644]
doc/build/Makefile [new file with mode: 0644]
doc/build/api/Readme [new file with mode: 0644]
doc/build/architecture.dia [new file with mode: 0644]
doc/build/architecture.png [new file with mode: 0644]
doc/build/default.css [new file with mode: 0644]
doc/build/docutils.css [new file with mode: 0644]
doc/build/style.css [new file with mode: 0644]
doc/build/tiramisu.jpeg [new file with mode: 0644]
doc/build/tiramisu.tar.gz [new file with mode: 0644]
doc/code2html [new file with mode: 0755]
doc/config.txt [new file with mode: 0644]
doc/configapi.txt [new file with mode: 0644]
doc/consistency.txt [new file with mode: 0644]
doc/eole-report/eolreport/D01AccesVariables.txt [new file with mode: 0644]
doc/eole-report/eolreport/D02CoherenceVariables.txt [new file with mode: 0644]
doc/eole-report/eolreport/D03ReglesEtats.txt [new file with mode: 0644]
doc/eole-report/eolreport/Makefile [new file with mode: 0644]
doc/eole-report/eolreport/build/Makefile [new file with mode: 0644]
doc/eole-report/eolreport/build/default.css [new file with mode: 0644]
doc/eole-report/eolreport/build/docutils.css [new file with mode: 0644]
doc/eole-report/eolreport/build/imgs/eol.png [new file with mode: 0644]
doc/eole-report/eolreport/build/imgs/logo.png [new file with mode: 0644]
doc/eole-report/eolreport/build/index-report.html [new file with mode: 0644]
doc/eole-report/eolreport/build/pdfreport/D01AccesVariables.pdf [new file with mode: 0644]
doc/eole-report/eolreport/build/pdfreport/D02CoherenceVariables.pdf [new file with mode: 0644]
doc/eole-report/eolreport/build/pdfreport/D03ReglesEtats.pdf [new file with mode: 0644]
doc/eole-report/eolreport/build/pdfreport/make_index [new file with mode: 0755]
doc/eole-report/eolreport/build/pdfreport/rst.py [new file with mode: 0644]
doc/eole-report/eolreport/build/style.css [new file with mode: 0644]
doc/eole-report/eolreport/inc/00-Redacteur.txt [new file with mode: 0644]
doc/eole-report/eolreport/inc/eol.png [new file with mode: 0644]
doc/eole-report/eolreport/inc/logo.png [new file with mode: 0644]
doc/eole-report/eolreport/inc/menjva.gif [new file with mode: 0644]
doc/eole-report/eolreport/inc/preambule.txt [new file with mode: 0644]
doc/eole-report/eolreport/index-report.txt [new file with mode: 0644]
doc/eole-report/eolreport/styles.odt [new file with mode: 0644]
doc/eole-report/proposal/Makefile [new file with mode: 0644]
doc/eole-report/proposal/comparaison.tex [new file with mode: 0644]
doc/eole-report/proposal/definition.tex [new file with mode: 0644]
doc/eole-report/proposal/statut.tex [new file with mode: 0644]
doc/eole-report/proposal/tiramisu.tex [new file with mode: 0644]
doc/epydoc.sh [new file with mode: 0755]
doc/gaspacho.txt [new file with mode: 0644]
doc/getting-started.txt [new file with mode: 0644]
doc/glossary.txt [new file with mode: 0644]
doc/index.txt [new file with mode: 0644]
doc/optionapi.txt [new file with mode: 0644]
doc/pydoc/Makefile [new file with mode: 0644]
doc/pydoc/crarr.png [new file with mode: 0644]
doc/pydoc/epydoc.css [new file with mode: 0644]
doc/pydoc/epydoc.js [new file with mode: 0644]
doc/rst2html.py [new file with mode: 0755]
doc/status.txt [new file with mode: 0644]
doc/todo.txt [new file with mode: 0644]
error.py [new file with mode: 0644]
option.py [new file with mode: 0644]
report/Makefile [new file with mode: 0644]
report/__init__.py [new file with mode: 0644]
report/build/Makefile [new file with mode: 0644]
report/build/basic.css [new file with mode: 0644]
report/build/rst2html.py [new file with mode: 0755]
report/build/style.css [new file with mode: 0644]
report/generate.py [new file with mode: 0644]
report/makerestdoc.py [new file with mode: 0644]
report/rst.py [new file with mode: 0644]
report/test_config_big_example.py [new file with mode: 0644]
test/__init__.py [new file with mode: 0644]
test/autopath.py [new file with mode: 0644]
test/test_config.py [new file with mode: 0644]
test/test_config_api.py [new file with mode: 0644]
test/test_config_big_example.py [new file with mode: 0644]
test/test_option_consistency.py [new file with mode: 0644]
test/test_option_default.py [new file with mode: 0644]
test/test_option_owner.py [new file with mode: 0644]
test/test_option_setting.py [new file with mode: 0644]
test/test_option_type.py [new file with mode: 0644]
test/test_option_with_special_name.py [new file with mode: 0644]
test/test_parsing_group.py [new file with mode: 0644]
test/test_reverse_from_path.py [new file with mode: 0644]
test/test_tool.py [new file with mode: 0644]
tool.py [new file with mode: 0644]

diff --git a/__init__.py b/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/autolib.py b/autolib.py
new file mode 100644 (file)
index 0000000..c4ea2fb
--- /dev/null
@@ -0,0 +1,7 @@
+"enables us to carry out a calculation and return an option's value"
+
+# FIXME: import eosfunc here
+def identical(name, config, *args):
+    return "identical" + name 
+    
+    
diff --git a/config.py b/config.py
new file mode 100644 (file)
index 0000000..871d6d1
--- /dev/null
+++ b/config.py
@@ -0,0 +1,460 @@
+# -*- coding: utf-8 -*-
+"pretty small and local configuration management tool"
+# The original `Config` design model is unproudly borrowed from 
+# the rough gus of pypy: pypy: http://codespeak.net/svn/pypy/dist/pypy/config/
+from error import (HiddenOptionError, ConfigError, NotFoundError, 
+                AmbigousOptionError, ConflictConfigError, NoMatchingOptionFound, 
+                            SpecialOwnersError, MandatoryError, MethodCallError, 
+                                           DisabledOptionError, ModeOptionError)
+from option import (OptionDescription, Option, SymLinkOption, group_types, 
+                                                          apply_requires, modes)
+import autolib
+# ____________________________________________________________
+# automatic Option object
+special_owners = ['auto', 'fill']
+                  
+def special_owner_factory(name, owner, default=None, 
+                              callback=None, config=None):
+    # auto behavior: carries out a calculation
+    if owner == 'auto':
+        return auto_factory(name, callback, config)
+    # fill behavior: carries out a calculation only if a default value isn't set
+    if owner == 'fill':
+        if default == None:
+            return auto_factory(name, callback, config)
+        else:
+            return default
+
+def auto_factory(name, callback, config):
+    try:
+        return getattr(autolib, callback)(name, config)
+    except AttributeError:
+        raise SpecialOwnersError("callback: {0} not found for "
+                                           "option: {1}".format(callback, name))
+    
+# ____________________________________________________________
+class Config(object):
+    _cfgimpl_hidden = True
+    _cfgimpl_disabled = True
+    _cfgimpl_mandatory = True
+    _cfgimpl_frozen = False
+    _cfgimpl_owner = "user"
+    _cfgimpl_toplevel = None
+    _cfgimpl_mode = 'normal'
+    
+    def __init__(self, descr, parent=None, **overrides):
+        self._cfgimpl_descr = descr
+        self._cfgimpl_value_owners = {}
+        self._cfgimpl_parent = parent
+        # `Config()` indeed supports the configuration `Option()`'s values...
+        self._cfgimpl_values = {}
+        self._cfgimpl_previous_values = {}
+        # XXX warnings are a great idea, let's make up a better use of it 
+        self._cfgimpl_warnings = []
+        self._cfgimpl_toplevel = self._cfgimpl_get_toplevel()
+        # `freeze()` allows us to carry out this calculation again if necessary 
+        self._cfgimpl_frozen = self._cfgimpl_toplevel._cfgimpl_frozen 
+        #
+        self._cfgimpl_build(overrides)
+
+    def _validate_duplicates(self, children):
+        duplicates = []
+        for dup in children:
+            if dup._name not in duplicates:
+                duplicates.append(dup._name)
+            else:
+                raise ConflictConfigError('duplicate option name: <%s>' % \
+                                                                      dup._name)
+
+    def _cfgimpl_build(self, overrides):
+        self._validate_duplicates(self._cfgimpl_descr._children)
+        for child in self._cfgimpl_descr._children:
+            if isinstance(child, Option):
+                self._cfgimpl_values[child._name] = child.getdefault()
+                self._cfgimpl_value_owners[child._name] = 'default'
+            elif isinstance(child, OptionDescription):
+                self._validate_duplicates(child._children)
+                self._cfgimpl_values[child._name] = Config(child, parent=self)
+        self.override(overrides)
+
+    def cfgimpl_update(self):
+        "dynamically adds `Option()` or `OptionDescription()`"
+        # Nothing is static. Everything evolve.
+        # FIXME this is an update for new options in the schema only 
+        # see the update_child() method of the descr object 
+        for child in self._cfgimpl_descr._children:
+            if isinstance(child, Option):
+                if child._name not in self._cfgimpl_values:
+                    self._cfgimpl_values[child._name] = child.getdefault()
+                    self._cfgimpl_value_owners[child._name] = 'default'
+            elif isinstance(child, OptionDescription):
+                if child._name not in self._cfgimpl_values:
+                    self._cfgimpl_values[child._name] = Config(child, parent=self)
+
+    def override(self, overrides):
+        for name, value in overrides.iteritems():
+            homeconfig, name = self._cfgimpl_get_home_by_path(name)
+            #  if there are special_owners, impossible to override
+            if homeconfig._cfgimpl_value_owners[name] in special_owners:
+                raise SpecialOwnersError("cannot override option: {0} because "
+                                            "of its special owner".format(name))
+            homeconfig.setoption(name, value, 'default')
+
+    def cfgimpl_set_owner(self, owner):
+        self._cfgimpl_owner = owner
+        for child in self._cfgimpl_descr._children:
+            if isinstance(child, OptionDescription):
+                self._cfgimpl_values[child._name].cfgimpl_set_owner(owner)
+    # ____________________________________________________________
+    def cfgimpl_hide(self):
+        if self._cfgimpl_parent != None:
+            raise MethodCallError("this method root_hide() shall not be"
+                                           "used with non-root Config() object") 
+        rootconfig = self._cfgimpl_get_toplevel()
+        rootconfig._cfgimpl_hidden = True
+
+    def cfgimpl_show(self):
+        if self._cfgimpl_parent != None:
+            raise MethodCallError("this method root_hide() shall not be"
+                                           "used with non-root Config() object") 
+        rootconfig = self._cfgimpl_get_toplevel()
+        rootconfig._cfgimpl_hidden = False
+    # ____________________________________________________________
+    def cfgimpl_disable(self):
+        if self._cfgimpl_parent != None:
+            raise MethodCallError("this method root_hide() shall not be"
+                                           "used with non-root Confit() object") 
+        rootconfig = self._cfgimpl_get_toplevel()
+        rootconfig._cfgimpl_disabled = True
+
+    def cfgimpl_enable(self):
+        if self._cfgimpl_parent != None:
+            raise MethodCallError("this method root_hide() shall not be"
+                                           "used with non-root Confit() object") 
+        rootconfig = self._cfgimpl_get_toplevel()
+        rootconfig._cfgimpl_disabled = False
+    # ____________________________________________________________
+    def __setattr__(self, name, value):
+        if '.' in name:
+            homeconfig, name = self._cfgimpl_get_home_by_path(name)
+            return setattr(homeconfig, name, value)
+
+        if name.startswith('_cfgimpl_'):
+            self.__dict__[name] = value
+            return
+        if self._cfgimpl_frozen and getattr(self, name) != value:
+            raise TypeError("trying to change a value in a frozen config"
+                                                ": {0} {1}".format(name, value))
+        if type(getattr(self._cfgimpl_descr, name)) != SymLinkOption:
+            self._validate(name, getattr(self._cfgimpl_descr, name))
+        self.setoption(name, value, self._cfgimpl_owner)
+
+    def _validate(self, name, opt_or_descr):
+        if not type(opt_or_descr) == OptionDescription:
+            apply_requires(opt_or_descr, self) 
+            # hidden options
+            if self._cfgimpl_toplevel._cfgimpl_hidden and \
+                (opt_or_descr._is_hidden() or self._cfgimpl_descr._is_hidden()):
+                raise HiddenOptionError("trying to access to a hidden option:"
+                                                           " {0}".format(name))
+            # disabled options
+            if self._cfgimpl_toplevel._cfgimpl_disabled and \
+            (opt_or_descr._is_disabled() or self._cfgimpl_descr._is_disabled()):
+                raise DisabledOptionError("this option is disabled:"
+                                                            " {0}".format(name))
+            # expert options 
+            # XXX currently doesn't look at the group, is it really necessary ?
+            if self._cfgimpl_toplevel._cfgimpl_mode != 'normal':
+                if opt_or_descr.get_mode() != 'normal':
+                    raise ModeOptionError("this option's mode is not normal:"
+                                                            " {0}".format(name))
+        if type(opt_or_descr) == OptionDescription:
+            apply_requires(opt_or_descr, self) 
+
+    def __getattr__(self, name):
+        # attribute access by passing a path, 
+        # for instance getattr(self, "creole.general.family.adresse_ip_eth0") 
+        if '.' in name:
+            homeconfig, name = self._cfgimpl_get_home_by_path(name)
+            return getattr(homeconfig, name)
+        opt_or_descr = getattr(self._cfgimpl_descr, name)
+        # symlink options 
+        if type(opt_or_descr) == SymLinkOption:
+            return getattr(self, opt_or_descr.path)
+        self._validate(name, opt_or_descr)
+        # special attributes
+        if name.startswith('_cfgimpl_'):
+            # if it were in __dict__ it would have been found already
+            return self.__dict__[name]
+            raise AttributeError("%s object has no attribute %s" %
+                                 (self.__class__, name))
+        if name not in self._cfgimpl_values:
+            raise AttributeError("%s object has no attribute %s" %
+                                 (self.__class__, name))
+        if name in self._cfgimpl_value_owners:
+            owner = self._cfgimpl_value_owners[name]
+            # special owners
+            if owner in special_owners:
+                return special_owner_factory(name, owner, 
+                                              default=opt_or_descr.getdefault(),
+                                            callback=opt_or_descr.getcallback(),
+                                            config=self)
+        # mandatory options
+        if not isinstance(opt_or_descr, OptionDescription):
+            homeconfig = self._cfgimpl_get_toplevel()
+            mandatory = homeconfig._cfgimpl_mandatory
+            if opt_or_descr.is_mandatory() and mandatory:
+                if self._cfgimpl_values[name] == None\
+                  and opt_or_descr.getdefault() == None:
+                    raise MandatoryError("option: {0} is mandatory " 
+                                          "and shall have a value".format(name))
+        return self._cfgimpl_values[name]
+
+    def __dir__(self):
+        #from_type = dir(type(self))
+        from_dict = list(self.__dict__)
+        extras = list(self._cfgimpl_values)
+        return sorted(set(extras + from_dict))
+
+    def unwrap_from_name(self, name):
+        # didn't have to stoop so low: `self.get()` must be the proper method
+        # **and it is slow**: it recursively searches into the namespaces
+        paths = self.getpaths(allpaths=True)
+        opts = dict([(path, self.unwrap_from_path(path)) for path in paths])
+        all_paths = [p.split(".") for p in self.getpaths()]
+        for pth in all_paths:
+            if name in pth:
+                return opts[".".join(pth)]
+        raise NotFoundError("name: {0} not found".format(name))
+        
+    def unwrap_from_path(self, path):
+        # didn't have to stoop so low, `geattr(self, path)` is much better
+        # **fast**: finds the option directly in the appropriate namespace
+        if '.' in path:
+            homeconfig, path = self._cfgimpl_get_home_by_path(path)
+            return getattr(homeconfig._cfgimpl_descr, path)
+        return getattr(self._cfgimpl_descr, path)
+                                            
+    def __delattr__(self, name):
+        # if you use delattr you are responsible for all bad things happening
+        if name.startswith('_cfgimpl_'):
+            del self.__dict__[name]
+            return
+        self._cfgimpl_value_owners[name] = 'default'
+        opt = getattr(self._cfgimpl_descr, name)
+        if isinstance(opt, OptionDescription):
+            raise AttributeError("can't option subgroup")
+        self._cfgimpl_values[name] = getattr(opt, 'default', None)
+
+    def setoption(self, name, value, who=None):
+        if who == None:
+            who == self._cfgimpl_owner
+        child = getattr(self._cfgimpl_descr, name)
+        if type(child) != SymLinkOption:
+            if name not in self._cfgimpl_values:
+                raise AttributeError('unknown option %s' % (name,))
+            # special owners, a value with a owner *auto* cannot be changed
+            oldowner = self._cfgimpl_value_owners[child._name]
+            if oldowner == 'auto':
+                if who == 'auto':
+                    raise ConflictConfigError('cannot override value to %s for '
+                                          'option %s' % (value, name))
+            if oldowner == who:
+                oldvalue = getattr(self, name)
+                if oldvalue == value: #or who in ("default",):
+                    return
+            child.setoption(self, value, who)
+            # if the value owner is 'auto', set the option to hidden 
+            if who == 'auto':
+                if not child._is_hidden():
+                    child.hide()
+            self._cfgimpl_value_owners[name] = who
+        else:
+            homeconfig = self._cfgimpl_get_toplevel()
+            child.setoption(homeconfig, value, who)
+
+    def set(self, **kwargs):
+        all_paths = [p.split(".") for p in self.getpaths(allpaths=True)]
+        for key, value in kwargs.iteritems():
+            key_p = key.split('.')
+            candidates = [p for p in all_paths if p[-len(key_p):] == key_p]
+            if len(candidates) == 1:
+                name = '.'.join(candidates[0])
+                homeconfig, name = self._cfgimpl_get_home_by_path(name)
+                try:
+                    getattr(homeconfig, name)
+                except MandatoryError:
+                    pass
+                except Exception, e:
+                    raise e # HiddenOptionError or DisabledOptionError
+                homeconfig.setoption(name, value, self._cfgimpl_owner)
+            elif len(candidates) > 1:
+                raise AmbigousOptionError(
+                    'more than one option that ends with %s' % (key, ))
+            else:
+                raise NoMatchingOptionFound(
+                    'there is no option that matches %s' 
+                    ' or the option is hidden or disabled'% (key, ))
+
+    def get(self, name):
+        paths = self.getpaths(allpaths=True)
+        pathsvalues = []
+        for path in paths:
+            pathname = path.split('.')[-1]
+            if pathname == name:
+                try:
+                    value = getattr(self, path)            
+                    return value 
+                except Exception, e:
+                    raise e
+        raise NotFoundError("option {0} not found in config".format(name))                    
+
+    def _cfgimpl_get_home_by_path(self, path):
+        """returns tuple (config, name)"""
+        path = path.split('.')
+        
+        for step in path[:-1]:
+            self = getattr(self, step)
+        return self, path[-1]
+
+    def _cfgimpl_get_toplevel(self):
+        while self._cfgimpl_parent is not None:
+            self = self._cfgimpl_parent
+        return self
+
+    def add_warning(self, warning):
+        self._cfgimpl_get_toplevel()._cfgimpl_warnings.append(warning)
+
+    def get_warnings(self):
+        return self._cfgimpl_get_toplevel()._cfgimpl_warnings
+    # ____________________________________________________________
+    # freeze and read-write statuses
+    def cfgimpl_freeze(self):
+        rootconfig = self._cfgimpl_get_toplevel()
+        rootconfig._cfgimpl_frozen = True
+        self._cfgimpl_frozen = True
+
+    def cfgimpl_unfreeze(self):
+        rootconfig = self._cfgimpl_get_toplevel()
+        rootconfig._cfgimpl_frozen = False
+        self._cfgimpl_frozen = False
+
+    def is_frozen(self):
+        # it should be the same value as self._cfgimpl_frozen...
+        rootconfig = self._cfgimpl_get_toplevel()
+        return rootconfig.__dict__['_cfgimpl_frozen']
+
+    def cfgimpl_read_only(self):
+        # hung up on freeze, hidden and disabled concepts 
+        self.cfgimpl_freeze()
+        rootconfig = self._cfgimpl_get_toplevel()
+        rootconfig._cfgimpl_hidden = False
+        rootconfig._cfgimpl_disabled = True
+        rootconfig._cfgimpl_mandatory = True
+
+    def cfgimpl_set_mode(self, mode):
+        # normal or expert mode
+        rootconfig = self._cfgimpl_get_toplevel()
+        if mode not in modes:
+            raise ConfigError("mode {0} not available".format(mode))
+        rootconfig._cfgimpl_mode = mode
+    
+    def cfgimpl_read_write(self):
+        # hung up on freeze, hidden and disabled concepts
+        self.cfgimpl_unfreeze()
+        rootconfig = self._cfgimpl_get_toplevel()
+        rootconfig._cfgimpl_hidden = True
+        rootconfig._cfgimpl_disabled = False
+        rootconfig._cfgimpl_mandatory = False
+    # ____________________________________________________________
+    def getkey(self):
+        return self._cfgimpl_descr.getkey(self)
+
+    def __hash__(self):
+        return hash(self.getkey())
+
+    def __eq__(self, other):
+        return self.getkey() == other.getkey()
+
+    def __ne__(self, other):
+        return not self == other
+
+    def __iter__(self):
+        # iteration only on Options (not OptionDescriptions)
+        for child in self._cfgimpl_descr._children:
+            if isinstance(child, Option):
+                try:
+                    yield child._name, getattr(self, child._name)
+                except:
+                    pass # hidden, disabled option group
+
+    def iter_groups(self, group_type=None):
+        "iteration on OptionDescriptions"
+        if group_type == None:
+            groups = group_types
+        else:
+            if group_type not in group_types:
+                raise TypeError("Unknown group_type: {0}".format(group_type))
+            groups = [group_type]
+        for child in self._cfgimpl_descr._children:
+            if isinstance(child, OptionDescription):
+                    try:
+                        if child.get_group_type() in groups: 
+                            yield child._name, getattr(self, child._name)
+                    except:
+                        pass # hidden, disabled option
+                    
+    def __str__(self, indent=""):
+        lines = []
+        children = [(child._name, child)
+                    for child in self._cfgimpl_descr._children]
+        children.sort()
+        for name, child in children:
+            if self._cfgimpl_value_owners.get(name, None) == 'default':
+                continue
+            value = getattr(self, name)
+            if isinstance(value, Config):
+                substr = value.__str__(indent + "    ")
+            else:
+                substr = "%s    %s = %s" % (indent, name, value)
+            if substr:
+                lines.append(substr)
+        if indent and not lines:
+            return ''   # hide subgroups with all default values
+        lines.insert(0, "%s[%s]" % (indent, self._cfgimpl_descr._name,))
+        return '\n'.join(lines)
+
+    def getpaths(self, include_groups=False, allpaths=False):
+        """returns a list of all paths in self, recursively, taking care of 
+        the context (hidden/disabled)
+        """
+        paths = []
+        for path in self._cfgimpl_descr.getpaths(include_groups=include_groups):
+            try: 
+                value = getattr(self, path)
+            except Exception, e:
+                if not allpaths:
+                    pass # hidden or disabled option
+                else:
+                    paths.append(path) # hidden or disabled option added
+            else:
+                paths.append(path)
+        return paths 
+        
+def make_dict(config, flatten=False):
+    paths = config.getpaths()
+    pathsvalues = []
+    for path in paths:
+        if flatten:
+            pathname = path.split('.')[-1]
+        else:
+            pathname = path
+        try:
+            value = getattr(config, path)            
+            pathsvalues.append((pathname, value))      
+        except:
+            pass # this just a hidden or disabled option  
+    options = dict(pathsvalues)
+    return options
+# ____________________________________________________________
+
diff --git a/doc/Changelog b/doc/Changelog
new file mode 100644 (file)
index 0000000..77a7916
--- /dev/null
@@ -0,0 +1,14 @@
+2012-03-23
+
+    - set_group_type (instead of set_descr())
+    - iteration utilities (for -> on option, iter_group -> on 
+     OptionDescriptions (group of options)
+    - hide and disable for option groups (and subgroups) -> not OK
+
+2012-03-20
+
+    - get() method for recursive attribute access
+    - make_path() in a flatten way
+    - ro and rw  
+    
+
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644 (file)
index 0000000..7cac92a
--- /dev/null
@@ -0,0 +1,25 @@
+SRC=$(wildcard *.txt)
+HTMLFRAGMENT=$(addsuffix .html, $(basename $(SRC)))
+
+.SUFFIXES:
+
+.PHONY: all clean
+
+all: html code
+#      make -C ./build/code all
+#      make -C ./build/test all
+#      make -C ./build all
+               
+html: $(HTMLFRAGMENT)
+
+%.html: %.txt    
+       ./rst2html.py --stylesheet ./build/style.css $< > ./build/$@
+
+code:
+       ./code2html
+
+clean:
+       make -C ./build clean
+       make -C ./pydoc/ clean
+#      make -C ./build/test clean
+       
diff --git a/doc/build/Makefile b/doc/build/Makefile
new file mode 100644 (file)
index 0000000..cc5f93b
--- /dev/null
@@ -0,0 +1,6 @@
+.PHONY: clean
+.SUFFIXES:
+
+clean:
+       rm -f *.html
+       rm -f api/*.html
diff --git a/doc/build/api/Readme b/doc/build/api/Readme
new file mode 100644 (file)
index 0000000..8623c0e
--- /dev/null
@@ -0,0 +1 @@
+API's directory
diff --git a/doc/build/architecture.dia b/doc/build/architecture.dia
new file mode 100644 (file)
index 0000000..35adb7e
Binary files /dev/null and b/doc/build/architecture.dia differ
diff --git a/doc/build/architecture.png b/doc/build/architecture.png
new file mode 100644 (file)
index 0000000..fecefa0
Binary files /dev/null and b/doc/build/architecture.png differ
diff --git a/doc/build/default.css b/doc/build/default.css
new file mode 100644 (file)
index 0000000..8625c0e
--- /dev/null
@@ -0,0 +1,1080 @@
+body,body.editor,body.body {
+    font: 110% "Times New Roman", Arial, Verdana, Helvetica, serif;
+    background: White;
+    color: Black;
+}
+
+a, a.reference {
+       text-decoration: none; 
+}
+a[href]:hover { text-decoration: underline; }
+
+img {
+    border: none;
+       vertical-align: middle;
+}
+
+p, div.text {
+    text-align: left;
+    line-height: 1.5em;
+    margin: 0.5em 0em 0em 0em;
+}
+
+
+
+p a:active {
+       color: Red;
+    background-color: transparent;
+}
+
+p img {
+    border: 0;
+    margin: 0;
+}
+
+img.inlinephoto {
+    padding: 0;
+    padding-right: 1em;
+    padding-top: 0.7em;
+    float: left;
+}
+
+hr {
+    clear: both;
+    height: 1px;
+    color: #8CACBB;
+    background-color: transparent;
+}
+
+
+ul { 
+    line-height: 1.5em;
+    /*list-style-image: url("bullet.gif"); */
+    margin-left: 1.5em;
+    padding:0;
+}
+
+ol {
+    line-height: 1.5em;
+    margin-left: 1.5em;
+    padding:0;
+}
+
+ul a, ol a {
+    text-decoration: underline;
+}
+
+dl {
+}
+
+dt {
+    font-weight: bold;    
+}
+
+dd {
+    line-height: 1.5em;
+    margin-bottom: 1em;
+}
+
+blockquote {
+    font-family: Times, "Times New Roman", serif;
+    font-style: italic;
+    font-size: 120%;
+}
+
+code {
+    color: Black;
+    /*background-color: #dee7ec;*/
+    background-color: #cccccc;
+}
+
+pre {
+    padding: 1em;
+    border: 1px solid #8cacbb;
+    color: Black;
+    background-color: #dee7ec;
+    background-color: #cccccc;
+    overflow: auto;
+}
+
+
+.netscape4 {
+    display: none;
+}
+
+/* main page styles */
+
+/*a[href]:hover { color: black; text-decoration: underline; }
+a[href]:link { color: black; text-decoration: underline; }
+a[href] { color: black; text-decoration: underline; }
+*/
+
+span.menu_selected {
+       color: black;
+       font: 140% Verdana, Helvetica, Arial, sans-serif;
+       text-decoration: none;
+    padding-right: 0.3em;
+    background-color: #cccccc;
+}
+
+
+a.menu {
+       /*color: #3ba6ec; */
+       font: 140% Verdana, Helvetica, Arial, sans-serif;
+       text-decoration: none;
+    padding-right: 0.3em;
+}
+
+a.menu[href]:visited, a.menu[href]:link{
+       /*color: #3ba6ec; */
+       font: 140% Verdana, Helvetica, Arial, sans-serif;
+       text-decoration: none;
+}
+
+a.menu[href]:hover {
+       /*color: black;*/
+}
+
+div.project_title{
+  /*border-spacing: 20px;*/
+  font: 160% Verdana, Helvetica, Arial, sans-serif;
+  color: #3ba6ec; 
+  vertical-align: middle;
+  padding-bottom: 0.3em;
+}
+
+a.wikicurrent {
+  font: 100% Verdana, Helvetica, Arial, sans-serif;
+  color: #3ba6ec; 
+  vertical-align: middle;
+}
+
+
+table.body {
+  border: 0;
+  /*padding: 0;
+  border-spacing: 0px;
+  border-collapse: separate;
+  */
+}
+
+td.page-header-left {
+  padding: 5px;
+  /*border-bottom: 1px solid #444444;*/
+}
+
+td.page-header-top {
+  padding: 0;
+    
+  /*border-bottom: 1px solid #444444;*/
+}
+
+td.sidebar {
+  padding: 1 0 0 1;
+}
+
+td.sidebar p.classblock {
+  padding: 0 5 0 5;
+  margin: 1 1 1 1;
+  border: 1px solid #444444;
+  background-color: #eeeeee;
+}
+
+td.sidebar p.userblock {
+  padding: 0 5 0 5;
+  margin: 1 1 1 1;
+  border: 1px solid #444444;
+  background-color: #eeeeff;
+}
+
+td.content {
+  padding: 1 5 1 5;
+  vertical-align: top;
+  width: 100%;
+}
+
+p.ok-message {
+  background-color: #22bb22;
+  padding: 5 5 5 5;
+  color: white;
+  font-weight: bold;
+}
+p.error-message {
+  background-color: #bb2222;
+  padding: 5 5 5 5;
+  color: white;
+  font-weight: bold;
+}
+
+p:first-child { 
+  margin: 0 ;
+  padding: 0;
+}
+
+/* style for forms */
+table.form {
+  padding: 2;
+  border-spacing: 0px;
+  border-collapse: separate;
+}
+
+table.form th {
+  color: #333388;
+  text-align: right;
+  vertical-align: top;
+  font-weight: normal;
+}
+table.form th.header {
+  font-weight: bold;
+  background-color: #eeeeff;
+  text-align: left;
+}
+
+table.form th.required {
+  font-weight: bold;
+}
+
+table.form td {
+  color: #333333;
+  empty-cells: show;
+  vertical-align: top;
+}
+
+table.form td.optional {
+  font-weight: bold;
+  font-style: italic;
+}
+
+table.form td.html {
+  color: #777777;
+}
+
+/* style for lists */
+table.list {
+  border-spacing: 0px;
+  border-collapse: separate;
+  vertical-align: top;
+  padding-top: 0;
+  width: 100%;
+}
+
+table.list th {
+  padding: 0 4 0 4;
+  color: #404070;
+  background-color: #eeeeff;
+  border-right: 1px solid #404070;
+  border-top: 1px solid #404070;
+  border-bottom: 1px solid #404070;
+  vertical-align: top;
+  empty-cells: show;
+}
+table.list th a[href]:hover { color: #404070 }
+table.list th a[href]:link { color: #404070 }
+table.list th a[href] { color: #404070 }
+table.list th.group {
+  background-color: #f4f4ff;
+  text-align: center;
+  font-size: 120%;
+}
+
+table.list td {
+  padding: 0 4 0 4;
+  border: 0 2 0 2;
+  border-right: 1px solid #404070;
+  color: #404070;
+  background-color: white;
+  vertical-align: top;
+  empty-cells: show;
+}
+
+table.list tr.normal td {
+  background-color: white;
+  white-space: nowrap;
+}
+
+table.list tr.alt td {
+  background-color: #efefef;
+  white-space: nowrap;
+}
+
+table.list td:first-child {
+  border-left: 1px solid #404070;
+  border-right: 1px solid #404070;
+}
+
+table.list th:first-child {
+  border-left: 1px solid #404070;
+  border-right: 1px solid #404070;
+}
+
+table.list tr.navigation th {
+  text-align: right;
+}
+table.list tr.navigation th:first-child {
+  border-right: none;
+  text-align: left;
+}
+
+
+/* style for message displays */
+table.messages {
+  border-spacing: 0px;
+  border-collapse: separate;
+  width: 100%;
+}
+
+table.messages th.header{
+  padding-top: 10px;
+  border-bottom: 1px solid gray;
+  font-weight: bold;
+  background-color: white;
+  color: #707040;
+}
+
+table.messages th {
+  font-weight: bold;
+  color: black;
+  text-align: left;
+  border-bottom: 1px solid #afafaf;
+}
+
+table.messages td {
+  font-family: monospace;
+  background-color: #efefef;
+  border-bottom: 1px solid #afafaf;
+  color: black;
+  empty-cells: show;
+  border-right: 1px solid #afafaf;
+  vertical-align: top;
+  padding: 2 5 2 5;
+}
+
+table.messages td:first-child {
+  border-left: 1px solid #afafaf;
+  border-right: 1px solid #afafaf;
+}
+
+/* style for file displays */
+table.files {
+  border-spacing: 0px;
+  border-collapse: separate;
+  width: 100%;
+}
+
+table.files th.header{
+  padding-top: 10px;
+  border-bottom: 1px solid gray;
+  font-weight: bold;
+  background-color: white;
+  color: #707040;
+}
+
+table.files th {
+  border-bottom: 1px solid #afafaf;
+  font-weight: bold;
+  text-align: left;
+}
+
+table.files td {
+  font-family: monospace;
+  empty-cells: show;
+}
+
+/* style for history displays */
+table.history {
+  border-spacing: 0px;
+  border-collapse: separate;
+  width: 100%;
+}
+
+table.history th.header{
+  padding-top: 10px;
+  border-bottom: 1px solid gray;
+  font-weight: bold;
+  background-color: white;
+  color: #707040;
+  font-size: 100%;
+}
+
+table.history th {
+  border-bottom: 1px solid #afafaf;
+  font-weight: bold;
+  text-align: left;
+  font-size: 90%;
+}
+
+table.history td {
+  font-size: 90%;
+  vertical-align: top;
+  empty-cells: show;
+}
+
+
+/* style for class list */
+table.classlist {
+  border-spacing: 0px;
+  border-collapse: separate;
+  width: 100%;
+}
+
+table.classlist th.header{
+  padding-top: 10px;
+  border-bottom: 1px solid gray;
+  font-weight: bold;
+  background-color: white;
+  color: #707040;
+}
+
+table.classlist th {
+  font-weight: bold;
+  text-align: left;
+}
+
+
+/* style for class help display */
+table.classhelp {
+  border-spacing: 0px;
+  border-collapse: separate;
+  width: 100%;
+}
+
+table.classhelp th {
+  font-weight: bold;
+  text-align: left;
+  color: #707040;
+}
+
+table.classhelp td {
+  padding: 2 2 2 2;
+  border: 1px solid black;
+  text-align: left;
+  vertical-align: top;
+  empty-cells: show;
+}
+
+
+/* style for "other" displays */
+table.otherinfo {
+  border-spacing: 0px;
+  border-collapse: separate;
+  width: 100%;
+}
+
+table.otherinfo th.header{
+  padding-top: 10px;
+  border-bottom: 1px solid gray;
+  font-weight: bold;
+  background-color: white;
+  color: #707040;
+}
+
+table.otherinfo th {
+  border-bottom: 1px solid #afafaf;
+  font-weight: bold;
+  text-align: left;
+}
+
+input {
+    border: 1px solid #8cacbb;
+    color: Black;
+    background-color: white;
+    vertical-align: middle;
+    margin-bottom: 1px; /* IE bug fix */
+    padding: 0.1em;
+}
+
+select {
+    border: 1px solid #8cacbb;
+    color: Black;
+    background-color: white;
+    vertical-align: middle;
+    margin-bottom: 1px; /* IE bug fix */
+    padding: 0.1em;
+}
+
+
+a.nonexistent {
+    color: #FF2222;
+}
+a.nonexistent:visited {
+    color: #FF2222;
+}
+a.external {
+    color: #AA6600;
+}
+
+/*
+dl,ul,ol {
+    margin-top: 1pt;
+}
+tt,pre {
+    font-family: Lucida Console,Courier New,Courier,monotype;
+    font-size: 12pt;
+}
+pre.code {
+    margin-top: 8pt;
+    margin-bottom: 8pt;
+    background-color: #FFFFEE;
+    white-space:pre;
+    border-style:solid;
+    border-width:1pt;
+    border-color:#999999;
+    color:#111111;
+    padding:5px;
+    width:100%;
+}
+*/
+div.diffold {
+    background-color: #FFFF80;
+    border-style:none;
+    border-width:thin;
+    width:100%;
+}
+div.diffnew {
+    background-color: #80FF80;
+    border-style:none;
+    border-width:thin;
+    width:100%;
+}
+div.message {
+    margin-top: 6pt;
+    background-color: #E8FFE8;
+    border-style:solid;
+    border-width:1pt;
+    border-color:#999999;
+    color:#440000;
+    padding:5px;
+    width:100%;
+}
+strong.highlight {
+    background-color: #FFBBBB;
+/* as usual, NetScape fucks up with innocent CSS
+    border-color: #FFAAAA;
+    border-style: solid;
+    border-width: 1pt;
+*/
+}
+
+table.navibar {
+    background-color: #C8C8C8;
+    border-spacing: 3px;
+}
+td.navibar {
+    background-color: #E8E8E8;
+    vertical-align: top;
+    text-align: right;
+    padding: 0px;
+}
+
+div.pagename {
+    font-size: 140%;
+    color: blue;
+    text-align: center;
+    font-weight: bold;
+    background-color: white;
+    padding: 0 ;
+}
+
+a.wikiaction, input.wikiaction {
+    color: black; 
+    text-decoration: None;
+    text-align: center;
+    color: black;
+    /*border: 1px solid #3ba6ec; */
+    margin: 4px;
+    padding: 5;
+    padding-bottom: 0;
+    white-space: nowrap;
+}
+
+a.wikiaction[href]:hover { 
+       color: black; 
+       text-decoration: none; 
+       /*background-color: #dddddd; */
+}
+
+span.wikiuserpref {
+    padding-top: 1em;
+    font-size: 120%;
+}
+
+div.wikitrail {
+    vertical-align: bottom;
+    /*font-size: -1;*/
+    padding-top: 1em;
+    display: none;
+}
+
+div.wikiaction {
+    vertical-align: middle;
+    /*border-bottom: 1px solid #8cacbb;*/
+    padding-bottom:1em;
+    text-align: left;
+    width: 100%;
+}
+
+div.wikieditmenu {
+    text-align: right;
+}
+
+form.wikiedit {
+    border: 1px solid #8cacbb;
+    background-color: #f0f0f0;
+    background-color: #fabf00;
+    padding: 1em;
+    padding-right: 0em;
+}
+
+div.legenditem {
+    padding-top: 0.5em;
+    padding-left: 0.3em;
+}
+
+span.wikitoken {
+   background-color: #eeeeee;
+}
+    
+
+div#contentspace h1:first-child, div.heading:first-child { 
+  padding-top: 0;
+  margin-top: 0;
+}
+div#contentspace h2:first-child { 
+  padding-top: 0;
+  margin-top: 0;
+}
+
+/* heading and paragraph text */
+
+div.heading, h1 {
+    font-family: Verdana, Helvetica, Arial, sans-serif;
+    background-color: #58b3ef;
+    background-color: #FFFFFF; 
+    /*color: #4893cf;*/
+    color: black;
+    padding-top: 1.0em;
+    padding-bottom:0.2em;
+    text-align: left;
+    margin-top: 0em; 
+    /*margin-bottom:8pt;*/
+    font-weight: bold;
+    font-size: 115%;
+    border-bottom: 1px solid #8CACBB;
+}
+
+
+h1, h2, h3, h4, h5, h6 {
+    color: orange;
+    clear: left;
+    font: 100% Verdana, Helvetica, Arial, sans-serif;
+    margin: 0;
+    padding-left: 0em;
+    padding-top: 1em;
+    padding-bottom: 0.2em;
+    /*border-bottom: 1px solid #8CACBB;*/
+}
+/* h1,h2 { padding-top: 0; }*/
+
+
+h1 { font-size: 145%; }
+h2 { font-size: 135%; }
+h3 { font-size: 125%; }
+h4 { font-size: 120%; }
+h5 { font-size: 110%; }
+h6 { font-size: 80%; }
+
+h1 a { text-decoration: None;}
+
+div.exception {
+  background-color: #bb2222;
+  padding: 5 5 5 5;
+  color: white;
+  font-weight: bold;
+}
+pre.exception {
+    font-size: 110%;
+    padding: 1em;
+    border: 1px solid #8cacbb;
+    color: Black;
+    background-color: #dee7ec;
+    background-color: #cccccc;
+}
+
+/* defines for navgiation bar (documentation) */
+
+
+div.direntry {
+    padding-top: 0.3em;
+    padding-bottom: 0.3em;
+    margin-right: 1em;
+    font-weight: bold;
+    background-color: #dee7ec;
+    font-size: 110%;
+}
+
+div.fileentry {
+    font-family: Verdana, Helvetica, Arial, sans-serif;
+    padding-bottom: 0.3em;
+    white-space: nowrap;
+    line-height: 150%;
+}
+
+a.fileentry {
+    white-space: nowrap;
+}
+
+
+span.left {
+    text-align: left;
+}
+span.right {
+    text-align: right;
+}
+
+div.navbar {
+  /*margin: 0;*/
+  font-size: 80% /*smaller*/;
+  font-weight: bold;
+  text-align: left;
+  /* position: fixed; */
+  top: 100pt;
+  left: 0pt; /*  auto; */
+  width: 120pt;
+  /* right: auto;
+  right: 0pt;  2em; */
+}
+
+
+div.history a {
+    /* font-size: 70%; */
+}
+
+div.wikiactiontitle { 
+  font-weight: bold;
+}
+
+/*  REST  defines */
+
+div.document {
+    margin: 0;
+}
+
+h1.title {
+    margin: 0;
+    margin-bottom: 0.5em;
+}
+
+td.toplist {
+    vertical-align: top;
+}
+
+img#pyimg {
+    position: absolute;
+    top: 4px;
+    left: 4px;
+}
+    
+div#navspace {
+    position: absolute;
+    top: 130px;
+    left: 11px;
+    font-size: 100%;
+    width: 150px;
+    overflow: hidden; /* scroll;  */
+}
+
+div#metaspace {
+    position: absolute;
+    top: 40px;
+    left: 170px;
+}
+
+div#errorline {
+    position: relative;
+    top: 5px; 
+    float: right; 
+}
+
+div#contentspace {
+    position: absolute;
+       /* font: 120% "Times New Roman", serif;*/
+    font: 110% Verdana, Helvetica, Arial, sans-serif;
+    top: 130px;
+    left: 170px;
+    margin-right: 5px;
+}
+
+div#menubar {
+/*    width: 400px; */
+    float: left;
+}
+
+/* for the documentation page */
+div#docinfoline {
+  position: relative;
+  top: 5px; 
+  left: 0px;
+
+  /*background-color: #dee7ec; */
+  padding: 5pt; 
+  padding-bottom: 1em; 
+  color: black;
+  /*border-width: 1pt;
+  border-style: solid;*/
+
+}
+
+div#docnavlist {
+  /*background-color: #dee7ec; */
+  padding: 5pt; 
+  padding-bottom: 2em; 
+  color: black;
+  border-width: 1pt;
+  /*border-style: solid;*/
+}
+
+
+/* text markup */
+
+div.listtitle {
+    color: Black;
+    clear: left;
+    font: 120% Verdana, Helvetica, Arial, sans-serif;
+    margin: 0;
+    padding-left: 0em;
+    padding-top: 0em;
+    padding-bottom: 0.2em;
+    margin-right: 0.5em;
+    border-bottom: 1px solid #8CACBB;
+}
+
+div.actionbox h3 { 
+  padding-top: 0;
+  padding-right: 0.5em;
+  padding-left: 0.5em;
+  background-color: #fabf00;
+  text-align: center;
+  border: 1px solid black; /* 8cacbb; */
+}
+
+div.actionbox a { 
+  display: block;
+  padding-bottom: 0.5em;
+  padding-top: 0.5em;
+  margin-left: 0.5em;
+}
+
+div.actionbox a.history { 
+  display: block;
+  padding-bottom: 0.5em;
+  padding-top: 0.5em;
+  margin-left: 0.5em;
+  font-size: 90%; 
+}
+
+div.actionbox { 
+  margin-bottom: 2em;
+  padding-bottom: 1em;
+  overflow: hidden; /* scroll;  */
+}
+
+/* taken from docutils (oh dear, a bit senseless) */
+ol.simple, ul.simple {
+  margin-bottom: 1em }
+
+ol.arabic {
+  list-style: decimal }
+
+ol.loweralpha {
+  list-style: lower-alpha }
+
+ol.upperalpha {
+  list-style: upper-alpha }
+
+ol.lowerroman {
+  list-style: lower-roman }
+
+ol.upperroman {
+  list-style: upper-roman }
+
+
+/*
+:Author: David Goodger
+:Contact: goodger@users.sourceforge.net
+:date: $Date: 2003/01/22 22:26:48 $
+:version: $Revision: 1.29 $
+:copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+*/
+/*
+.first {
+  margin-top: 0 }
+
+.last {
+  margin-bottom: 0 }
+
+a.toc-backref {
+  text-decoration: none ;
+  color: black }
+
+dd {
+  margin-bottom: 0.5em }
+
+div.abstract {
+  margin: 2em 5em }
+
+div.abstract p.topic-title {
+  font-weight: bold ;
+  text-align: center }
+
+div.attention, div.caution, div.danger, div.error, div.hint,
+div.important, div.note, div.tip, div.warning {
+  margin: 2em ;
+  border: medium outset ;
+  padding: 1em }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title {
+  color: red ;
+  font-weight: bold ;
+  font-family: sans-serif }
+
+div.hint p.admonition-title, div.important p.admonition-title,
+div.note p.admonition-title, div.tip p.admonition-title {
+  font-weight: bold ;
+  font-family: sans-serif }
+
+div.dedication {
+  margin: 2em 5em ;
+  text-align: center ;
+  font-style: italic }
+
+div.dedication p.topic-title {
+  font-weight: bold ;
+  font-style: normal }
+
+div.figure {
+  margin-left: 2em }
+
+div.footer, div.header {
+  font-size: smaller }
+
+div.system-messages {
+  margin: 5em }
+
+div.system-messages h1 {
+  color: red }
+
+div.system-message {
+  border: medium outset ;
+  padding: 1em }
+
+div.system-message p.system-message-title {
+  color: red ;
+  font-weight: bold }
+
+div.topic {
+  margin: 2em }
+
+h1.title {
+  text-align: center ;
+  color: orange}
+
+h2.subtitle {
+  color: orange;
+  text-align: center }
+
+hr {
+  width: 75% }
+
+p.caption {
+  font-style: italic }
+
+p.credits {
+  font-style: italic ;
+  font-size: smaller }
+
+p.label {
+  white-space: nowrap }
+
+p.topic-title {
+  font-weight: bold }
+
+pre.address {
+  margin-bottom: 0 ;
+  margin-top: 0 ;
+  font-family: serif ;
+  font-size: 100% }
+
+pre.line-block {
+  font-family: serif ;
+  font-size: 100% }
+
+pre.literal-block, pre.doctest-block {
+  margin-left: 2em ;
+  margin-right: 2em ;
+  background-color: #eeeeee }
+
+span.classifier {
+  font-family: sans-serif ;
+  font-style: oblique }
+
+span.classifier-delimiter {
+  font-family: sans-serif ;
+  font-weight: bold }
+
+span.interpreted {
+  font-family: sans-serif }
+
+span.option {
+  white-space: nowrap }
+
+span.option-argument {
+  font-style: italic }
+
+span.pre {
+  white-space: pre }
+
+span.problematic {
+  color: red }
+
+table {
+  margin-top: 0.5em ;
+  margin-bottom: 0.5em }
+
+table.citation {
+  border-left: solid thin gray ;
+  padding-left: 0.5ex }
+
+table.docinfo {
+  margin: 2em 4em }
+
+table.footnote {
+  border-left: solid thin black ;
+  padding-left: 0.5ex }
+
+td, th {
+  padding-left: 0.5em ;
+  padding-right: 0.5em ;
+  vertical-align: top }
+
+th.docinfo-name, th.field-name {
+  font-weight: bold ;
+  text-align: left ;
+  white-space: nowrap }
+
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+  font-size: 100% }
+
+tt {
+  background-color: #eeeeee }
+
+ul.auto-toc {
+  list-style-type: none }
+*/
+
+div.section {
+  margin-top: 1.0em ;
+}    
diff --git a/doc/build/docutils.css b/doc/build/docutils.css
new file mode 100644 (file)
index 0000000..f03e03d
--- /dev/null
@@ -0,0 +1,255 @@
+.first {
+  margin-top: 0 ! important }
+
+.last {
+  margin-bottom: 0 ! important }
+
+.hidden {
+  display: none }
+
+a.toc-backref {
+  text-decoration: none ;
+  color: inherit }
+
+blockquote.epigraph {
+  margin: 2em 5em }
+
+dl.docutils dd {
+  margin-bottom: 0.5em }
+
+dl.docutils dt {
+  font-weight: bold }
+
+dl dt { line-height: 150% }
+
+div.abstract {
+  margin: 2em 5em }
+
+div.abstract p.topic-title {
+  font-weight: bold ;
+  text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+  margin: 2em ;
+  border: medium outset ;
+  padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+  font-weight: bold ;
+  font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title {
+  color: red ;
+  font-weight: bold ;
+  font-family: sans-serif }
+
+div.compound .compound-first, div.compound .compound-middle {
+  margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+  margin-top: 0.5em }
+
+div.dedication {
+  margin: 2em 5em ;
+  text-align: center ;
+  font-style: italic }
+
+div.dedication p.topic-title {
+  font-weight: bold ;
+  font-style: normal }
+
+div.document {
+  width: 600px ;
+  margin-left: 5em ;
+  margin-right: 5em }
+
+div.figure {
+  margin-left: 2em }
+
+div.footer, div.header {
+  font-size: smaller }
+
+div.line-block {
+  display: block ;
+  margin-top: 1em ;
+  margin-bottom: 1em }
+
+div.line-block div.line-block {
+  margin-top: 0 ;
+  margin-bottom: 0 ;
+  margin-left: 1.5em }
+
+div.sidebar {
+  margin-left: 1em ;
+  border: medium outset ;
+  padding: 1em ;
+  background-color: #ffffee ;
+  width: 40% ;
+  float: right ;
+  clear: right }
+
+div.sidebar p.rubric {
+  font-family: sans-serif ;
+  font-size: medium }
+
+div.system-messages {
+  margin: 5em }
+
+div.system-messages h1 {
+  color: red }
+
+div.system-message {
+  border: medium outset ;
+  padding: 1em }
+
+div.system-message p.system-message-title {
+  color: red ;
+  font-weight: bold }
+
+div.topic {
+  margin: 2em }
+
+h1, h2, h3, h4, h5 {
+  font-family: sans-serif ;
+  line-height: 150% ;
+  color: orange} /* #666 } */
+
+h1.title {
+  text-align: center
+  }
+h2.subtitle {
+  text-align: center }
+
+hr.docutils {
+  width: 75% }
+
+ol.simple, ul.simple {
+  margin-bottom: 1em }
+
+ol.arabic {
+  list-style: decimal }
+
+ol.loweralpha {
+  list-style: lower-alpha }
+
+ol.upperalpha {
+  list-style: upper-alpha }
+
+ol.lowerroman {
+  list-style: lower-roman }
+
+ol.upperroman {
+  list-style: upper-roman }
+
+p.attribution {
+  text-align: right ;
+  margin-left: 50% }
+
+p.caption {
+  font-style: italic }
+
+p.credits {
+  font-style: italic ;
+  font-size: smaller }
+
+p.label {
+  white-space: nowrap }
+
+p.rubric {
+  font-weight: bold ;
+  font-size: larger ;
+  color: maroon ;
+  text-align: center }
+
+p.sidebar-title {
+  font-family: sans-serif ;
+  font-weight: bold ;
+  font-size: larger }
+
+p.sidebar-subtitle {
+  font-family: sans-serif ;
+  font-weight: bold }
+
+p.topic-title {
+  font-weight: bold }
+
+pre.address {
+  margin-bottom: 0 ;
+  margin-top: 0 ;
+  font-family: serif ;
+  font-size: 100% }
+
+pre.line-block {
+  font-family: serif ;
+  font-size: 100% }
+
+pre.literal-block, pre.doctest-block {
+  margin-left: 2em ;
+  margin-right: 2em ;
+  font-size: small ;
+  background-color: #eeeeee }
+
+span.classifier {
+  font-family: sans-serif ;
+  font-style: oblique }
+
+span.classifier-delimiter {
+  font-family: sans-serif ;
+  font-weight: bold }
+
+span.interpreted {
+  font-family: sans-serif }
+
+span.option {
+  white-space: nowrap }
+
+span.option-argument {
+  font-style: italic }
+
+span.pre {
+  white-space: pre }
+
+span.problematic {
+  color: red }
+
+table.citation {
+  border-left: solid thin gray }
+
+table.docinfo {
+  /* float: right ; */
+  margin: 2em 4em ;
+  color: #666 }
+
+table.docutils {
+  margin-top: 0.5em ;
+  margin-bottom: 0.5em }
+
+table.footnote {
+  border-left: solid thin black }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+  padding-left: 0.5em ;
+  padding-right: 0.5em ;
+  vertical-align: top }
+
+th.docinfo-name, th.field-name {
+  font-weight: bold ;
+  text-align: right ;
+  white-space: nowrap }
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+  font-size: 100% }
+
+tt.docutils {
+  background-color: #eeeeee }
+
+ul.auto-toc {
+  list-style-type: none }
+
diff --git a/doc/build/style.css b/doc/build/style.css
new file mode 100644 (file)
index 0000000..28c256e
--- /dev/null
@@ -0,0 +1,32 @@
+@import url(docutils.css);
+@import url(default.css);
+a:link {
+       color: orange;
+       font-weight: bold;
+       text-decoration: none;
+}
+a:visited {
+       text-decoration: none;
+       color: #999999;
+}
+a:hover {
+       text-decoration: none;
+       color: #999999;
+}
+a:active {
+       text-decoration: none;
+       color: #999999;
+}
+
+.header {
+       color: orange;
+       background-color: white;
+       padding: 1em;
+}
+.footer {
+       color: #666;
+       background-color: inherit;
+       font-size: 75%;
+}
+
+
diff --git a/doc/build/tiramisu.jpeg b/doc/build/tiramisu.jpeg
new file mode 100644 (file)
index 0000000..84b391d
Binary files /dev/null and b/doc/build/tiramisu.jpeg differ
diff --git a/doc/build/tiramisu.tar.gz b/doc/build/tiramisu.tar.gz
new file mode 100644 (file)
index 0000000..1820be3
Binary files /dev/null and b/doc/build/tiramisu.tar.gz differ
diff --git a/doc/code2html b/doc/code2html
new file mode 100755 (executable)
index 0000000..be62c7c
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+import types
+from os.path import join
+from inspect import getsource, getmembers, isclass, isfunction, ismethod, ismodule
+from importlib import import_module
+
+root="./build/api"
+
+htmltmpl = """
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+<title>{title}</title>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8">
+<meta http-equiv="content-style-type" content="text/css">
+<meta http-equiv="expires" content="0">
+</head>
+<body>
+<pre>
+{content}
+</pre>
+</body>
+</html>
+"""
+
+def write_source(name, content):
+    fh = file(join(root, name)+'.html', 'w')
+    fh.write(format_html(name, content))
+    fh.close()
+
+def format_html(title, content):
+    return htmltmpl.format(title=title, content=content)
+
+def parse_module(module):
+    module = import_module(module)
+    write_source(module.__name__, getsource(module))
+#    classes = [(cls, value) for cls, value in getattr(module, '__dict__').items() if value == types.ClassType]
+    classes = getmembers(module, isclass)
+    for name, obj in classes:
+        write_source(module.__name__ + '.' + name, getsource(obj))
+#        methods = [(meth, value) for meth, value in getattr(obj, '__dict__').items() if type(value) == types.MethodType]
+        methods = getmembers(obj, ismethod)
+        for meth, value in methods:
+            write_source(module.__name__ + '.' + name + '.' + meth, getsource(value))
+
+    #functions = [(func, value) for func, value in getattr(module, '__dict__').items() if type(value) == types.FunctionType]
+    functions =  getmembers(module, isfunction)
+    for name, obj in functions:
+        write_source(module.__name__ + '.' + name, getsource(obj))
+
+def process_modules():
+    from glob import glob
+    from os.path import abspath, dirname, normpath, splitext, basename
+    here = abspath(__file__)
+    directory = dirname(here)
+    pyfiles = glob(normpath(join(directory, '..', '*.py')))
+    for pyf in pyfiles:
+        pyf = splitext(basename(pyf))[0]
+        modname = 'tiramisu.' + pyf
+        if not '__init__' in modname:
+            parse_module(modname)        
+
+    pyfiles = glob(normpath(join(directory, '..', 'test', '*.py')))
+    for pyf in pyfiles:
+        pyf = splitext(basename(pyf))[0]
+        modname = 'tiramisu.test.' + pyf
+        if not '__init__' in modname:
+            parse_module(modname)        
+
+process_modules()
+
diff --git a/doc/config.txt b/doc/config.txt
new file mode 100644 (file)
index 0000000..9b967d7
--- /dev/null
@@ -0,0 +1,158 @@
+.. 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.
+
+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
+
diff --git a/doc/configapi.txt b/doc/configapi.txt
new file mode 100644 (file)
index 0000000..d6fe570
--- /dev/null
@@ -0,0 +1,103 @@
+.. default-role:: literal
+
+Config API Details
+==================
+
+:module: :api:`config.py`
+:test cases: - :api:`test_config_api.py`
+             - :api:`test_config_big_example.py`
+
+
+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).
+
+The configuration object
+-------------------------
+
+:api:`config.Config()` object that lives in :api:`config.py` hold the 
+choosen values for the options (or the default value for the 
+:api:`option.Option()` object, if no choice was made).
+
+A `Config` object is informed by an :api:`option.OptionDescription` 
+instance. The attributes of the ``Config`` objects are the names of the 
+children of the ``OptionDescription``.
+
+Here are the (useful) methods on ``Config``:
+
+    :api:`config.Config.__init__(self, descr, **overrides)`:
+        ``descr`` is an instance of :api:`option.OptionDescription` that 
+        describes the configuration object. ``override`` can be used to 
+        set different default values (see method ``override``).
+
+    :api:`config.Config.override(self, overrides)`:
+        override default values. This marks the overridden values as defaults.
+        ``overrides`` is a dictionary of path strings to values.
+
+    :api:`config.Config.set(self, **kwargs)`:
+        "do what I mean"-interface to option setting. Searches all paths 
+        starting from that config for matches of the optional arguments 
+        and sets the found option if the match is not ambiguous.
+
+    :api:`config.Config.get(self, name)`:
+        the behavior is much like the attribute access way, except that 
+        the search for the option is performed recursively in the whole 
+        configuration tree.
+
+    :api:`config.Config.cfgimpl_read_write()`:
+        configuration level `read_write` status, see :doc:`status` 
+    
+    :api:`config.Config.cfgimpl_read_only()`:
+        configuration level `read_only` status, see :doc:`status` 
+
+Here are some private attributes of a `Config()` object, for a 
+comprehension of the internal merchanism:
+    
+- `_cfgimpl_descr =` :api:`option.OptionDescription()`, 
+  e.g. the :ref:`optionapi#schema`
+
+- `_cfgimpl_values` contains the :api:`option.Option()`'s values. 
+  Yes, the values of the options: remember that the values are stored **inside**
+  the :api:`config.Config()` and not in the `Option()`
+
+`_cfgimpl_values` contains something like that 
+
+::
+
+    {'int': 0, 'wantframework': False, 'objspace': 'std', 'bool': False,
+    'str': 'abc', 'gc': <config.Config object at 0xa33f8ec>, 'wantref': False}
+
+We can see that values can also be config objects, it's the 
+sub-namespaces that are stored in the values as `Config()` objects.
+
+convenience utilities (iteration, exports...)
+-----------------------------------------------
+
+With this :api:`config.Config()` configuration management entry point, 
+it is possible to
+
+- `iter` on config, notice that there is an iteration order wich is 
+  the order of the :ref:`optionapi#schema` specification entries,
+- compare two configs (equality),
+- export the whole config into a `dict` with :api:`config.make_dict()`,
+- `validate()` an option value into a config, see :doc:`consistency`.
+
+:api:`option.Option()` objects in a config are iterable in the pythonic 
+way, that is something like `[(name, value) for name, value in config]`. 
+
+To iter on groups in the same manner, use the
+:api:`config.Config.iter_groups()` method wich yields generators too.
+
+**iteration utilities**
+
+    :api:`config.Config.__iter__()` 
+        Pythonesque way of parsing group's ordered options.
+    
+    :api:`config.Config.iter_groups(group_type=None)`:
+        To iter on groups objects only. 
+        All groups are returned if `group_type` is `None`, otherwise the groups
+        can be filtered by categories (families, or whatever).
+
diff --git a/doc/consistency.txt b/doc/consistency.txt
new file mode 100644 (file)
index 0000000..873b0f2
--- /dev/null
@@ -0,0 +1,96 @@
+.. default-role:: literal
+
+The global configuration's consistency
+========================================
+
+:module: :api:`config.py`
+:tests: :api:`test_option_consistency.py`
+
+Option's values type validation
+--------------------------------
+
+When a value is set to the option, the value is validated by the 
+option's :api:`option.Option()` validator's type.
+
+Notice that if the option is `multi`, that is the `multi` attribute is set to 
+`True`, then the validation of the option value accepts a list of values 
+of the same type.
+
+Requirements
+------------
+
+Configuration options can specify requirements as parameters at the init 
+time, the specification of some links between options or groups allows 
+to carry out a dependencies calculation. For example, an option can ben 
+hidden if another option has been set with some expected value. This is 
+just an example, because the possibilities are hudge.
+
+A requirement is specified using a list of triplets. The first element 
+of the triplet gives the path of the option that is required, the second 
+element is the value wich is expected to trigger the callback, and the 
+third one is the callback's action name (`hide`, `show`...)::
+
+    stroption = StrOption('str', 'Test string option', default="abc", 
+                          requires=[('int', 1, 'hide')])
+
+Take a look at an example here 
+:api:`test_option_consistency.test_hidden_if_in()`
+
+Config updates
+---------------
+
+New configuration options and groups can be dynamically added. 
+
+The configuration has to be *updated* after that the description has been 
+passed to the Config objet, see:
+
+::
+
+    >>> config = Config(descr)
+    >>> newoption = BoolOption('newoption', 'dummy twoo', default=False)
+    >>> descr.add_child(newoption)
+    >>> config.update()
+    >>> config.newoption
+    False
+
+in 
+
+- :api:`test_option_consistency.test_newoption_add_in_descr()`
+- :api:`test_option_consistency.test_newoption_add_in_subdescr()`
+- :api:`test_option_consistency.test_newoption_add_in_config()`
+
+
+Validation upon a whole configuration object
+----------------------------------------------
+
+An option's integrity can be validated towards a whole configuration.
+
+This type of validation is very open. Let's take a use case : an option 
+has a certain value, and the value of this option can change the owner 
+of another option or option group... Everything is possible.
+
+FIXME : put an example here
+
+Identical option names 
+----------------------
+
+If an :api:`option.Option()` happens to be defined twice in the 
+:ref:`glossary#schema` (e.g. the :api:`option.OptionDescription()`), 
+:that is the two options actually have the same name, an exception is raised.
+
+The calculation is currently carried out in the samespace, for example 
+if `config.gc.name` is defined, another option in `gc` with the name 
+`name` is **not** allowed, whereas `config.whateverelse.name` is still 
+allowed. 
+    
+.. the calculation was carried out by the requires, wich is not a goog idead
+
+    Type constraints with the `multi` type
+    ----------------------------------------
+
+    By convention, if a multi option has somme requires, the constraints on 
+    the multi type is in all the OptionGroup (a group has to be `multi`, and 
+    a multi of the same length).
+
+    See :api:`test_option_consistency.test_multi_constraints()`
+
diff --git a/doc/eole-report/eolreport/D01AccesVariables.txt b/doc/eole-report/eolreport/D01AccesVariables.txt
new file mode 100644 (file)
index 0000000..af6f19f
--- /dev/null
@@ -0,0 +1,82 @@
+.. default-role:: literal
+
+.. include:: inc/preambule.txt
+
+Accès aux variables 
+====================
+
+Protocole d'accès aux valeurs
+-------------------------------
+
+**Créole**
+
+- Si la variable n'a pas été déclarée, une erreur est levée
+- Si la variable a été déclarée, mais qu'aucune valeur n'a été définie, (ni valeur affectée, ni valeur par défaut) la valeur retournée est `[]` ou `""` ou `[""]` ou `["",""]`,
+- Si la variable a été déclarée et qu'une valeur par défaut a été définie, la valeur retournée et la valeur par défaut,
+- Si la variable a été déclarée et qu'une valeur a été définie, la valeur retournée est la valeur de la variable.
+
+**tiramisu**
+
+- Si la variable n'a pas été déclarée, une erreur est levée
+- Si la variable a été déclarée, mais qu'aucune valeur n'a été définie, (ni valeur affectée, ni valeur par défaut) la valeur retournée est `None`,
+- Si la variable a été déclarée et qu'une valeur par défaut a été définie, la valeur retournée et la valeur par défaut,
+- Si la variable a été déclarée et qu'une valeur a été définie, la valeur retournée est la valeur de la variable.
+
+la différence tient au fait de la valeur nulle (`None`) qui a été mal définie 
+dès le début dans `Créole`.
+
+Accès Créole par "dictionnaire"
+--------------------------------
+
+La définition est dans le `XML`
+
+::
+
+    <family name="general">
+
+    <variable name="adresse_ip_eth0">
+
+Le dictionnaire est chargé dans un `EoleDict()`
+
+::
+
+    from creole.cfgparser import EoleDict
+    eoldict = EoleDict(...)
+
+Un export dans un dictionnaire est necessaire pour manipuler les données
+
+::
+
+    from creole.parsedico import parse_dico
+
+    flatdict = parse_dico(eoldict)
+
+    assert dico['ip'] == '10.10.1.11'
+
+
+le resultat de l'accès aux données vient de `typeole.EoleVar('ip').get_value()`
+
+
+Accès `tiramisu` par espace de nommage
+----------------------------------------
+
+
+- espaces de nommages ;
+- c'est la configuration qui est responsable de l'accès aux valeurs ;
+- une configuration par accès direct (pas d'export) ;
+- un point d'entrée unique aisément manipulable grâce aux espaces de nommage.
+
+::
+
+    from tiramisu.config import Config
+    from tiramisu.option import OptionDescription
+    subdescr = OptionDescription("creole", [IPOption('ip')])
+    descr = OptionDescription("creole", [subdescr])
+    config = Config(descr)
+    assert config.creole.general.ip == '10.10.1.11'
+
+Les valeurs sont dépendantes **de la configuration** et donc la responsabilité 
+des valeurs dépend de la configuration et pas de la variable elle-même.
+
+
+
diff --git a/doc/eole-report/eolreport/D02CoherenceVariables.txt b/doc/eole-report/eolreport/D02CoherenceVariables.txt
new file mode 100644 (file)
index 0000000..fa37aaf
--- /dev/null
@@ -0,0 +1,109 @@
+.. default-role:: literal
+
+.. include:: inc/preambule.txt
+
+Cohérence des valeurs des variables 
+====================================
+
+type des variables 
+-------------------
+
+**Créole**
+
+pas d'unicité du type abstrait : `Multivar`, `CreoleVar` et `TypedVar`
+
+- `String`
+- `Ip`
+- `Netmask`
+- `Number`
+- `Boolean`
+- `OuiNon`
+
+**tiramisu**
+
+unicité du type abstrait : `Option()`
+
+pas de nouveau type multivalué, mais un attribut des types existants::
+
+    >>> from option import BoolOption
+    >>> boolopt = BoolOption('bool', 'description de bool', multi=True)
+
+tous les types Créole, plus
+
+- `SymlinkOption`
+- `CheckOption` qui permet de définir les "oui/non", "On/Off"
+
+Validations suivant l'organisation en familles
+-----------------------------------------------
+
+**Créole**
+
+**Organisation par accumulation de références sur des dictionnaires (`EoleDict`)**
+
+On peut charger un EoleDict avec des variables qui pointent vers des families
+qui n'existent pas, aucune validation n'est faite (confiance absolument faite au
+moment du chargemzent du XML)
+
+exemple, dans l'espace de nommage racine::
+
+    <variables>
+    <variable name="adresse_ip_eth0">
+
+
+::
+
+    from creole.parsedico import parse_dico
+    flatdict = parse_dico(eoldict)
+    dico['adresse_ip_eth0']
+    KeyError: 'adresse_ip_eth0'
+
+**Tiramisu**
+
+**Organisation par arborescence.**
+
+Un espace de nommage doit systématiquement être défini, la variable n'est
+accessible **que** par un path.
+
+
+Variables présentes deux fois
+-------------------------------
+
+- Créole : pas de validation possible
+- tiramisu : comportement règlable (on autorise l'unicité ou pas)
+
+- dans Créole les valeurs sont **fausses** (c'est la dernière variable qui qui gagne)
+
+Il faut faire confiance au XML 
+
+::
+
+    <family name="general">
+    <variable name="adresse_ip_eth0">
+    <valeur>toto
+
+
+    <family name="services">
+    <variable name="adresse_ip_eth0">
+    <valeur>tutu
+    
+
+dans `gen_config` la valeur retenue est::
+
+    general/adresse_ip_eth0 -> tutu 
+    services/adresse_ip_eth0 -> tutu 
+
+dans `parsedico`, la variable est écrasée::
+
+    >>> from creole.parsedico import parse_dico
+    >>> d = parse_dico()
+    >>> d['adresse_ip_eth0']
+    tutu
+    
+dans tiramisu::
+
+    >>> config.general.adresse_ip_eth0
+    toto
+    >>> config.services.adresse_ip_eth0
+    tutu
+    
+   
diff --git a/doc/eole-report/eolreport/D03ReglesEtats.txt b/doc/eole-report/eolreport/D03ReglesEtats.txt
new file mode 100644 (file)
index 0000000..70705f5
--- /dev/null
@@ -0,0 +1,113 @@
+.. default-role:: literal
+
+.. include:: inc/preambule.txt
+
+Etats et statuts des options de configuration
+================================================
+
+état des variables et lisibilité de l'API
+-------------------------------------------
+
+**Creole**
+
+`EoleVar()`
+
+- `get_value()`
+- `get_final_value()`
+- `get_final_value_at_index()`
+- `check_value()`
+- `get_prec_value()`
+- `get_calculated_value()` -> automatique
+
+**tiramisu**
+
+`Option()`
+
+- **aucune API** d'accès à la valeur d'une option au niveau de l'option de configuration
+- `option.getdefault()`
+- `option.setoption(config, value, owner)`
+
+variables "automatiques"
+------------------------------
+
+si `owner` == 'auto', la variable est automatique et la configuration le sait,
+elle lance alors les fonctions de calcul à chaque évaluation
+
+dans Créole, c'est validé aux niveau de la variable par un appel à `eval_func()`
+
+Accès suivant les états de la configuration
+--------------------------------------------
+
+- disabled
+- hidden
+- mode (normal/expert)
+- obligatoire (mandatory)
+- ...
+
+- `EoleVar.hidden`
+- `EoleVar.disabled`
+
+pas d'objet `Family` dans Créole donc l'organisation des hiérarchie de 
+hidden est opaque
+
+- `EoleDict.families['hidden']` pour avoir accès à l'état d'une famille
+
+dans Tiramisu
+
+- `hidden` au niveau `Option`, `OptionDescription` et **aussi** au niveau de 
+  la configuration ce qui permet d'avoir des états (inexistant dans `Créole`)
+  
+.. maitres/esclaves avec Créole : `mavar.get_slaves()`
+
+
+`hidden_if_in`, `hidden_if_not_in`
+-------------------------------------
+
+La notion est généralisée dans tiramisu avec les `requires`.
+
+Dans Créole : très difficile de conserver une cohérence des `hidden_if_in`
+quand il y en a plusieurs. 
+
+Dans Tiramisu : validation et levée d'exception si les **requirements** sont 
+incohérents, action inverse si aucun requires n'est matché.
+
+exemple de requires
+
+::
+
+    <family name="clamav">
+    <variable name="activer_clam">
+
+    <variable name="activer_clam_exim">
+    <valeur>non
+    
+    <variable name="activer_clam_samba">
+    <valeur>oui
+   
+    <condition name='hidden_if_in' source='activer_clam_exim'>
+    <param>non
+    <target type='variable'>activer_clam
+    <!-- ça hide (momentanément)-->
+    
+    <condition name='hidden_if_in' source='activer_clam_samba'>
+    <param>non
+    <target type='variable'>activer_clam
+    <!-- ça show (et c'est le dernier qui a raison) -->
+    
+:résultat: `activer_clam` est visible, c'est la dernière condition qui a raison
+
+avec tiramisu, `activer_clam` **dans les même conditions**, est cachée.
+
+::
+
+    >>> activer_clam = StrOption('activer_clam', 'activer clamav',
+                requires=[('activer_clam_exim', 'non', 'hide'), 
+                          ('activer_clam_samba', 'non', 'hide'),])
+    >>> config.clamav.activer_clam_exim = 'non'
+    >>> config.clamav.activer_clam_samba = 'oui'                     
+    >>> config.clamav.activer_clam
+    >>> Traceback (most recent call last):
+        File "<stdin>", line 1, in <module>
+        HiddenOptionError("trying to access to a hidden option:activer_clam")
+    >>> 
+
diff --git a/doc/eole-report/eolreport/Makefile b/doc/eole-report/eolreport/Makefile
new file mode 100644 (file)
index 0000000..fbf5816
--- /dev/null
@@ -0,0 +1,7 @@
+
+%.odt: %.txt
+       rst2odt --create-links --custom-odt-footer="Page %p% de %P%" --endnotes-end-doc --no-generator --stylesheet=styles.odt $< $@
+
+%.html: %.txt    
+       rst2html --stylesheet ./build/style.css $< > ./build/$@
+    
diff --git a/doc/eole-report/eolreport/build/Makefile b/doc/eole-report/eolreport/build/Makefile
new file mode 100644 (file)
index 0000000..cc5f93b
--- /dev/null
@@ -0,0 +1,6 @@
+.PHONY: clean
+.SUFFIXES:
+
+clean:
+       rm -f *.html
+       rm -f api/*.html
diff --git a/doc/eole-report/eolreport/build/default.css b/doc/eole-report/eolreport/build/default.css
new file mode 100644 (file)
index 0000000..8625c0e
--- /dev/null
@@ -0,0 +1,1080 @@
+body,body.editor,body.body {
+    font: 110% "Times New Roman", Arial, Verdana, Helvetica, serif;
+    background: White;
+    color: Black;
+}
+
+a, a.reference {
+       text-decoration: none; 
+}
+a[href]:hover { text-decoration: underline; }
+
+img {
+    border: none;
+       vertical-align: middle;
+}
+
+p, div.text {
+    text-align: left;
+    line-height: 1.5em;
+    margin: 0.5em 0em 0em 0em;
+}
+
+
+
+p a:active {
+       color: Red;
+    background-color: transparent;
+}
+
+p img {
+    border: 0;
+    margin: 0;
+}
+
+img.inlinephoto {
+    padding: 0;
+    padding-right: 1em;
+    padding-top: 0.7em;
+    float: left;
+}
+
+hr {
+    clear: both;
+    height: 1px;
+    color: #8CACBB;
+    background-color: transparent;
+}
+
+
+ul { 
+    line-height: 1.5em;
+    /*list-style-image: url("bullet.gif"); */
+    margin-left: 1.5em;
+    padding:0;
+}
+
+ol {
+    line-height: 1.5em;
+    margin-left: 1.5em;
+    padding:0;
+}
+
+ul a, ol a {
+    text-decoration: underline;
+}
+
+dl {
+}
+
+dt {
+    font-weight: bold;    
+}
+
+dd {
+    line-height: 1.5em;
+    margin-bottom: 1em;
+}
+
+blockquote {
+    font-family: Times, "Times New Roman", serif;
+    font-style: italic;
+    font-size: 120%;
+}
+
+code {
+    color: Black;
+    /*background-color: #dee7ec;*/
+    background-color: #cccccc;
+}
+
+pre {
+    padding: 1em;
+    border: 1px solid #8cacbb;
+    color: Black;
+    background-color: #dee7ec;
+    background-color: #cccccc;
+    overflow: auto;
+}
+
+
+.netscape4 {
+    display: none;
+}
+
+/* main page styles */
+
+/*a[href]:hover { color: black; text-decoration: underline; }
+a[href]:link { color: black; text-decoration: underline; }
+a[href] { color: black; text-decoration: underline; }
+*/
+
+span.menu_selected {
+       color: black;
+       font: 140% Verdana, Helvetica, Arial, sans-serif;
+       text-decoration: none;
+    padding-right: 0.3em;
+    background-color: #cccccc;
+}
+
+
+a.menu {
+       /*color: #3ba6ec; */
+       font: 140% Verdana, Helvetica, Arial, sans-serif;
+       text-decoration: none;
+    padding-right: 0.3em;
+}
+
+a.menu[href]:visited, a.menu[href]:link{
+       /*color: #3ba6ec; */
+       font: 140% Verdana, Helvetica, Arial, sans-serif;
+       text-decoration: none;
+}
+
+a.menu[href]:hover {
+       /*color: black;*/
+}
+
+div.project_title{
+  /*border-spacing: 20px;*/
+  font: 160% Verdana, Helvetica, Arial, sans-serif;
+  color: #3ba6ec; 
+  vertical-align: middle;
+  padding-bottom: 0.3em;
+}
+
+a.wikicurrent {
+  font: 100% Verdana, Helvetica, Arial, sans-serif;
+  color: #3ba6ec; 
+  vertical-align: middle;
+}
+
+
+table.body {
+  border: 0;
+  /*padding: 0;
+  border-spacing: 0px;
+  border-collapse: separate;
+  */
+}
+
+td.page-header-left {
+  padding: 5px;
+  /*border-bottom: 1px solid #444444;*/
+}
+
+td.page-header-top {
+  padding: 0;
+    
+  /*border-bottom: 1px solid #444444;*/
+}
+
+td.sidebar {
+  padding: 1 0 0 1;
+}
+
+td.sidebar p.classblock {
+  padding: 0 5 0 5;
+  margin: 1 1 1 1;
+  border: 1px solid #444444;
+  background-color: #eeeeee;
+}
+
+td.sidebar p.userblock {
+  padding: 0 5 0 5;
+  margin: 1 1 1 1;
+  border: 1px solid #444444;
+  background-color: #eeeeff;
+}
+
+td.content {
+  padding: 1 5 1 5;
+  vertical-align: top;
+  width: 100%;
+}
+
+p.ok-message {
+  background-color: #22bb22;
+  padding: 5 5 5 5;
+  color: white;
+  font-weight: bold;
+}
+p.error-message {
+  background-color: #bb2222;
+  padding: 5 5 5 5;
+  color: white;
+  font-weight: bold;
+}
+
+p:first-child { 
+  margin: 0 ;
+  padding: 0;
+}
+
+/* style for forms */
+table.form {
+  padding: 2;
+  border-spacing: 0px;
+  border-collapse: separate;
+}
+
+table.form th {
+  color: #333388;
+  text-align: right;
+  vertical-align: top;
+  font-weight: normal;
+}
+table.form th.header {
+  font-weight: bold;
+  background-color: #eeeeff;
+  text-align: left;
+}
+
+table.form th.required {
+  font-weight: bold;
+}
+
+table.form td {
+  color: #333333;
+  empty-cells: show;
+  vertical-align: top;
+}
+
+table.form td.optional {
+  font-weight: bold;
+  font-style: italic;
+}
+
+table.form td.html {
+  color: #777777;
+}
+
+/* style for lists */
+table.list {
+  border-spacing: 0px;
+  border-collapse: separate;
+  vertical-align: top;
+  padding-top: 0;
+  width: 100%;
+}
+
+table.list th {
+  padding: 0 4 0 4;
+  color: #404070;
+  background-color: #eeeeff;
+  border-right: 1px solid #404070;
+  border-top: 1px solid #404070;
+  border-bottom: 1px solid #404070;
+  vertical-align: top;
+  empty-cells: show;
+}
+table.list th a[href]:hover { color: #404070 }
+table.list th a[href]:link { color: #404070 }
+table.list th a[href] { color: #404070 }
+table.list th.group {
+  background-color: #f4f4ff;
+  text-align: center;
+  font-size: 120%;
+}
+
+table.list td {
+  padding: 0 4 0 4;
+  border: 0 2 0 2;
+  border-right: 1px solid #404070;
+  color: #404070;
+  background-color: white;
+  vertical-align: top;
+  empty-cells: show;
+}
+
+table.list tr.normal td {
+  background-color: white;
+  white-space: nowrap;
+}
+
+table.list tr.alt td {
+  background-color: #efefef;
+  white-space: nowrap;
+}
+
+table.list td:first-child {
+  border-left: 1px solid #404070;
+  border-right: 1px solid #404070;
+}
+
+table.list th:first-child {
+  border-left: 1px solid #404070;
+  border-right: 1px solid #404070;
+}
+
+table.list tr.navigation th {
+  text-align: right;
+}
+table.list tr.navigation th:first-child {
+  border-right: none;
+  text-align: left;
+}
+
+
+/* style for message displays */
+table.messages {
+  border-spacing: 0px;
+  border-collapse: separate;
+  width: 100%;
+}
+
+table.messages th.header{
+  padding-top: 10px;
+  border-bottom: 1px solid gray;
+  font-weight: bold;
+  background-color: white;
+  color: #707040;
+}
+
+table.messages th {
+  font-weight: bold;
+  color: black;
+  text-align: left;
+  border-bottom: 1px solid #afafaf;
+}
+
+table.messages td {
+  font-family: monospace;
+  background-color: #efefef;
+  border-bottom: 1px solid #afafaf;
+  color: black;
+  empty-cells: show;
+  border-right: 1px solid #afafaf;
+  vertical-align: top;
+  padding: 2 5 2 5;
+}
+
+table.messages td:first-child {
+  border-left: 1px solid #afafaf;
+  border-right: 1px solid #afafaf;
+}
+
+/* style for file displays */
+table.files {
+  border-spacing: 0px;
+  border-collapse: separate;
+  width: 100%;
+}
+
+table.files th.header{
+  padding-top: 10px;
+  border-bottom: 1px solid gray;
+  font-weight: bold;
+  background-color: white;
+  color: #707040;
+}
+
+table.files th {
+  border-bottom: 1px solid #afafaf;
+  font-weight: bold;
+  text-align: left;
+}
+
+table.files td {
+  font-family: monospace;
+  empty-cells: show;
+}
+
+/* style for history displays */
+table.history {
+  border-spacing: 0px;
+  border-collapse: separate;
+  width: 100%;
+}
+
+table.history th.header{
+  padding-top: 10px;
+  border-bottom: 1px solid gray;
+  font-weight: bold;
+  background-color: white;
+  color: #707040;
+  font-size: 100%;
+}
+
+table.history th {
+  border-bottom: 1px solid #afafaf;
+  font-weight: bold;
+  text-align: left;
+  font-size: 90%;
+}
+
+table.history td {
+  font-size: 90%;
+  vertical-align: top;
+  empty-cells: show;
+}
+
+
+/* style for class list */
+table.classlist {
+  border-spacing: 0px;
+  border-collapse: separate;
+  width: 100%;
+}
+
+table.classlist th.header{
+  padding-top: 10px;
+  border-bottom: 1px solid gray;
+  font-weight: bold;
+  background-color: white;
+  color: #707040;
+}
+
+table.classlist th {
+  font-weight: bold;
+  text-align: left;
+}
+
+
+/* style for class help display */
+table.classhelp {
+  border-spacing: 0px;
+  border-collapse: separate;
+  width: 100%;
+}
+
+table.classhelp th {
+  font-weight: bold;
+  text-align: left;
+  color: #707040;
+}
+
+table.classhelp td {
+  padding: 2 2 2 2;
+  border: 1px solid black;
+  text-align: left;
+  vertical-align: top;
+  empty-cells: show;
+}
+
+
+/* style for "other" displays */
+table.otherinfo {
+  border-spacing: 0px;
+  border-collapse: separate;
+  width: 100%;
+}
+
+table.otherinfo th.header{
+  padding-top: 10px;
+  border-bottom: 1px solid gray;
+  font-weight: bold;
+  background-color: white;
+  color: #707040;
+}
+
+table.otherinfo th {
+  border-bottom: 1px solid #afafaf;
+  font-weight: bold;
+  text-align: left;
+}
+
+input {
+    border: 1px solid #8cacbb;
+    color: Black;
+    background-color: white;
+    vertical-align: middle;
+    margin-bottom: 1px; /* IE bug fix */
+    padding: 0.1em;
+}
+
+select {
+    border: 1px solid #8cacbb;
+    color: Black;
+    background-color: white;
+    vertical-align: middle;
+    margin-bottom: 1px; /* IE bug fix */
+    padding: 0.1em;
+}
+
+
+a.nonexistent {
+    color: #FF2222;
+}
+a.nonexistent:visited {
+    color: #FF2222;
+}
+a.external {
+    color: #AA6600;
+}
+
+/*
+dl,ul,ol {
+    margin-top: 1pt;
+}
+tt,pre {
+    font-family: Lucida Console,Courier New,Courier,monotype;
+    font-size: 12pt;
+}
+pre.code {
+    margin-top: 8pt;
+    margin-bottom: 8pt;
+    background-color: #FFFFEE;
+    white-space:pre;
+    border-style:solid;
+    border-width:1pt;
+    border-color:#999999;
+    color:#111111;
+    padding:5px;
+    width:100%;
+}
+*/
+div.diffold {
+    background-color: #FFFF80;
+    border-style:none;
+    border-width:thin;
+    width:100%;
+}
+div.diffnew {
+    background-color: #80FF80;
+    border-style:none;
+    border-width:thin;
+    width:100%;
+}
+div.message {
+    margin-top: 6pt;
+    background-color: #E8FFE8;
+    border-style:solid;
+    border-width:1pt;
+    border-color:#999999;
+    color:#440000;
+    padding:5px;
+    width:100%;
+}
+strong.highlight {
+    background-color: #FFBBBB;
+/* as usual, NetScape fucks up with innocent CSS
+    border-color: #FFAAAA;
+    border-style: solid;
+    border-width: 1pt;
+*/
+}
+
+table.navibar {
+    background-color: #C8C8C8;
+    border-spacing: 3px;
+}
+td.navibar {
+    background-color: #E8E8E8;
+    vertical-align: top;
+    text-align: right;
+    padding: 0px;
+}
+
+div.pagename {
+    font-size: 140%;
+    color: blue;
+    text-align: center;
+    font-weight: bold;
+    background-color: white;
+    padding: 0 ;
+}
+
+a.wikiaction, input.wikiaction {
+    color: black; 
+    text-decoration: None;
+    text-align: center;
+    color: black;
+    /*border: 1px solid #3ba6ec; */
+    margin: 4px;
+    padding: 5;
+    padding-bottom: 0;
+    white-space: nowrap;
+}
+
+a.wikiaction[href]:hover { 
+       color: black; 
+       text-decoration: none; 
+       /*background-color: #dddddd; */
+}
+
+span.wikiuserpref {
+    padding-top: 1em;
+    font-size: 120%;
+}
+
+div.wikitrail {
+    vertical-align: bottom;
+    /*font-size: -1;*/
+    padding-top: 1em;
+    display: none;
+}
+
+div.wikiaction {
+    vertical-align: middle;
+    /*border-bottom: 1px solid #8cacbb;*/
+    padding-bottom:1em;
+    text-align: left;
+    width: 100%;
+}
+
+div.wikieditmenu {
+    text-align: right;
+}
+
+form.wikiedit {
+    border: 1px solid #8cacbb;
+    background-color: #f0f0f0;
+    background-color: #fabf00;
+    padding: 1em;
+    padding-right: 0em;
+}
+
+div.legenditem {
+    padding-top: 0.5em;
+    padding-left: 0.3em;
+}
+
+span.wikitoken {
+   background-color: #eeeeee;
+}
+    
+
+div#contentspace h1:first-child, div.heading:first-child { 
+  padding-top: 0;
+  margin-top: 0;
+}
+div#contentspace h2:first-child { 
+  padding-top: 0;
+  margin-top: 0;
+}
+
+/* heading and paragraph text */
+
+div.heading, h1 {
+    font-family: Verdana, Helvetica, Arial, sans-serif;
+    background-color: #58b3ef;
+    background-color: #FFFFFF; 
+    /*color: #4893cf;*/
+    color: black;
+    padding-top: 1.0em;
+    padding-bottom:0.2em;
+    text-align: left;
+    margin-top: 0em; 
+    /*margin-bottom:8pt;*/
+    font-weight: bold;
+    font-size: 115%;
+    border-bottom: 1px solid #8CACBB;
+}
+
+
+h1, h2, h3, h4, h5, h6 {
+    color: orange;
+    clear: left;
+    font: 100% Verdana, Helvetica, Arial, sans-serif;
+    margin: 0;
+    padding-left: 0em;
+    padding-top: 1em;
+    padding-bottom: 0.2em;
+    /*border-bottom: 1px solid #8CACBB;*/
+}
+/* h1,h2 { padding-top: 0; }*/
+
+
+h1 { font-size: 145%; }
+h2 { font-size: 135%; }
+h3 { font-size: 125%; }
+h4 { font-size: 120%; }
+h5 { font-size: 110%; }
+h6 { font-size: 80%; }
+
+h1 a { text-decoration: None;}
+
+div.exception {
+  background-color: #bb2222;
+  padding: 5 5 5 5;
+  color: white;
+  font-weight: bold;
+}
+pre.exception {
+    font-size: 110%;
+    padding: 1em;
+    border: 1px solid #8cacbb;
+    color: Black;
+    background-color: #dee7ec;
+    background-color: #cccccc;
+}
+
+/* defines for navgiation bar (documentation) */
+
+
+div.direntry {
+    padding-top: 0.3em;
+    padding-bottom: 0.3em;
+    margin-right: 1em;
+    font-weight: bold;
+    background-color: #dee7ec;
+    font-size: 110%;
+}
+
+div.fileentry {
+    font-family: Verdana, Helvetica, Arial, sans-serif;
+    padding-bottom: 0.3em;
+    white-space: nowrap;
+    line-height: 150%;
+}
+
+a.fileentry {
+    white-space: nowrap;
+}
+
+
+span.left {
+    text-align: left;
+}
+span.right {
+    text-align: right;
+}
+
+div.navbar {
+  /*margin: 0;*/
+  font-size: 80% /*smaller*/;
+  font-weight: bold;
+  text-align: left;
+  /* position: fixed; */
+  top: 100pt;
+  left: 0pt; /*  auto; */
+  width: 120pt;
+  /* right: auto;
+  right: 0pt;  2em; */
+}
+
+
+div.history a {
+    /* font-size: 70%; */
+}
+
+div.wikiactiontitle { 
+  font-weight: bold;
+}
+
+/*  REST  defines */
+
+div.document {
+    margin: 0;
+}
+
+h1.title {
+    margin: 0;
+    margin-bottom: 0.5em;
+}
+
+td.toplist {
+    vertical-align: top;
+}
+
+img#pyimg {
+    position: absolute;
+    top: 4px;
+    left: 4px;
+}
+    
+div#navspace {
+    position: absolute;
+    top: 130px;
+    left: 11px;
+    font-size: 100%;
+    width: 150px;
+    overflow: hidden; /* scroll;  */
+}
+
+div#metaspace {
+    position: absolute;
+    top: 40px;
+    left: 170px;
+}
+
+div#errorline {
+    position: relative;
+    top: 5px; 
+    float: right; 
+}
+
+div#contentspace {
+    position: absolute;
+       /* font: 120% "Times New Roman", serif;*/
+    font: 110% Verdana, Helvetica, Arial, sans-serif;
+    top: 130px;
+    left: 170px;
+    margin-right: 5px;
+}
+
+div#menubar {
+/*    width: 400px; */
+    float: left;
+}
+
+/* for the documentation page */
+div#docinfoline {
+  position: relative;
+  top: 5px; 
+  left: 0px;
+
+  /*background-color: #dee7ec; */
+  padding: 5pt; 
+  padding-bottom: 1em; 
+  color: black;
+  /*border-width: 1pt;
+  border-style: solid;*/
+
+}
+
+div#docnavlist {
+  /*background-color: #dee7ec; */
+  padding: 5pt; 
+  padding-bottom: 2em; 
+  color: black;
+  border-width: 1pt;
+  /*border-style: solid;*/
+}
+
+
+/* text markup */
+
+div.listtitle {
+    color: Black;
+    clear: left;
+    font: 120% Verdana, Helvetica, Arial, sans-serif;
+    margin: 0;
+    padding-left: 0em;
+    padding-top: 0em;
+    padding-bottom: 0.2em;
+    margin-right: 0.5em;
+    border-bottom: 1px solid #8CACBB;
+}
+
+div.actionbox h3 { 
+  padding-top: 0;
+  padding-right: 0.5em;
+  padding-left: 0.5em;
+  background-color: #fabf00;
+  text-align: center;
+  border: 1px solid black; /* 8cacbb; */
+}
+
+div.actionbox a { 
+  display: block;
+  padding-bottom: 0.5em;
+  padding-top: 0.5em;
+  margin-left: 0.5em;
+}
+
+div.actionbox a.history { 
+  display: block;
+  padding-bottom: 0.5em;
+  padding-top: 0.5em;
+  margin-left: 0.5em;
+  font-size: 90%; 
+}
+
+div.actionbox { 
+  margin-bottom: 2em;
+  padding-bottom: 1em;
+  overflow: hidden; /* scroll;  */
+}
+
+/* taken from docutils (oh dear, a bit senseless) */
+ol.simple, ul.simple {
+  margin-bottom: 1em }
+
+ol.arabic {
+  list-style: decimal }
+
+ol.loweralpha {
+  list-style: lower-alpha }
+
+ol.upperalpha {
+  list-style: upper-alpha }
+
+ol.lowerroman {
+  list-style: lower-roman }
+
+ol.upperroman {
+  list-style: upper-roman }
+
+
+/*
+:Author: David Goodger
+:Contact: goodger@users.sourceforge.net
+:date: $Date: 2003/01/22 22:26:48 $
+:version: $Revision: 1.29 $
+:copyright: This stylesheet has been placed in the public domain.
+
+Default cascading style sheet for the HTML output of Docutils.
+*/
+/*
+.first {
+  margin-top: 0 }
+
+.last {
+  margin-bottom: 0 }
+
+a.toc-backref {
+  text-decoration: none ;
+  color: black }
+
+dd {
+  margin-bottom: 0.5em }
+
+div.abstract {
+  margin: 2em 5em }
+
+div.abstract p.topic-title {
+  font-weight: bold ;
+  text-align: center }
+
+div.attention, div.caution, div.danger, div.error, div.hint,
+div.important, div.note, div.tip, div.warning {
+  margin: 2em ;
+  border: medium outset ;
+  padding: 1em }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title {
+  color: red ;
+  font-weight: bold ;
+  font-family: sans-serif }
+
+div.hint p.admonition-title, div.important p.admonition-title,
+div.note p.admonition-title, div.tip p.admonition-title {
+  font-weight: bold ;
+  font-family: sans-serif }
+
+div.dedication {
+  margin: 2em 5em ;
+  text-align: center ;
+  font-style: italic }
+
+div.dedication p.topic-title {
+  font-weight: bold ;
+  font-style: normal }
+
+div.figure {
+  margin-left: 2em }
+
+div.footer, div.header {
+  font-size: smaller }
+
+div.system-messages {
+  margin: 5em }
+
+div.system-messages h1 {
+  color: red }
+
+div.system-message {
+  border: medium outset ;
+  padding: 1em }
+
+div.system-message p.system-message-title {
+  color: red ;
+  font-weight: bold }
+
+div.topic {
+  margin: 2em }
+
+h1.title {
+  text-align: center ;
+  color: orange}
+
+h2.subtitle {
+  color: orange;
+  text-align: center }
+
+hr {
+  width: 75% }
+
+p.caption {
+  font-style: italic }
+
+p.credits {
+  font-style: italic ;
+  font-size: smaller }
+
+p.label {
+  white-space: nowrap }
+
+p.topic-title {
+  font-weight: bold }
+
+pre.address {
+  margin-bottom: 0 ;
+  margin-top: 0 ;
+  font-family: serif ;
+  font-size: 100% }
+
+pre.line-block {
+  font-family: serif ;
+  font-size: 100% }
+
+pre.literal-block, pre.doctest-block {
+  margin-left: 2em ;
+  margin-right: 2em ;
+  background-color: #eeeeee }
+
+span.classifier {
+  font-family: sans-serif ;
+  font-style: oblique }
+
+span.classifier-delimiter {
+  font-family: sans-serif ;
+  font-weight: bold }
+
+span.interpreted {
+  font-family: sans-serif }
+
+span.option {
+  white-space: nowrap }
+
+span.option-argument {
+  font-style: italic }
+
+span.pre {
+  white-space: pre }
+
+span.problematic {
+  color: red }
+
+table {
+  margin-top: 0.5em ;
+  margin-bottom: 0.5em }
+
+table.citation {
+  border-left: solid thin gray ;
+  padding-left: 0.5ex }
+
+table.docinfo {
+  margin: 2em 4em }
+
+table.footnote {
+  border-left: solid thin black ;
+  padding-left: 0.5ex }
+
+td, th {
+  padding-left: 0.5em ;
+  padding-right: 0.5em ;
+  vertical-align: top }
+
+th.docinfo-name, th.field-name {
+  font-weight: bold ;
+  text-align: left ;
+  white-space: nowrap }
+
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+  font-size: 100% }
+
+tt {
+  background-color: #eeeeee }
+
+ul.auto-toc {
+  list-style-type: none }
+*/
+
+div.section {
+  margin-top: 1.0em ;
+}    
diff --git a/doc/eole-report/eolreport/build/docutils.css b/doc/eole-report/eolreport/build/docutils.css
new file mode 100644 (file)
index 0000000..f03e03d
--- /dev/null
@@ -0,0 +1,255 @@
+.first {
+  margin-top: 0 ! important }
+
+.last {
+  margin-bottom: 0 ! important }
+
+.hidden {
+  display: none }
+
+a.toc-backref {
+  text-decoration: none ;
+  color: inherit }
+
+blockquote.epigraph {
+  margin: 2em 5em }
+
+dl.docutils dd {
+  margin-bottom: 0.5em }
+
+dl.docutils dt {
+  font-weight: bold }
+
+dl dt { line-height: 150% }
+
+div.abstract {
+  margin: 2em 5em }
+
+div.abstract p.topic-title {
+  font-weight: bold ;
+  text-align: center }
+
+div.admonition, div.attention, div.caution, div.danger, div.error,
+div.hint, div.important, div.note, div.tip, div.warning {
+  margin: 2em ;
+  border: medium outset ;
+  padding: 1em }
+
+div.admonition p.admonition-title, div.hint p.admonition-title,
+div.important p.admonition-title, div.note p.admonition-title,
+div.tip p.admonition-title {
+  font-weight: bold ;
+  font-family: sans-serif }
+
+div.attention p.admonition-title, div.caution p.admonition-title,
+div.danger p.admonition-title, div.error p.admonition-title,
+div.warning p.admonition-title {
+  color: red ;
+  font-weight: bold ;
+  font-family: sans-serif }
+
+div.compound .compound-first, div.compound .compound-middle {
+  margin-bottom: 0.5em }
+
+div.compound .compound-last, div.compound .compound-middle {
+  margin-top: 0.5em }
+
+div.dedication {
+  margin: 2em 5em ;
+  text-align: center ;
+  font-style: italic }
+
+div.dedication p.topic-title {
+  font-weight: bold ;
+  font-style: normal }
+
+div.document {
+  width: 600px ;
+  margin-left: 5em ;
+  margin-right: 5em }
+
+div.figure {
+  margin-left: 2em }
+
+div.footer, div.header {
+  font-size: smaller }
+
+div.line-block {
+  display: block ;
+  margin-top: 1em ;
+  margin-bottom: 1em }
+
+div.line-block div.line-block {
+  margin-top: 0 ;
+  margin-bottom: 0 ;
+  margin-left: 1.5em }
+
+div.sidebar {
+  margin-left: 1em ;
+  border: medium outset ;
+  padding: 1em ;
+  background-color: #ffffee ;
+  width: 40% ;
+  float: right ;
+  clear: right }
+
+div.sidebar p.rubric {
+  font-family: sans-serif ;
+  font-size: medium }
+
+div.system-messages {
+  margin: 5em }
+
+div.system-messages h1 {
+  color: red }
+
+div.system-message {
+  border: medium outset ;
+  padding: 1em }
+
+div.system-message p.system-message-title {
+  color: red ;
+  font-weight: bold }
+
+div.topic {
+  margin: 2em }
+
+h1, h2, h3, h4, h5 {
+  font-family: sans-serif ;
+  line-height: 150% ;
+  color: orange} /* #666 } */
+
+h1.title {
+  text-align: center
+  }
+h2.subtitle {
+  text-align: center }
+
+hr.docutils {
+  width: 75% }
+
+ol.simple, ul.simple {
+  margin-bottom: 1em }
+
+ol.arabic {
+  list-style: decimal }
+
+ol.loweralpha {
+  list-style: lower-alpha }
+
+ol.upperalpha {
+  list-style: upper-alpha }
+
+ol.lowerroman {
+  list-style: lower-roman }
+
+ol.upperroman {
+  list-style: upper-roman }
+
+p.attribution {
+  text-align: right ;
+  margin-left: 50% }
+
+p.caption {
+  font-style: italic }
+
+p.credits {
+  font-style: italic ;
+  font-size: smaller }
+
+p.label {
+  white-space: nowrap }
+
+p.rubric {
+  font-weight: bold ;
+  font-size: larger ;
+  color: maroon ;
+  text-align: center }
+
+p.sidebar-title {
+  font-family: sans-serif ;
+  font-weight: bold ;
+  font-size: larger }
+
+p.sidebar-subtitle {
+  font-family: sans-serif ;
+  font-weight: bold }
+
+p.topic-title {
+  font-weight: bold }
+
+pre.address {
+  margin-bottom: 0 ;
+  margin-top: 0 ;
+  font-family: serif ;
+  font-size: 100% }
+
+pre.line-block {
+  font-family: serif ;
+  font-size: 100% }
+
+pre.literal-block, pre.doctest-block {
+  margin-left: 2em ;
+  margin-right: 2em ;
+  font-size: small ;
+  background-color: #eeeeee }
+
+span.classifier {
+  font-family: sans-serif ;
+  font-style: oblique }
+
+span.classifier-delimiter {
+  font-family: sans-serif ;
+  font-weight: bold }
+
+span.interpreted {
+  font-family: sans-serif }
+
+span.option {
+  white-space: nowrap }
+
+span.option-argument {
+  font-style: italic }
+
+span.pre {
+  white-space: pre }
+
+span.problematic {
+  color: red }
+
+table.citation {
+  border-left: solid thin gray }
+
+table.docinfo {
+  /* float: right ; */
+  margin: 2em 4em ;
+  color: #666 }
+
+table.docutils {
+  margin-top: 0.5em ;
+  margin-bottom: 0.5em }
+
+table.footnote {
+  border-left: solid thin black }
+
+table.docutils td, table.docutils th,
+table.docinfo td, table.docinfo th {
+  padding-left: 0.5em ;
+  padding-right: 0.5em ;
+  vertical-align: top }
+
+th.docinfo-name, th.field-name {
+  font-weight: bold ;
+  text-align: right ;
+  white-space: nowrap }
+
+h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
+h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
+  font-size: 100% }
+
+tt.docutils {
+  background-color: #eeeeee }
+
+ul.auto-toc {
+  list-style-type: none }
+
diff --git a/doc/eole-report/eolreport/build/imgs/eol.png b/doc/eole-report/eolreport/build/imgs/eol.png
new file mode 100644 (file)
index 0000000..5b23138
Binary files /dev/null and b/doc/eole-report/eolreport/build/imgs/eol.png differ
diff --git a/doc/eole-report/eolreport/build/imgs/logo.png b/doc/eole-report/eolreport/build/imgs/logo.png
new file mode 100644 (file)
index 0000000..9c554f4
Binary files /dev/null and b/doc/eole-report/eolreport/build/imgs/logo.png differ
diff --git a/doc/eole-report/eolreport/build/index-report.html b/doc/eole-report/eolreport/build/index-report.html
new file mode 100644 (file)
index 0000000..098c9c7
--- /dev/null
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="generator" content="Docutils 0.9: http://docutils.sourceforge.net/" />
+<title>rapports eole</title>
+<style type="text/css">
+
+@import url(docutils.css);
+@import url(default.css);
+a:link {
+       color: orange;
+       font-weight: bold;
+       text-decoration: none;
+}
+a:visited {
+       text-decoration: none;
+       color: #999999;
+}
+a:hover {
+       text-decoration: none;
+       color: #999999;
+}
+a:active {
+       text-decoration: none;
+       color: #999999;
+}
+
+.header {
+       color: orange;
+       background-color: white;
+       padding: 1em;
+}
+.footer {
+       color: #666;
+       background-color: inherit;
+       font-size: 75%;
+}
+
+
+
+</style>
+</head>
+<body>
+<div class="document">
+
+
+<img alt="imgs/eol.png" class="align-right" src="imgs/eol.png" />
+<table class="docutils field-list" frame="void" rules="none">
+<col class="field-name" />
+<col class="field-body" />
+<tbody valign="top">
+<tr class="field"><th class="field-name">date:</th><td class="field-body">mai 2012</td>
+</tr>
+<tr class="field"><th class="field-name">description:</th><td class="field-body">rapports <tt class="docutils literal">Créole</tt>, compatibilités <tt class="docutils literal">Creole</tt> et <tt class="docutils literal">tiramisu</tt></td>
+</tr>
+</tbody>
+</table>
+<div class="section" id="vue-d-ensemble-des-rapports">
+<h1>Vue d'ensemble des rapports</h1>
+<p>Les rapports ci-dessous résument et permettent de donner des points d'appui à
+des discussions de recherche et développement concernant l'évolution du
+projet <tt class="docutils literal">Creole</tt> (comprenant <tt class="docutils literal">Creole_Serv</tt>). Il y a aussi le support de
+documentation développeur <tt class="docutils literal">tiramisu</tt> (en anglais) qui constitue une bonne
+base pour connaître et comprendre plus en détails les motivations de
+la nouvelle implementation.</p>
+<ul class="simple">
+<li><a class="reference external" href="pdfreport/D01AccesVariables.pdf">D01AccesVariables.pdf</a></li>
+<li><a class="reference external" href="pdfreport/D02CoherenceVariables.pdf">D02CoherenceVariables.pdf</a></li>
+<li><a class="reference external" href="pdfreport/D03ReglesEtats.pdf">D03ReglesEtats.pdf</a></li>
+</ul>
+</div>
+</div>
+</body>
+</html>
diff --git a/doc/eole-report/eolreport/build/pdfreport/D01AccesVariables.pdf b/doc/eole-report/eolreport/build/pdfreport/D01AccesVariables.pdf
new file mode 100644 (file)
index 0000000..471a375
Binary files /dev/null and b/doc/eole-report/eolreport/build/pdfreport/D01AccesVariables.pdf differ
diff --git a/doc/eole-report/eolreport/build/pdfreport/D02CoherenceVariables.pdf b/doc/eole-report/eolreport/build/pdfreport/D02CoherenceVariables.pdf
new file mode 100644 (file)
index 0000000..d8c37fe
Binary files /dev/null and b/doc/eole-report/eolreport/build/pdfreport/D02CoherenceVariables.pdf differ
diff --git a/doc/eole-report/eolreport/build/pdfreport/D03ReglesEtats.pdf b/doc/eole-report/eolreport/build/pdfreport/D03ReglesEtats.pdf
new file mode 100644 (file)
index 0000000..7ec977c
Binary files /dev/null and b/doc/eole-report/eolreport/build/pdfreport/D03ReglesEtats.pdf differ
diff --git a/doc/eole-report/eolreport/build/pdfreport/make_index b/doc/eole-report/eolreport/build/pdfreport/make_index
new file mode 100755 (executable)
index 0000000..ca207c8
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+import sys
+from glob import glob
+from os.path import isfile, dirname, abspath, join, basename, splitext
+from rst import Rest, Paragraph, Strong, ListItem, Link
+
+
+here = abspath(dirname(__file__))
+html = glob(join(here, '*.pdf'))
+
+basehtml = [basename(htm) for htm in html]
+basehtml.sort()
+
+content = Rest()
+
+for htm in basehtml:
+    link = Link( htm , "pdfreport/" +htm)
+    content.add(ListItem(link))
+
+sys.stdout.write(content.text())
+
+
diff --git a/doc/eole-report/eolreport/build/pdfreport/rst.py b/doc/eole-report/eolreport/build/pdfreport/rst.py
new file mode 100644 (file)
index 0000000..7548cdd
--- /dev/null
@@ -0,0 +1,410 @@
+# unproudly borrowed from pypy : 
+# http://codespeak.net/svn/pypy/trunk/pypy/tool/rest/rst.py
+""" reStructuredText generation tools
+
+    provides an api to build a tree from nodes, which can be converted to
+    ReStructuredText on demand
+
+    note that not all of ReST is supported, a usable subset is offered, but
+    certain features aren't supported, and also certain details (like how links
+    are generated, or how escaping is done) can not be controlled
+"""
+
+import re
+
+def escape(txt):
+    """escape ReST markup"""
+    if not isinstance(txt, str) and not isinstance(txt, unicode):
+        txt = str(txt)
+    # XXX this takes a very naive approach to escaping, but it seems to be
+    # sufficient...
+    for c in '\\*`|:_':
+        txt = txt.replace(c, '\\%s' % (c,))
+    return txt
+
+class RestError(Exception):
+    """ raised on containment errors (wrong parent) """
+
+class AbstractMetaclass(type):
+    def __new__(cls, *args):
+        obj = super(AbstractMetaclass, cls).__new__(cls, *args)
+        parent_cls = obj.parentclass
+        if parent_cls is None:
+            return obj
+        if not isinstance(parent_cls, list):
+            class_list = [parent_cls]
+        else:
+            class_list = parent_cls
+        if obj.allow_nesting:
+            class_list.append(obj)
+        
+        for _class in class_list:
+            if not _class.allowed_child:
+                _class.allowed_child = {obj:True}
+            else:
+                _class.allowed_child[obj] = True
+        return obj
+
+class AbstractNode(object):
+    """ Base class implementing rest generation
+    """
+    sep = ''
+    __metaclass__ = AbstractMetaclass
+    parentclass = None # this exists to allow parent to know what
+        # children can exist
+    allow_nesting = False
+    allowed_child = {}
+    defaults = {}
+    
+    _reg_whitespace = re.compile('\s+')
+
+    def __init__(self, *args, **kwargs):
+        self.parent = None
+        self.children = []
+        for child in args:
+            self._add(child)
+        for arg in kwargs:
+            setattr(self, arg, kwargs[arg])
+    
+    def join(self, *children):
+        """ add child nodes
+        
+            returns a reference to self
+        """
+        for child in children:
+            self._add(child)
+        return self
+    
+    def add(self, child):
+        """ adds a child node
+            
+            returns a reference to the child
+        """
+        self._add(child)
+        return child
+        
+    def _add(self, child):
+        if child.__class__ not in self.allowed_child:
+            raise RestError("%r cannot be child of %r" % \
+                (child.__class__, self.__class__))
+        self.children.append(child)
+        child.parent = self
+    
+    def __getitem__(self, item):
+        return self.children[item]
+    
+    def __setitem__(self, item, value):
+        self.children[item] = value
+
+    def text(self):
+        """ return a ReST string representation of the node """
+        return self.sep.join([child.text() for child in self.children])
+    
+    def wordlist(self):
+        """ return a list of ReST strings for this node and its children """ 
+        return [self.text()]
+
+class Rest(AbstractNode):
+    """ Root node of a document """
+    
+    sep = "\n\n"
+    def __init__(self, *args, **kwargs):
+        AbstractNode.__init__(self, *args, **kwargs)
+        self.links = {}
+    
+    def render_links(self, check=False):
+        """render the link attachments of the document"""
+        assert not check, "Link checking not implemented"
+        if not self.links:
+            return ""
+        link_texts = []
+        # XXX this could check for duplicates and remove them...
+        for link, target in self.links.iteritems():
+            link_texts.append(".. _`%s`: %s" % (escape(link), target))
+        return "\n" + "\n".join(link_texts) + "\n\n"
+
+    def text(self):
+        outcome = []
+        if (isinstance(self.children[0], Transition) or
+                isinstance(self.children[-1], Transition)):
+            raise ValueError, ('document must not begin or end with a '
+                               'transition')
+        for child in self.children:
+            outcome.append(child.text())
+        
+        # always a trailing newline
+        text = self.sep.join([i for i in outcome if i]) + "\n"
+        return text + self.render_links()
+
+class Transition(AbstractNode):
+    """ a horizontal line """
+    parentclass = Rest
+
+    def __init__(self, char='-', width=80, *args, **kwargs):
+        self.char = char
+        self.width = width
+        super(Transition, self).__init__(*args, **kwargs)
+        
+    def text(self):
+        return (self.width - 1) * self.char
+
+class Paragraph(AbstractNode):
+    """ simple paragraph """
+
+    parentclass = Rest
+    sep = " "
+    indent = ""
+    # FIXME
+    width = 880
+    
+    def __init__(self, *args, **kwargs):
+        # make shortcut
+        args = list(args)
+        for num, arg in enumerate(args):
+            if isinstance(arg, str):
+                args[num] = Text(arg)
+        super(Paragraph, self).__init__(*args, **kwargs)
+    
+    def text(self):
+        texts = []
+        for child in self.children:
+            texts += child.wordlist()
+        
+        buf = []
+        outcome = []
+        lgt = len(self.indent)
+        
+        def grab(buf):
+            outcome.append(self.indent + self.sep.join(buf))
+        
+        texts.reverse()
+        while texts:
+            next = texts[-1]
+            if not next:
+                texts.pop()
+                continue
+            if lgt + len(self.sep) + len(next) <= self.width or not buf:
+                buf.append(next)
+                lgt += len(next) + len(self.sep)
+                texts.pop()
+            else:
+                grab(buf)
+                lgt = len(self.indent)
+                buf = []
+        grab(buf)
+        return "\n".join(outcome)
+    
+class SubParagraph(Paragraph):
+    """ indented sub paragraph """
+
+    indent = " "
+    
+class Title(Paragraph):
+    """ title element """
+
+    parentclass = Rest
+    belowchar = "="
+    abovechar = ""
+    
+    def text(self):
+        txt = self._get_text()
+        lines = []
+        if self.abovechar:
+            lines.append(self.abovechar * len(txt))
+        lines.append(txt)
+        if self.belowchar:
+            lines.append(self.belowchar * len(txt))
+        return "\n".join(lines)
+
+    def _get_text(self):
+        txt = []
+        for node in self.children:
+            txt += node.wordlist()
+        return ' '.join(txt)
+
+class AbstractText(AbstractNode):
+    parentclass = [Paragraph, Title]
+    start = ""
+    end = ""
+    def __init__(self, _text):
+        self._text = _text
+    
+    def text(self):
+        text = self.escape(self._text)
+        return self.start + text + self.end
+
+    def escape(self, text):
+        if not isinstance(text, str) and not isinstance(text, unicode):
+            text = str(text)
+        if self.start:
+            text = text.replace(self.start, '\\%s' % (self.start,))
+        if self.end and self.end != self.start:
+            text = text.replace(self.end, '\\%s' % (self.end,))
+        return text
+    
+class Text(AbstractText):
+    def wordlist(self):
+        text = escape(self._text)
+        return self._reg_whitespace.split(text)
+
+class LiteralBlock(AbstractText):
+    parentclass = Rest
+    start = '::\n\n'
+
+    def text(self):
+        if not self._text.strip():
+            return ''
+        text = self.escape(self._text).split('\n')
+        for i, line in enumerate(text):
+            if line.strip():
+                text[i] = '  %s' % (line,)
+        return self.start + '\n'.join(text)
+
+class Em(AbstractText):
+    start = "*"
+    end = "*"
+
+class Strong(AbstractText):
+    start = "**"
+    end = "**"
+
+class Quote(AbstractText):
+    start = '``'
+    end = '``'
+
+class Anchor(AbstractText):
+    start = '_`'
+    end = '`'
+
+class Footnote(AbstractText):
+    def __init__(self, note, symbol=False):
+        raise NotImplemented('XXX')
+
+class Citation(AbstractText):
+    def __init__(self, text, cite):
+        raise NotImplemented('XXX')
+
+class ListItem(Paragraph):
+    allow_nesting = True
+    item_chars = '*+-'
+    
+    def text(self):
+        idepth = self.get_indent_depth()
+        indent = self.indent + (idepth + 1) * '  '
+        txt = '\n\n'.join(self.render_children(indent))
+        ret = []
+        item_char = self.item_chars[idepth]
+        ret += [indent[len(item_char)+1:], item_char, ' ', txt[len(indent):]]
+        return ''.join(ret)
+    
+    def render_children(self, indent):
+        txt = []
+        buffer = []
+        def render_buffer(fro, to):
+            if not fro:
+                return
+            p = Paragraph(indent=indent, *fro)
+            p.parent = self.parent
+            to.append(p.text())
+        for child in self.children:
+            if isinstance(child, AbstractText):
+                buffer.append(child)
+            else:
+                if buffer:
+                    render_buffer(buffer, txt)
+                    buffer = []
+                txt.append(child.text())
+
+        render_buffer(buffer, txt)
+        return txt
+
+    def get_indent_depth(self):
+        depth = 0
+        current = self
+        while (current.parent is not None and
+                isinstance(current.parent, ListItem)):
+            depth += 1
+            current = current.parent
+        return depth
+
+class OrderedListItem(ListItem):
+    item_chars = ["#."] * 5
+
+class DListItem(ListItem):
+    item_chars = None
+    def __init__(self, term, definition, *args, **kwargs):
+        self.term = term
+        super(DListItem, self).__init__(definition, *args, **kwargs)
+
+    def text(self):
+        idepth = self.get_indent_depth()
+        indent = self.indent + (idepth + 1) * '  '
+        txt = '\n\n'.join(self.render_children(indent))
+        ret = []
+        ret += [indent[2:], self.term, '\n', txt]
+        return ''.join(ret)
+
+class Link(AbstractText):
+    start = '`'
+    end = '`_'
+
+    def __init__(self, _text, target):
+        self._text = _text
+        self.target = target
+        self.rest = None
+    
+    def text(self):
+        if self.rest is None:
+            self.rest = self.find_rest()
+        if self.rest.links.get(self._text, self.target) != self.target:
+            raise ValueError('link name %r already in use for a different '
+                             'target' % (self.target,))
+        self.rest.links[self._text] = self.target
+        return AbstractText.text(self)
+
+    def find_rest(self):
+        # XXX little overkill, but who cares...
+        next = self
+        while next.parent is not None:
+            next = next.parent
+        return next
+
+class InternalLink(AbstractText):
+    start = '`'
+    end = '`_'
+    
+class LinkTarget(Paragraph):
+    def __init__(self, name, target):
+        self.name = name
+        self.target = target
+    
+    def text(self):
+        return ".. _`%s`:%s\n" % (self.name, self.target)
+
+class Substitution(AbstractText):
+    def __init__(self, text, **kwargs):
+        raise NotImplemented('XXX')
+
+class Directive(Paragraph):
+    indent = '   '
+    def __init__(self, name, *args, **options):
+        self.name = name
+        self.content = args
+        super(Directive, self).__init__()
+        self.options = options
+        
+    def text(self):
+        # XXX not very pretty...
+        txt = '.. %s::' % (self.name,)
+        options = '\n'.join(['    :%s: %s' % (k, v) for (k, v) in
+                             self.options.iteritems()])
+        if options:
+            txt += '\n%s' % (options,)
+
+        if self.content:
+            txt += '\n'
+            for item in self.content:
+                txt += '\n    ' + item
+        
+        return txt
+
diff --git a/doc/eole-report/eolreport/build/style.css b/doc/eole-report/eolreport/build/style.css
new file mode 100644 (file)
index 0000000..28c256e
--- /dev/null
@@ -0,0 +1,32 @@
+@import url(docutils.css);
+@import url(default.css);
+a:link {
+       color: orange;
+       font-weight: bold;
+       text-decoration: none;
+}
+a:visited {
+       text-decoration: none;
+       color: #999999;
+}
+a:hover {
+       text-decoration: none;
+       color: #999999;
+}
+a:active {
+       text-decoration: none;
+       color: #999999;
+}
+
+.header {
+       color: orange;
+       background-color: white;
+       padding: 1em;
+}
+.footer {
+       color: #666;
+       background-color: inherit;
+       font-size: 75%;
+}
+
+
diff --git a/doc/eole-report/eolreport/inc/00-Redacteur.txt b/doc/eole-report/eolreport/inc/00-Redacteur.txt
new file mode 100644 (file)
index 0000000..554e81b
--- /dev/null
@@ -0,0 +1,12 @@
+.. container:: rubric
+
+    **Rédacteurs**
+
+           | Gwenaël Rémond (gremond@cadoles.com)
+           | Emmanuel Garette (egarette@cadoles.com)
+
+**Référence**
+
+    | ``tiramisu/doc/eole-reports``
+    | ``git clone ssh://gitosis@git.cadol.es:2222/tiramisu.git``
+    
diff --git a/doc/eole-report/eolreport/inc/eol.png b/doc/eole-report/eolreport/inc/eol.png
new file mode 100644 (file)
index 0000000..5b23138
Binary files /dev/null and b/doc/eole-report/eolreport/inc/eol.png differ
diff --git a/doc/eole-report/eolreport/inc/logo.png b/doc/eole-report/eolreport/inc/logo.png
new file mode 100644 (file)
index 0000000..9c554f4
Binary files /dev/null and b/doc/eole-report/eolreport/inc/logo.png differ
diff --git a/doc/eole-report/eolreport/inc/menjva.gif b/doc/eole-report/eolreport/inc/menjva.gif
new file mode 100644 (file)
index 0000000..49d17a6
Binary files /dev/null and b/doc/eole-report/eolreport/inc/menjva.gif differ
diff --git a/doc/eole-report/eolreport/inc/preambule.txt b/doc/eole-report/eolreport/inc/preambule.txt
new file mode 100644 (file)
index 0000000..c25bb90
--- /dev/null
@@ -0,0 +1,18 @@
+.. csv-table::
+
+    .. image:: inc/logo.png, .. image:: inc/eol.png
+
+.. container:: title
+
+  Rapports de discussions de recherche et développements 
+
+------------
+
+.. container:: subtitle
+
+  Comparaison ``tiramisu`` et ``Créole`` 
+
+.. include:: 00-Redacteur.txt
+
+
+
diff --git a/doc/eole-report/eolreport/index-report.txt b/doc/eole-report/eolreport/index-report.txt
new file mode 100644 (file)
index 0000000..4670b94
--- /dev/null
@@ -0,0 +1,33 @@
+.. default-role:: literal
+
+.. title:: rapports eole
+
+
+.. image:: imgs/eol.png
+    :align: right
+
+:date: mai 2012
+:description: rapports `Créole`, compatibilités `Creole` et `tiramisu`
+
+
+Vue d'ensemble des rapports
+===================================
+
+Les rapports ci-dessous résument et permettent de donner des points d'appui à 
+des discussions de recherche et développement concernant l'évolution du 
+projet `Creole` (comprenant `Creole_Serv`). Il y a aussi le support de 
+documentation développeur `tiramisu` (en anglais) qui constitue une bonne 
+base pour connaître et comprendre plus en détails les motivations de 
+la nouvelle implementation.
+
+
+* `D01AccesVariables.pdf`_
+
+* `D02CoherenceVariables.pdf`_
+
+* `D03ReglesEtats.pdf`_
+
+.. _`D03ReglesEtats.pdf`: pdfreport/D03ReglesEtats.pdf
+.. _`D02CoherenceVariables.pdf`: pdfreport/D02CoherenceVariables.pdf
+.. _`D01AccesVariables.pdf`: pdfreport/D01AccesVariables.pdf
+
diff --git a/doc/eole-report/eolreport/styles.odt b/doc/eole-report/eolreport/styles.odt
new file mode 100644 (file)
index 0000000..2121251
Binary files /dev/null and b/doc/eole-report/eolreport/styles.odt differ
diff --git a/doc/eole-report/proposal/Makefile b/doc/eole-report/proposal/Makefile
new file mode 100644 (file)
index 0000000..7b32a56
--- /dev/null
@@ -0,0 +1,12 @@
+SRC=$(wildcard *.tex)
+OBJ=$(subst .tex,.pdf,$(SRC))
+
+pdf: $(OBJ)
+
+%.pdf: %.tex
+       pdflatex $< 
+
+clean:
+       rm -f $(OBJ)
+       rm -f *.aux *.log *.toc *.snm *.out *.nav
+       
diff --git a/doc/eole-report/proposal/comparaison.tex b/doc/eole-report/proposal/comparaison.tex
new file mode 100644 (file)
index 0000000..53e168a
--- /dev/null
@@ -0,0 +1,16 @@
+\begin{frame}
+ \frametitle{Comparaison entre le noyau de Créole et Tiramisu}
+ \begin{itemize}
+  \item \emph{Créole} : \texttt{cfgparser.py + typeeole.py} $ \Rightarrow 2500$ lignes ;
+  \item \emph{Tiramisu} : \texttt{config.py + option.py} $ \Rightarrow 800$ lignes ;
+  \item Et en plus : 
+ \begin{itemize}
+  \item \emph{Créole} valide le type mais pas la structure (fait confiance au \texttt{XML}) ;
+  \item \emph{Créole} difficile d'ajouter un type à cause de la métaclasse ;
+  \item \emph{Tiramisu} valide le type \emph{et} la structure, ajout de types aisé.
+ \end{itemize}
+  \item \texttt{eole-report/D02CoherenceVariables.pdf}
+ \end{itemize}
+\end{frame}
+
+
diff --git a/doc/eole-report/proposal/definition.tex b/doc/eole-report/proposal/definition.tex
new file mode 100644 (file)
index 0000000..5b10ac1
--- /dev/null
@@ -0,0 +1,33 @@
+\begin{frame}
+ \frametitle{Définition d'un gestionnaire de configuration}
+ \begin{itemize}
+  \item \emph{dictionnaire} de données (au sens python) ;
+  \item clefs-valeurs, mais quelles valeurs exactement ? ;
+  \item \texttt{eole-report/D01AccesVariables.pdf}
+\end{itemize}
+
+\end{frame}
+
+\begin{frame}
+ \frametitle{Définition d'un gestionnaire de configuration}
+ \begin{itemize}
+  \item espaces de nommages ;
+  \item c'est la configuration qui est responsable de l'accès aux valeurs ;
+  \item une configuration aisément manipulable ;
+  \item un point d'entrée unique.
+  \item \texttt{eole-report/D01AccesVariables.pdf}
+ \end{itemize}
+
+\end{frame}
+
+\begin{frame}
+ \frametitle{Définition d'un gestionnaire de configuration 2}
+ \begin{itemize}
+  \item serveur de données de configuration ;
+  \item $1^{ere}$ méthode : exportation (snapshot) d'un état de la config $\Rightarrow$ Créole ;
+  \item $2^{eme}$ méthode : JIT (just in time) calculation, une modification 
+de l'état de la configuration est possible \emph{pendant} la manipulation et l'utilisation $\Rightarrow$ Tiramisu.
+  \item \texttt{doc/getting-started.html}
+ \end{itemize}
+\end{frame}
+
diff --git a/doc/eole-report/proposal/statut.tex b/doc/eole-report/proposal/statut.tex
new file mode 100644 (file)
index 0000000..1f665b6
--- /dev/null
@@ -0,0 +1,51 @@
+\begin{frame}
+ \frametitle{Organisation en espace de nommage}
+ \begin{itemize}
+  \item dans \emph{tiramisu} l'accent est mis sur l'organisation arborescente des données ;
+  \item la validation des options de configuration se fait par l'appartenance aux groupes (families, master/slaves \dots) ;
+  \item l'organisation en groupes est unifiée par l'espace de nommage ;
+  \item la lisibilité de l'API excellente, contrairement à \emph{Creole}
+  \item \texttt{eole-report/D03ReglesEtats.pdf}
+  \item lisibilité d'une config : \texttt{tiramisu/report/build/index.html} rapport html d'une config
+
+ \end{itemize}
+
+\end{frame}
+
+\begin{frame}
+ \frametitle{Etats de la configuration}
+ \begin{itemize}
+  \item système d'états de la configuration par droits d'accès
+  \item \texttt{read write}, \texttt{read only};
+  \item correspond à \texttt{freeze}, \texttt{hidden}, \texttt{disabled} \dots ;
+  \item \texttt{doc/status.html}
+  \item \texttt{eole-report/D03ReglesEtats.pdf}
+ \end{itemize}
+
+\end{frame}
+
+\begin{frame}
+ \frametitle{hidden if in, hidden if not in}
+ \begin{itemize}
+ \item les hidden if in, disabled if, \dots sont généralisés
+ \item dans tiramisu, ce sont des pré-requis sur une (des) variables
+ \item \texttt{eole-report/D03ReglesEtats.pdf}
+ \item \texttt{doc/consistency.html}
+ \end{itemize}
+
+\end{frame}
+
+\begin{frame}
+ \frametitle{compatibilité Créole : ce qui reste à faire}
+ \begin{itemize}
+\item tous les options spéciales sont implémentées (auto, fill, obligatoire, \dots)
+\item tous les états sont implémentés (hidden, disabled, mode (normal/expert), \dots)
+\item reste la librairie des fonctions pour les variables automatiques
+\item les "valprec" (valeur précédentes) 
+\item fixer les comportement des hides (sous-groupes récursifs, \dots)
+\item validations master/slaves, validations globales (au regard de la configuration entière) éventuellement
+\end{itemize}
+
+\end{frame}
+
+
diff --git a/doc/eole-report/proposal/tiramisu.tex b/doc/eole-report/proposal/tiramisu.tex
new file mode 100644 (file)
index 0000000..eb01619
--- /dev/null
@@ -0,0 +1,47 @@
+%%presentation
+\documentclass{beamer}
+\usepackage{beamerthemetree}
+%%impression
+%\documentclass[a4paper,9pt]{extarticle}
+%\usepackage{beamerarticle}
+%%
+
+% class FR
+\usepackage[T1]{fontenc}
+\usepackage[utf8]{inputenc}
+\usepackage[frenchb]{babel}
+
+% image
+\usepackage{graphicx}
+% code 
+%\usepackage{listings}
+%\lstset{language=python, 
+%        caption=Descriptive Caption Text, 
+%        label=DescriptiveLabel,
+%        tabsize=2,
+%        frame=tb,
+%        basicstyle=\small,
+%        }
+\usepackage{alltt}
+\usecolortheme{crane}
+\beamertemplatetransparentcovered
+
+% le logo
+%\logo{\includegraphics[height=1cm]{ban.png}}
+
+\title{Présentation de Tiramisu}
+\subtitle{gestionnaire de configuration}
+
+\author{REMOND Gwenaël}
+\institute{Cadoles}
+\date{\today}
+
+\begin{document}
+\frame{\titlepage}
+
+\include{definition}
+\include{comparaison}
+\include{statut}
+
+\end{document}
+
diff --git a/doc/epydoc.sh b/doc/epydoc.sh
new file mode 100755 (executable)
index 0000000..a5bcf22
--- /dev/null
@@ -0,0 +1,3 @@
+epydoc --css grayscale -o ./pydoc ../config.py ../option.py
+#apirst2html.py --stylesheet=docutils.css --external-api=epydoc --external-api-root=epydoc:./api/ --external-api-file=epydoc:./api/api-objects.txt  doc.txt > doc.htm
+
diff --git a/doc/gaspacho.txt b/doc/gaspacho.txt
new file mode 100644 (file)
index 0000000..457e3e9
--- /dev/null
@@ -0,0 +1,70 @@
+- abstract values from `gaspacho`
+  
+    Les types possibles :
+
+    - sans valeur : `boolean`
+    - avec valeur : `unicode` (un texte libre), `integer` (un chiffre), `enum` (une liste de choix prédéfinies) et `list` (une liste de choix libres).
+
+    Les types sans valeurs sont les plus simples. Par exemple cette règle n’attend
+    aucune valeur particulière Vérifier que Firefox est le navigateur par défaut.
+
+    Alors que celle-ci attend une adresse IP Configuration du serveur proxy manuelle.
+
+    Il existe un autre type (multi) qui permet de mêler plusieurs types.
+
+    Il s’agit bien de définir ici le type de la règle (et uniquement de la règle).
+
+- configuration levels in `creole`
+
+  *thu, 28 april 2011*
+
+    Exemple de niveau de configuration (dans l'ordre) :
+
+    1. - Coeur
+
+    2. 
+        - Coeur
+        - gen_config
+
+    3.
+        - Coeur
+        - gen_config
+        - EAD
+
+    4.
+        - Coeur
+        - EAD
+
+    5.
+        - Coeur
+        - baculaconfig.py
+
+    (`fill` : calcule une valeur jusqu'à ce que l'utilisateur change la
+    valeur)
+
+    Gestion des ACL en écriture :
+
+    Le coeur charge les variables
+
+    - si auto : seul le coeur peut la modifier (cas 1) ;
+    - si fill : le coeur calcule une valeur tant que pas configuré par
+      l'utilisateur. L'utilisateur peut modifier (cas 2 ou 3) ;
+    - des variables modifiables que par gen_config (cas 2) ;
+
+    - des variables modifiables par gen_config ou l'EAD (cas 3) ;
+
+    - des variables d'autres applications (cas 4 et 5).
+
+    Gestion des ACLs en lecture :
+
+    - seule une application peut lire certaines variables (exemple un mot de
+      passe).
+
+
+
+
+
+
+
diff --git a/doc/getting-started.txt b/doc/getting-started.txt
new file mode 100644 (file)
index 0000000..c72a671
--- /dev/null
@@ -0,0 +1,68 @@
+==================================
+`Tiramisu` - Getting Started 
+==================================
+
+What is Configuration handling ?
+================================= 
+
+Due to more and more available configuration options required to set up 
+an operating system, it became quite annoying to hand the necessary 
+options to where they are actually used and even more annoying to add 
+new options. To circumvent these problems the configuration management 
+was introduced.
+
+What is Tiramisu ?
+=================== 
+
+Tiramisu is yet another configuration handler, wich aims at producing 
+flexible and fast configuration options access. The main advantages are 
+its access :ref:`glossary#rules` and the fact that the configuration 's 
+consistency is preserved at any time, see :ref:`glossary#consistency`.
+
+There are type and structures's validations for configuration options, 
+and validations towards the whole configuration.
+
+Last but not least, configuration options can be reached and changed 
+according to the access rules from nearly everywhere in the OS boxes, 
+e.g. the containers via the `http/json` server.
+
+Just the facts 
+============== 
+
+.. _gettingtiramisu: 
+
+Download
+---------
+
+To obtain a copy of the sources, check it out from the repository using 
+`git`. We suggest using `git` if one wants to access the current development.
+
+::
+
+    git clone ssh://gitosis@git.cadol.es:2222/tiramisu.git
+
+This will get you a fresh checkout of the code repository in a local 
+directory named ``tiramisu``.
+
+Understanding Tiramisu's architecture
+--------------------------------------
+
+The :ref:`glossary#schema` is loaded from an XML file, and the values of 
+the configuration options are recovered from a `.ini` like file.
+
+By now, all the in-depth informations about the configuration are stored 
+in a **single** object, the :api:`config.Config()` object, wich is 
+responsible of nearly everything. All the necessary options are stored 
+into a configuration object, which is available nearly everywhere, so 
+that adding new options becomes trivial.
+
+This `Config()` is available from everywhere with the help of an http server
+that serves configuration datas as `json` strings (take a look at the server
+here: :api:`server`). 
+
+.. figure:: architecture.png
+   
+   The basics of Tiramisu's architecture.
+   Once loaded, http server serves the :api:`config.Config()` object, that is,
+   the configuration options and the configuration groups. 
+
diff --git a/doc/glossary.txt b/doc/glossary.txt
new file mode 100644 (file)
index 0000000..06f0616
--- /dev/null
@@ -0,0 +1,94 @@
+.. default-role:: literal
+
+glossary
+==========
+
+.. _configuration:
+
+**configuration**
+
+    Global configuration object, wich contains the whole configuration 
+    options *and* their descriptions (option types and group)
+
+.. _`option description`:
+.. _`schema`:
+
+**schema**:
+**option description**
+
+    see :api:`option.OptionDescription`, see :ref:`optionapi#schema`
+
+    The schema of a configuration : 
+    
+    - the option types
+
+    - how they are organised in groups or even subgroups, that's why we 
+      call them **groups** too.
+
+.. _`configoption`:
+
+**configuration option**
+
+    An option object wich has a name and a value and can be accessed 
+    from the configuration object
+
+.. _`defaultvalue`:
+
+**default value**
+
+    Default value of a configuration option. The default value can be 
+    set at instanciation time, or even at any moment. Remember that if 
+    you reset the default value, the owner reset to `default`
+
+.. _`rules`:
+
+**acces rules**
+    
+    Access rules are : :api:`config.Config.cfgimpl_read_write()` or 
+    :api:`config.Config.cfgimpl_read_only()`, see :doc:`status` 
+
+**freeze**
+
+    A whole configuration can be frozen (used in read only access). See 
+    :doc:`status` for details.
+
+.. _`valueowner`: 
+
+**value owner** 
+    
+    When an option is modified, including at the instanciation, we 
+    always know who has modified it. It's the owner of the option, see 
+    :doc:`status` for more details.
+
+**hidden option**
+
+    a hidden option has a different behaviour on regards to the access 
+    of the value in the configuration, see :doc:`status` for more details.
+
+**disabled option**
+
+    FIXME
+
+**fill option**
+
+    FIXME
+    
+**auto option**
+
+    FIXME
+
+.. _mandatory:
+
+**mandatory option**
+
+    A mandatory option is a configuration option wich value has to be 
+    set, that is the default value cannot be `None`, see 
+    :ref:`optionapi#optioninit`
+    
+    
+.. _consistency:
+
+**consistency**
+
+    Preserve the consistency in a whole configuration is a tricky thing,
+    tiramisu takes care of it for you, see :doc:`consistency` for details.
diff --git a/doc/index.txt b/doc/index.txt
new file mode 100644 (file)
index 0000000..a6e812a
--- /dev/null
@@ -0,0 +1,37 @@
+.. default-role:: literal
+
+.. meta::
+
+   :description: configuration management
+   :keywords: config, configuration
+   
+.. title:: tiramisu
+
+.. |version| replace:: 0.1
+
+The tasting of `Tiramisu`
+=========================
+
+.. image:: tiramisu.jpeg
+   :height: 150px
+   
+`Tiramisu`
+
+    is a cool, refreshing Italian dessert,
+
+    it is also a configuration management tool.
+
+
+It's a pretty small, local (that is, straight on the operating system) 
+configuration handler.
+
+- :doc:`getting-started`: where to go from here,
+- :doc:`config` explains the good praticies of configuration handling,
+- :doc:`configapi` and :doc:`optionapi` describe the API's details,
+- :doc:`status` for a summary of the `Option`'s and `Config`'s statuses,
+- :doc:`consistency` for the local and global integrity constraints,
+- :doc:`glossary` describes the specific terms used in Tiramisu.
+
+
+
+
diff --git a/doc/optionapi.txt b/doc/optionapi.txt
new file mode 100644 (file)
index 0000000..c3c534a
--- /dev/null
@@ -0,0 +1,127 @@
+.. default-role:: literal
+
+Options API Details
+=====================
+
+:module: :api:`option.py`
+
+.. _schema:
+
+Description of Options
+----------------------
+
+All the constructors take a ``name`` and a ``doc`` argument as first 
+arguments to give the option or option group a name and to document it. 
+Most constructors take a ``default`` argument that specifies the default 
+value of the option. If this argument is not supplied the default value 
+is assumed to be ``None``. 
+
+Appart from that, the `Option` object is not supposed to contain any 
+other value than the `tainted` attribute, which is explained later. The 
+container of the value is in the `Config` object.
+
+``OptionDescription``
++++++++++++++++++++++
+
+This class is used to group suboptions.
+
+    ``__init__(self, name, doc, children)``
+        ``children`` is a list of option descriptions (including
+        ``OptionDescription`` instances for nested namespaces).
+
+    ``set_group_type(self, group_name)``
+        Three available group_types : `default`, `family`, `group` and 
+        `master` (for master~slave group type). Notice that for a 
+        master~slave group, the name of the group and the name of the 
+        master option are identical.
+
+`Options description` objects lives in the `_cfgimpl_descr` config attribute. 
+
+If you need to access an option object, you can do it with the OptionDescription
+object. Not only the value of the option by attribute access, but the option
+object itself that lives behind the scene. It can always be accessed internally
+with the `_cfgimpl_descr` attribute of the `config` objects. For example, with a
+option named `name` in a `gc` group the `name` object can be accessed like
+this::
+
+    conf._cfgimpl_descr.name
+    
+of sub configs with ::
+
+    conf.gc._cfgimpl_descr.name
+
+This is a binding. The option objects are in the `_children` config's attribute.
+
+Why accessing an option object ? It is possible for example freeze the
+configuration option 
+
+::
+
+    conf.gc._cfgimpl_descr.dummy.freeze()
+
+or to hide it, or disable it, or... anything.
+
+.. _optioninit:
+
+generic option ``__init__`` method:
+
+    ``__init__(name, doc, default=None, requires=None, multi=False, mandatory=False)``
+
+    :``default``: specifies the default value of the option. 
+    :``requires``: is a list of names of options located anywhere in the configuration.
+    :``multi``: means the value can be a list.
+    :``mandatory``: see :ref:`glossary#mandatory`.
+
+.. _optiontype:
+
+``BoolOption``
+++++++++++++++
+
+Represents a choice between ``True`` and ``False``. 
+
+``IntOption``
++++++++++++++
+
+Represents a choice of an integer.
+
+``FloatOption``
++++++++++++++++
+
+Represents a choice of a floating point number.
+
+``StrOption``
++++++++++++++
+
+Represents the choice of a string.
+
+``SymLinkOption``
+++++++++++++++++++
+
+Redirects to another configuration option in the configuration, that is :
+
+- retrieves the value of the tagert,
+- can set the value of the target too.
+
+    ``__init__(self, name, path)``
+    
+        `path` is the path to the target, the option 
+
+``IPOption``
++++++++++++++
+
+Represents the choice of an ip.
+
+``NetmaskOption``
++++++++++++++++++++
+
+Represents the choice of a netmask.
+
+``ChoiceOption``
+++++++++++++++++
+
+Represents a choice out of several objects. The option can also have the value
+``None``.
+
+    ``__init__(self, name, doc, values, default=None, requires=None)``
+        ``values`` is a list of values the option can possibly take.
+
diff --git a/doc/pydoc/Makefile b/doc/pydoc/Makefile
new file mode 100644 (file)
index 0000000..612cb8f
--- /dev/null
@@ -0,0 +1,5 @@
+.PHONY: clean
+.SUFFIXES:
+
+clean:
+       rm -f *.html
diff --git a/doc/pydoc/crarr.png b/doc/pydoc/crarr.png
new file mode 100644 (file)
index 0000000..26b43c5
Binary files /dev/null and b/doc/pydoc/crarr.png differ
diff --git a/doc/pydoc/epydoc.css b/doc/pydoc/epydoc.css
new file mode 100644 (file)
index 0000000..1c00695
--- /dev/null
@@ -0,0 +1,322 @@
+
+
+/* Epydoc CSS Stylesheet
+ *
+ * This stylesheet can be used to customize the appearance of epydoc's
+ * HTML output.
+ *
+ */
+
+/* Default Colors & Styles
+ *   - Set the default foreground & background color with 'body'; and 
+ *     link colors with 'a:link' and 'a:visited'.
+ *   - Use bold for decision list terms.
+ *   - The heading styles defined here are used for headings *within*
+ *     docstring descriptions.  All headings used by epydoc itself use
+ *     either class='epydoc' or class='toc' (CSS styles for both
+ *     defined below).
+ */
+body                        { background: #ffffff; color: #000000; }
+p                           { margin-top: 0.5em; margin-bottom: 0.5em; }
+a:link                      { color: #000000; }
+a:visited                   { color: #404040; }
+dt                          { font-weight: bold; }
+h1                          { font-size: +140%; font-style: italic;
+                              font-weight: bold; }
+h2                          { font-size: +125%; font-style: italic;
+                              font-weight: bold; }
+h3                          { font-size: +110%; font-style: italic;
+                              font-weight: normal; }
+code                        { font-size: 100%; }
+/* N.B.: class, not pseudoclass */
+a.link                      { font-family: monospace; }
+/* Page Header & Footer
+ *   - The standard page header consists of a navigation bar (with
+ *     pointers to standard pages such as 'home' and 'trees'); a
+ *     breadcrumbs list, which can be used to navigate to containing
+ *     classes or modules; options links, to show/hide private
+ *     variables and to show/hide frames; and a page title (using
+ *     <h1>).  The page title may be followed by a link to the
+ *     corresponding source code (using 'span.codelink').
+ *   - The footer consists of a navigation bar, a timestamp, and a
+ *     pointer to epydoc's homepage.
+ */ 
+h1.epydoc                   { margin: 0; font-size: +140%; font-weight: bold; }
+h2.epydoc                   { font-size: +130%; font-weight: bold; }
+h3.epydoc                   { font-size: +115%; font-weight: bold;
+                              margin-top: 0.2em; }
+td h3.epydoc                { font-size: +115%; font-weight: bold;
+                              margin-bottom: 0; }
+table.navbar                { background: #c0c0c0; color: #000000;
+                              border: 2px groove #d0d0d0; }
+table.navbar table          { color: #000000; }
+th.navbar-select            { background: #b0b0b0;
+                              color: #000000; } 
+table.navbar a              { text-decoration: none; }  
+table.navbar a:link         { color: #000000; }
+table.navbar a:visited      { color: #404040; }
+span.breadcrumbs            { font-size: 85%; font-weight: bold; }
+span.options                { font-size: 70%; }
+span.codelink               { font-size: 85%; }
+td.footer                   { font-size: 85%; }
+
+/* Table Headers
+ *   - Each summary table and details section begins with a 'header'
+ *     row.  This row contains a section title (marked by
+ *     'span.table-header') as well as a show/hide private link
+ *     (marked by 'span.options', defined above).
+ *   - Summary tables that contain user-defined groups mark those
+ *     groups using 'group header' rows.
+ */
+td.table-header             { background: #b0b0b0; color: #000000;
+                              border: 1px solid #808080; }
+td.table-header table       { color: #000000; }
+td.table-header table a:link      { color: #000000; }
+td.table-header table a:visited   { color: #404040; }
+span.table-header           { font-size: 120%; font-weight: bold; }
+th.group-header             { background: #e0e0e0; color: #000000;
+                              text-align: left; font-style: italic; 
+                              font-size: 115%; 
+                              border: 1px solid #808080; }
+
+/* Summary Tables (functions, variables, etc)
+ *   - Each object is described by a single row of the table with
+ *     two cells.  The left cell gives the object's type, and is
+ *     marked with 'code.summary-type'.  The right cell gives the
+ *     object's name and a summary description.
+ *   - CSS styles for the table's header and group headers are
+ *     defined above, under 'Table Headers'
+ */
+table.summary               { border-collapse: collapse;
+                              background: #f0f0f0; color: #000000;
+                              border: 1px solid #808080;
+                              margin-bottom: 0.5em; }
+td.summary                  { border: 1px solid #808080; }
+code.summary-type           { font-size: 85%; }
+table.summary a:link        { color: #000000; }
+table.summary a:visited     { color: #404040; }
+
+
+/* Details Tables (functions, variables, etc)
+ *   - Each object is described in its own div.
+ *   - A single-row summary table w/ table-header is used as
+ *     a header for each details section (CSS style for table-header
+ *     is defined above, under 'Table Headers').
+ */
+table.details               { border-collapse: collapse;
+                              background: #f0f0f0; color: #000000;
+                              border: 1px solid #808080;
+                              margin: .2em 0 0 0; }
+table.details table         { color: #000000; }
+table.details a:link        { color: #000000; }
+table.details a:visited     { color: #404040; }
+
+/* Fields */
+dl.fields                   { margin-left: 2em; margin-top: 1em;
+                              margin-bottom: 1em; }
+dl.fields dd ul             { margin-left: 0em; padding-left: 0em; }
+dl.fields dd ul li ul       { margin-left: 2em; padding-left: 0em; }
+div.fields                  { margin-left: 2em; }
+div.fields p                { margin-bottom: 0.5em; }
+
+/* Index tables (identifier index, term index, etc)
+ *   - link-index is used for indices containing lists of links
+ *     (namely, the identifier index & term index).
+ *   - index-where is used in link indices for the text indicating
+ *     the container/source for each link.
+ *   - metadata-index is used for indices containing metadata
+ *     extracted from fields (namely, the bug index & todo index).
+ */
+table.link-index            { border-collapse: collapse;
+                              background: #f0f0f0; color: #000000;
+                              border: 1px solid #808080; }
+td.link-index               { border-width: 0px; }
+table.link-index a:link     { color: #000000; }
+table.link-index a:visited  { color: #404040; }
+span.index-where            { font-size: 70%; }
+table.metadata-index        { border-collapse: collapse;
+                              background: #f0f0f0; color: #000000;
+                              border: 1px solid #808080; 
+                              margin: .2em 0 0 0; }
+td.metadata-index           { border-width: 1px; border-style: solid; }
+table.metadata-index a:link { color: #000000; }
+table.metadata-index a:visited  { color: #404040; }
+
+/* Function signatures
+ *   - sig* is used for the signature in the details section.
+ *   - .summary-sig* is used for the signature in the summary 
+ *     table, and when listing property accessor functions.
+ * */
+.sig-name                   { color: #606060; }
+.sig-arg                    { color: #808080; }
+.sig-default                { color: #202020; }
+.summary-sig                { font-family: monospace; }
+.summary-sig-name           { color: #606060; font-weight: bold; }
+table.summary a.summary-sig-name:link
+                            { color: #606060; font-weight: bold; }
+table.summary a.summary-sig-name:visited
+                            { color: #606060; font-weight: bold; }
+.summary-sig-arg            { color: #606060; }
+.summary-sig-default        { color: #181818; }
+
+/* Subclass list
+ */
+ul.subclass-list { display: inline; }
+ul.subclass-list li { display: inline; }
+
+/* To render variables, classes etc. like functions */
+table.summary .summary-name { color: #606060; font-weight: bold;
+                              font-family: monospace; }
+table.summary
+     a.summary-name:link    { color: #606060; font-weight: bold;
+                              font-family: monospace; }
+table.summary
+    a.summary-name:visited  { color: #606060; font-weight: bold;
+                              font-family: monospace; }
+
+/* Variable values
+ *   - In the 'variable details' sections, each varaible's value is
+ *     listed in a 'pre.variable' box.  The width of this box is
+ *     restricted to 80 chars; if the value's repr is longer than
+ *     this it will be wrapped, using a backslash marked with
+ *     class 'variable-linewrap'.  If the value's repr is longer
+ *     than 3 lines, the rest will be ellided; and an ellipsis
+ *     marker ('...' marked with 'variable-ellipsis') will be used.
+ *   - If the value is a string, its quote marks will be marked
+ *     with 'variable-quote'.
+ *   - If the variable is a regexp, it is syntax-highlighted using
+ *     the re* CSS classes.
+ */
+pre.variable                { padding: .5em; margin: 0;
+                              background: #e4e4e4; color: #000000;
+                              border: 1px solid #888888; }
+.variable-linewrap          { color: #404040; font-weight: bold; }
+.variable-ellipsis          { color: #404040; font-weight: bold; }
+.variable-quote             { color: #404040; font-weight: bold; }
+.variable-group             { color: #808080; font-weight: bold; }
+.variable-op                { color: #404040; font-weight: bold; }
+.variable-string            { color: #606060; }
+.variable-unknown           { color: #000000; font-weight: bold; }
+.re                         { color: #000000; }
+.re-char                    { color: #606060; }
+.re-op                      { color: #000000; }
+.re-group                   { color: #303030; }
+.re-ref                     { color: #404040; }
+
+/* Base tree
+ *   - Used by class pages to display the base class hierarchy.
+ */
+pre.base-tree               { font-size: 80%; margin: 0; }
+
+/* Frames-based table of contents headers
+ *   - Consists of two frames: one for selecting modules; and
+ *     the other listing the contents of the selected module.
+ *   - h1.toc is used for each frame's heading
+ *   - h2.toc is used for subheadings within each frame.
+ */
+h1.toc                      { text-align: center; font-size: 105%;
+                              margin: 0; font-weight: bold;
+                              padding: 0; }
+h2.toc                      { font-size: 100%; font-weight: bold; 
+                              margin: 0.5em 0 0 -0.3em; }
+
+/* Syntax Highlighting for Source Code
+ *   - doctest examples are displayed in a 'pre.py-doctest' block.
+ *     If the example is in a details table entry, then it will use
+ *     the colors specified by the 'table pre.py-doctest' line.
+ *   - Source code listings are displayed in a 'pre.py-src' block.
+ *     Each line is marked with 'span.py-line' (used to draw a line
+ *     down the left margin, separating the code from the line
+ *     numbers).  Line numbers are displayed with 'span.py-lineno'.
+ *     The expand/collapse block toggle button is displayed with
+ *     'a.py-toggle' (Note: the CSS style for 'a.py-toggle' should not
+ *     modify the font size of the text.)
+ *   - If a source code page is opened with an anchor, then the
+ *     corresponding code block will be highlighted.  The code
+ *     block's header is highlighted with 'py-highlight-hdr'; and
+ *     the code block's body is highlighted with 'py-highlight'.
+ *   - The remaining py-* classes are used to perform syntax
+ *     highlighting (py-string for string literals, py-name for names,
+ *     etc.)
+ */
+pre.py-doctest              { padding: .5em; margin: 1em;
+                              background: #f0f0f0; color: #000000;
+                              border: 1px solid #888888; }
+table pre.py-doctest        { background: #e4e4e4;
+                              color: #000000; }
+pre.py-src                  { border: 2px solid #000000; 
+                              background: #f0f0f0; color: #000000; }
+.py-line                    { border-left: 2px solid #000000; 
+                              margin-left: .2em; padding-left: .4em; }
+.py-lineno                  { font-style: italic; font-size: 90%;
+                              padding-left: .5em; }
+a.py-toggle                 { text-decoration: none; }
+div.py-highlight-hdr        { border-top: 2px solid #000000;
+                              border-bottom: 2px solid #000000;
+                              background: #e8e8e8; }
+div.py-highlight            { border-bottom: 2px solid #000000;
+                              background: #e0e0e0; }
+.py-prompt                  { color: #505050; font-weight: bold;}
+.py-more                    { color: #505050; font-weight: bold;}
+.py-string                  { color: #606060; }
+.py-comment                 { color: #303030; }
+.py-keyword                 { color: #000000; }
+.py-output                  { color: #404040; }
+.py-name                    { color: #000000; }
+.py-name:link               { color: #000000 !important; }
+.py-name:visited            { color: #000000 !important; }
+.py-number                  { color: #505050; }
+.py-defname                 { color: #000000; font-weight: bold; }
+.py-def-name                { color: #000000; font-weight: bold; }
+.py-base-class              { color: #000000; }
+.py-param                   { color: #000000; }
+.py-docstring               { color: #606060; }
+.py-decorator               { color: #404040; }
+/* Use this if you don't want links to names underlined: */
+/*a.py-name                   { text-decoration: none; }*/
+
+/* Graphs & Diagrams
+ *   - These CSS styles are used for graphs & diagrams generated using
+ *     Graphviz dot.  'img.graph-without-title' is used for bare
+ *     diagrams (to remove the border created by making the image
+ *     clickable).
+ */
+img.graph-without-title     { border: none; }
+img.graph-with-title        { border: 1px solid #000000; }
+span.graph-title            { font-weight: bold; }
+span.graph-caption          { }
+
+/* General-purpose classes
+ *   - 'p.indent-wrapped-lines' defines a paragraph whose first line
+ *     is not indented, but whose subsequent lines are.
+ *   - The 'nomargin-top' class is used to remove the top margin (e.g.
+ *     from lists).  The 'nomargin' class is used to remove both the
+ *     top and bottom margin (but not the left or right margin --
+ *     for lists, that would cause the bullets to disappear.)
+ */
+p.indent-wrapped-lines      { padding: 0 0 0 7em; text-indent: -7em; 
+                              margin: 0; }
+.nomargin-top               { margin-top: 0; }
+.nomargin                   { margin-top: 0; margin-bottom: 0; }
+
+/* HTML Log */
+div.log-block               { padding: 0; margin: .5em 0 .5em 0;
+                              background: #f0f0f0; color: #000000;
+                              border: 1px solid #000000; }
+div.log-error               { padding: .1em .3em .1em .3em; margin: 4px;
+                              background: #b0b0b0; color: #000000;
+                              border: 1px solid #000000; }
+div.log-warning             { padding: .1em .3em .1em .3em; margin: 4px;
+                              background: #ffffff; color: #000000;
+                              border: 1px solid #000000; }
+div.log-info               { padding: .1em .3em .1em .3em; margin: 4px;
+                              background: #ffffff; color: #000000;
+                              border: 1px solid #000000; }
+h2.log-hdr                  { background: #b0b0b0; color: #000000;
+                              margin: 0; padding: 0em 0.5em 0em 0.5em;
+                              border-bottom: 1px solid #000000; font-size: 110%; }
+p.log                       { font-weight: bold; margin: .5em 0 .5em 0; }
+tr.opt-changed              { color: #000000; font-weight: bold; }
+tr.opt-default              { color: #606060; }
+pre.log                     { margin: 0; padding: 0; padding-left: 1em; }
diff --git a/doc/pydoc/epydoc.js b/doc/pydoc/epydoc.js
new file mode 100644 (file)
index 0000000..e787dbc
--- /dev/null
@@ -0,0 +1,293 @@
+function toggle_private() {
+        // Search for any private/public links on this page.  Store
+        // their old text in "cmd," so we will know what action to
+        // take; and change their text to the opposite action.
+        var cmd = "?";
+        var elts = document.getElementsByTagName("a");
+        for(var i=0; i<elts.length; i++) {
+          if (elts[i].className == "privatelink") {
+            cmd = elts[i].innerHTML;
+            elts[i].innerHTML = ((cmd && cmd.substr(0,4)=="show")?
+                                    "hide&nbsp;private":"show&nbsp;private");
+          }
+        }
+        // Update all DIVs containing private objects.
+        var elts = document.getElementsByTagName("div");
+        for(var i=0; i<elts.length; i++) {
+          if (elts[i].className == "private") {
+            elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"block");
+          }
+          else if (elts[i].className == "public") {
+            elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"block":"none");
+          }
+        }
+        // Update all table rows containing private objects.  Note, we
+        // use "" instead of "block" becaue IE & firefox disagree on what
+        // this should be (block vs table-row), and "" just gives the
+        // default for both browsers.
+        var elts = document.getElementsByTagName("tr");
+        for(var i=0; i<elts.length; i++) {
+          if (elts[i].className == "private") {
+            elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"");
+          }
+        }
+        // Update all list items containing private objects.
+        var elts = document.getElementsByTagName("li");
+        for(var i=0; i<elts.length; i++) {
+          if (elts[i].className == "private") {
+            elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?
+                                        "none":"");
+          }
+        }
+        // Update all list items containing private objects.
+        var elts = document.getElementsByTagName("ul");
+        for(var i=0; i<elts.length; i++) {
+          if (elts[i].className == "private") {
+            elts[i].style.display = ((cmd && cmd.substr(0,4)=="hide")?"none":"block");
+          }
+        }
+        // Set a cookie to remember the current option.
+        document.cookie = "EpydocPrivate="+cmd;
+      }
+function show_private() {
+        var elts = document.getElementsByTagName("a");
+        for(var i=0; i<elts.length; i++) {
+          if (elts[i].className == "privatelink") {
+            cmd = elts[i].innerHTML;
+            if (cmd && cmd.substr(0,4)=="show")
+                toggle_private();
+          }
+        }
+      }
+function getCookie(name) {
+        var dc = document.cookie;
+        var prefix = name + "=";
+        var begin = dc.indexOf("; " + prefix);
+        if (begin == -1) {
+          begin = dc.indexOf(prefix);
+          if (begin != 0) return null;
+        } else
+        { begin += 2; }
+        var end = document.cookie.indexOf(";", begin);
+        if (end == -1)
+        { end = dc.length; }
+        return unescape(dc.substring(begin + prefix.length, end));
+      }
+function setFrame(url1, url2) {
+          parent.frames[1].location.href = url1;
+          parent.frames[2].location.href = url2;
+      }
+function checkCookie() {
+        var cmd=getCookie("EpydocPrivate");
+        if (cmd && cmd.substr(0,4)!="show" && location.href.indexOf("#_") < 0)
+            toggle_private();
+      }
+function toggleCallGraph(id) {
+        var elt = document.getElementById(id);
+        if (elt.style.display == "none")
+            elt.style.display = "block";
+        else
+            elt.style.display = "none";
+      }
+function expand(id) {
+  var elt = document.getElementById(id+"-expanded");
+  if (elt) elt.style.display = "block";
+  var elt = document.getElementById(id+"-expanded-linenums");
+  if (elt) elt.style.display = "block";
+  var elt = document.getElementById(id+"-collapsed");
+  if (elt) { elt.innerHTML = ""; elt.style.display = "none"; }
+  var elt = document.getElementById(id+"-collapsed-linenums");
+  if (elt) { elt.innerHTML = ""; elt.style.display = "none"; }
+  var elt = document.getElementById(id+"-toggle");
+  if (elt) { elt.innerHTML = "-"; }
+}
+
+function collapse(id) {
+  var elt = document.getElementById(id+"-expanded");
+  if (elt) elt.style.display = "none";
+  var elt = document.getElementById(id+"-expanded-linenums");
+  if (elt) elt.style.display = "none";
+  var elt = document.getElementById(id+"-collapsed-linenums");
+  if (elt) { elt.innerHTML = "<br />"; elt.style.display="block"; }
+  var elt = document.getElementById(id+"-toggle");
+  if (elt) { elt.innerHTML = "+"; }
+  var elt = document.getElementById(id+"-collapsed");
+  if (elt) {
+    elt.style.display = "block";
+    
+    var indent = elt.getAttribute("indent");
+    var pad = elt.getAttribute("pad");
+    var s = "<tt class='py-lineno'>";
+    for (var i=0; i<pad.length; i++) { s += "&nbsp;" }
+    s += "</tt>";
+    s += "&nbsp;&nbsp;<tt class='py-line'>";
+    for (var i=0; i<indent.length; i++) { s += "&nbsp;" }
+    s += "<a href='#' onclick='expand(\"" + id;
+    s += "\");return false'>...</a></tt><br />";
+    elt.innerHTML = s;
+  }
+}
+
+function toggle(id) {
+  elt = document.getElementById(id+"-toggle");
+  if (elt.innerHTML == "-")
+      collapse(id); 
+  else
+      expand(id);
+  return false;
+}
+
+function highlight(id) {
+  var elt = document.getElementById(id+"-def");
+  if (elt) elt.className = "py-highlight-hdr";
+  var elt = document.getElementById(id+"-expanded");
+  if (elt) elt.className = "py-highlight";
+  var elt = document.getElementById(id+"-collapsed");
+  if (elt) elt.className = "py-highlight";
+}
+
+function num_lines(s) {
+  var n = 1;
+  var pos = s.indexOf("\n");
+  while ( pos > 0) {
+    n += 1;
+    pos = s.indexOf("\n", pos+1);
+  }
+  return n;
+}
+
+// Collapse all blocks that mave more than `min_lines` lines.
+function collapse_all(min_lines) {
+  var elts = document.getElementsByTagName("div");
+  for (var i=0; i<elts.length; i++) {
+    var elt = elts[i];
+    var split = elt.id.indexOf("-");
+    if (split > 0)
+      if (elt.id.substring(split, elt.id.length) == "-expanded")
+        if (num_lines(elt.innerHTML) > min_lines)
+          collapse(elt.id.substring(0, split));
+  }
+}
+
+function expandto(href) {
+  var start = href.indexOf("#")+1;
+  if (start != 0 && start != href.length) {
+    if (href.substring(start, href.length) != "-") {
+      collapse_all(4);
+      pos = href.indexOf(".", start);
+      while (pos != -1) {
+        var id = href.substring(start, pos);
+        expand(id);
+        pos = href.indexOf(".", pos+1);
+      }
+      var id = href.substring(start, href.length);
+      expand(id);
+      highlight(id);
+    }
+  }
+}
+
+function kill_doclink(id) {
+  var parent = document.getElementById(id);
+  parent.removeChild(parent.childNodes.item(0));
+}
+function auto_kill_doclink(ev) {
+  if (!ev) var ev = window.event;
+  if (!this.contains(ev.toElement)) {
+    var parent = document.getElementById(this.parentID);
+    parent.removeChild(parent.childNodes.item(0));
+  }
+}
+
+function doclink(id, name, targets_id) {
+  var elt = document.getElementById(id);
+
+  // If we already opened the box, then destroy it.
+  // (This case should never occur, but leave it in just in case.)
+  if (elt.childNodes.length > 1) {
+    elt.removeChild(elt.childNodes.item(0));
+  }
+  else {
+    // The outer box: relative + inline positioning.
+    var box1 = document.createElement("div");
+    box1.style.position = "relative";
+    box1.style.display = "inline";
+    box1.style.top = 0;
+    box1.style.left = 0;
+  
+    // A shadow for fun
+    var shadow = document.createElement("div");
+    shadow.style.position = "absolute";
+    shadow.style.left = "-1.3em";
+    shadow.style.top = "-1.3em";
+    shadow.style.background = "#404040";
+    
+    // The inner box: absolute positioning.
+    var box2 = document.createElement("div");
+    box2.style.position = "relative";
+    box2.style.border = "1px solid #a0a0a0";
+    box2.style.left = "-.2em";
+    box2.style.top = "-.2em";
+    box2.style.background = "white";
+    box2.style.padding = ".3em .4em .3em .4em";
+    box2.style.fontStyle = "normal";
+    box2.onmouseout=auto_kill_doclink;
+    box2.parentID = id;
+
+    // Get the targets
+    var targets_elt = document.getElementById(targets_id);
+    var targets = targets_elt.getAttribute("targets");
+    var links = "";
+    target_list = targets.split(",");
+    for (var i=0; i<target_list.length; i++) {
+        var target = target_list[i].split("=");
+        links += "<li><a href='" + target[1] + 
+               "' style='text-decoration:none'>" +
+               target[0] + "</a></li>";
+    }
+  
+    // Put it all together.
+    elt.insertBefore(box1, elt.childNodes.item(0));
+    //box1.appendChild(box2);
+    box1.appendChild(shadow);
+    shadow.appendChild(box2);
+    box2.innerHTML =
+        "Which <b>"+name+"</b> do you want to see documentation for?" +
+        "<ul style='margin-bottom: 0;'>" +
+        links + 
+        "<li><a href='#' style='text-decoration:none' " +
+        "onclick='kill_doclink(\""+id+"\");return false;'>"+
+        "<i>None of the above</i></a></li></ul>";
+  }
+  return false;
+}
+
+function get_anchor() {
+          var href = location.href;
+          var start = href.indexOf("#")+1;
+          if ((start != 0) && (start != href.length))
+              return href.substring(start, href.length);
+      }
+function redirect_url(dottedName) {
+          // Scan through each element of the "pages" list, and check
+          // if "name" matches with any of them.
+          for (var i=0; i<pages.length; i++) {
+
+              // Each page has the form "<pagename>-m" or "<pagename>-c";
+              // extract the <pagename> portion & compare it to dottedName.
+              var pagename = pages[i].substring(0, pages[i].length-2);
+              if (pagename == dottedName.substring(0,pagename.length)) {
+
+                  // We've found a page that matches `dottedName`;
+                  // construct its URL, using leftover `dottedName`
+                  // content to form an anchor.
+                  var pagetype = pages[i].charAt(pages[i].length-1);
+                  var url = pagename + ((pagetype=="m")?"-module.html":
+                                                        "-class.html");
+                  if (dottedName.length > pagename.length)
+                      url += "#" + dottedName.substring(pagename.length+1,
+                                                        dottedName.length);
+                  return url;
+              }
+          }
+      }
diff --git a/doc/rst2html.py b/doc/rst2html.py
new file mode 100755 (executable)
index 0000000..3e356f3
--- /dev/null
@@ -0,0 +1,82 @@
+#!/usr/bin/python
+# unproudly borrowed from David Goodger's rst2html.py
+
+"""
+A minimal front end to the Docutils Publisher, producing HTML.
+"""
+
+try:
+    import locale
+    locale.setlocale(locale.LC_ALL, '')
+except:
+    pass
+
+from docutils.core import publish_cmdline, default_description
+# ____________________________________________________________
+from docutils import nodes, utils
+from docutils.parsers.rst import roles
+
+"""
+description of the new roles:
+
+`:api:` : link to the code
+
+- code.py becomes api/code.html
+- code.Code.code_test becomes api/code.Code.code_test.html
+- code.Code() becomes api/code.Code.html
+
+`:doc:`a link to an internal file
+example become example.html
+
+ref: link with anchor as in an external file 
+
+:ref:`toto#titi` becomes toto.html#titi 
+"""
+from os.path import splitext
+
+def api_reference_role(role, rawtext, text, lineno, inliner,
+                    options={}, content=[]):
+    basename = text
+    if "(" in text:
+        basename = text.split("(")[0]
+    if ".py" in text:
+        basename = splitext(text)[0]                
+    if "test_" in text:
+        refuri = "api/" + "tiramisu.test." + basename + '.html'
+    else:
+        refuri = "api/" + "tiramisu." + basename + '.html'
+    roles.set_classes(options)
+    node = nodes.reference(rawtext, utils.unescape(text), refuri=refuri,
+                        **options)
+    return [node], []
+
+roles.register_local_role('api', api_reference_role)
+
+def doc_reference_role(role, rawtext, text, lineno, inliner,
+                    options={}, content=[]):
+    refuri = text + '.html'
+    roles.set_classes(options)
+    node = nodes.reference(rawtext, utils.unescape(text), refuri=refuri,
+                        **options)
+    return [node], []
+
+roles.register_local_role('doc', doc_reference_role)
+
+def ref_reference_role(role, rawtext, text, lineno, inliner,
+                    options={}, content=[]):
+    fname, anchor = text.split('#')
+    refuri = fname + '.html#' + anchor 
+    roles.set_classes(options)
+    node = nodes.reference(rawtext, utils.unescape(anchor), refuri=refuri,
+                        **options)
+    return [node], []
+
+roles.register_local_role('ref', ref_reference_role)
+
+# ____________________________________________________________
+
+description = ('Generates (X)HTML documents from standalone reStructuredText '
+               'sources.  ' + default_description)
+
+publish_cmdline(writer_name='html', description=description)
+
diff --git a/doc/status.txt b/doc/status.txt
new file mode 100644 (file)
index 0000000..67e015e
--- /dev/null
@@ -0,0 +1,181 @@
+.. default-role:: literal
+
+Configuration status
+======================
+
+:module: :api:`config.py`
+:tests: - :api:`test_option_owner.py`
+        - :api:`test_option_type.py` 
+        - :api:`test_option_default.py`
+
+Available configuration statuses 
+----------------------------------
+
+These configuration statuses corresponds to specific global attributes : 
+
+**read write status**
+
+    The configuration can be accessed by `__get__` and `__set__`
+    properties, except for the `hidden` configuration options but, yes, it is
+    possible to modify a disabled option.
+
+    To enable read-write status, call
+    :api:`config.Config.cfgimpl_read_write()`
+    
+**read only status**
+
+    The whole configuration is `frozen`, that is modifiying a value is 
+    forbidden. We can access to a configuration option only with the 
+    `__getattr__` property.
+    
+    The configuration has not an access to the hidden options
+    but can read the disabled options.  
+
+    To enable read only status, call :api:`config.Config.cfgimpl_read_only()`
+
+.. csv-table:: **Configuration's statuses summary**
+   :header: " ", "Hidden", "Disabled"
+
+    "read only status", `False`, `True`
+    "read-write status", `True`, `False`
+
+Freezing a configuration
+---------------------------
+
+It is possible to *freeze* a single `Option` object with 
+:api:`option.Option.freeze()`. If you try to modify a frozen option, it 
+raises a `TypeError: trying to change a frozen option object`.
+
+At the configuration level, :api:`config.Config.cfgimpl_freeze()` freeze 
+the whole configuration options.
+
+- :api:`test_option_type.test_freeze_one_option()`
+- :api:`test_option_type.test_frozen_value()`
+- :api:`test_option_type.test_freeze()`
+
+
+Restricted access to an `Option()`
+-----------------------------------
+
+Configuration options access statuses are defined at configuration level 
+that corresponds to theses :api:`option.Option()`'s attribute: 
+
+**hidden**
+
+    This means that an option raises an `HiddenOptionError` if we try to access 
+    the value of the option. 
+
+    See `hide()` or `show()` in `Option()` that comes from 
+    :api:`option.HiddenBaseType`
+
+corresponding convenience API provided:
+
+    `hide()`:
+        set the `hidden` attribute to `True`
+
+    `show()`:
+        set the `hidden` attribute to `False`
+
+**disabled**
+
+    This means that an option *doesn't exists* (doesn't say anything 
+    much more thant an `AttibuteAccess` error)
+
+    See in :api:`option.DisabledBaseType` the origins of 
+    `Option.enable()` or `Option.disable()`
+
+corresponding convenience API provided:
+
+    `disable()`:
+        set the `disabled` attribute to `True`
+
+    `enable()`:
+        set the `disabled` attribute to `False`
+
+mode 
+
+    a mode is `normal` or `expert`, just a category of `Option()` or 
+    group wich determines if an option is easy to choose or not, 
+    available methods are:
+
+    `get_mode()`:
+        returns the current mode
+        
+    `set_mode(mode)`:
+        sets a new mode
+
+    see it in :api:`option.ModeBaseType`
+
+Value owners
+-------------
+
+Every configuration option has a **owner**. When the option is 
+instanciated, the owner is `default` because a default value has been 
+set (including `None`, take a look at the tests).
+
+The `value_owner` is the man who did it. Yes, the man who changed the value of the
+configuration option. 
+
+- At the instance of the `Config` object, the value owner is `default` because
+  the default values are set at the instance of the configuration option object,
+
+::
+    
+    # let's expect there is an option named 'name'
+    config = Config(descr, bool=False)
+    # the override method has been called
+    config._cfgimpl_value_owners['name'] == 'default'
+
+- at the modification of an option, the owner is `default_owner`, (which is `user`)
+
+::
+
+    # modification of the value by attribute access
+    config.gc.dummy = True
+    assert config.gc._cfgimpl_value_owners['dummy'] == 'user'
+    assert config._cfgimpl_values['gc']._cfgimpl_value_owners['dummy'] == 'user'
+
+- the default owner can be set with the `set_owner()` method
+
+::
+
+    config.set_owner('spam')
+    config.set(dummy=True)
+    assert config.gc._cfgimpl_value_owners['dummy'] == 'spam'
+    assert config._cfgimpl_values['gc']._cfgimpl_value_owners['dummy'] == 'spam'
+
+Special owners
+---------------
+
+If the owner of a configuration option is `auto` or `fill` the behavior of the
+access of the value changes. In fact, there is nothing in the value. 
+The value comes from somewhere else (typically, it is calculated by the
+operation system). 
+
+**auto**
+
+    This means that it is a calculated value and therefore automatically 
+    protected it cannot be modified by attribute access once the owner 
+    is `auto`.
+    
+    The configuration option is hidden and a fonction in a specific
+    library is called for the computation of the value.
+
+**fill**
+
+    if the configuration option has a default value, the default is
+    returned, otherwise the value is calculated
+
+The default values behavior
+----------------------------
+
+Configuration options have default values that are stored in the 
+`Option()` object itself. Default values, the `default`, can be set in 
+various ways.
+
+.. FIXME : ADD DETAILS HERE
+
+If a default value is modified by overriding it, not only the value of 
+the option resets to the default that is proposed, but the owner is 
+modified too, it is reseted to `default`.
+
diff --git a/doc/todo.txt b/doc/todo.txt
new file mode 100644 (file)
index 0000000..9891062
--- /dev/null
@@ -0,0 +1,95 @@
+:date: 17 avril
+
+- lever une exception parlante (pour l'instant, c'est une "KeyError") 
+  lorsqu'on essaye d'affecter quelque chose
+  à un groupe, genre
+
+::
+
+    cfg = Config(descr)
+    cfg.gc = "uvw"
+
+alors que gc est un groupe
+
+:date: 12 avril
+
+- faire un mode dégradé avec des warnings
+- validations de longueur des maitres/esclaves ailleurs à sortir des requires
+  et à mettre dans des validators
+  
+:date: 3 avril 2012
+
+- hide sur les sous-sous groupe : il faut que ça hide **tout** les sous-groupe
+  récursivement
+  
+groupes `master/slaves`:
+
+    faut-il coder les multi avec des requires, ou bien simplement 
+    un groupe avec comme variable le nom du groupe ?
+
+auto, fill, obligatoire
+
+2012-03-22
+
+    **groupe master**
+        
+        faire une api du genre : `Option().is_master()`
+        pour cela, tester `if self.parent._name == self._name: return True`
+
+- mettre un attribut `auto` aux options de configuration, de manière à 
+  ce qu'elles sachent quelle fonction eos appeler (que ça soit une info 
+  dans l'option ou bien au niveau de la config ?)
+  le fait de détecter un "auto" vient du owner, mais il faut savoir 
+  quelle fonction appeler
+
+A documenter
+-------------
+
+- les variables multiples
+- expliquer les urls du json dans la doc
+- documenter le typage des options descriptions descr_type 
+
+A ajouter
+---------  
+
+Option -> attribut help (en plus de doc)
+          get_help() (à mettre en class Type avec Doc aussi)
+          
+separator -> pas pour l'instant
+
+fill, auto, obligatoire
+
+nouveau type : 
+
+type option (dérivé de ChoiceOPtion) dans lequel il y a des nouvelles valeurs
+possibles (pas de validations) ou plutôt une StringOption qui propose un choix
+de valeurs par défault de type liste.
+
+
+:date: 24 mars
+
+- hide pour les sous-sous config (récursivement) et pas seulement une 
+  seule sous-config (ou bien, quelque chose de réglable)
+
+- validate global : vérifier à l'init de la conf qu'une variable 
+  n'existe pas déjà, etc
+
+:date: 26 janvier
+
+- un attribut eosfunc pour auto + les paramètres à donner à la fonction
+  pareil pour le fill (function et paramètres)
+
+reset
+-------
+
+**à discuter** : ça correspond exactement au override, 
+ou bien au opt.setoption(None, 'default')
+
+**si la valeur par défaut est définie, un __get__ ne pourra jamais 
+renvoyer None.** ce qui est bloquant. Il faut pouvoir revenir à None.
+
+pour supprimer la valeur d'une options (et revenir à la valeur par défault)
+cfg.reset() (supprime _cfgimpl_value[name]) et _cfgimpl_value_owner[name])
+
+reset() 
+
diff --git a/error.py b/error.py
new file mode 100644 (file)
index 0000000..4588f71
--- /dev/null
+++ b/error.py
@@ -0,0 +1,25 @@
+class AmbigousOptionError(Exception):
+    pass
+class NoMatchingOptionFound(AttributeError):
+    pass
+class ConfigError(Exception):
+    pass
+class ConflictConfigError(ConfigError):
+    pass
+class HiddenOptionError(AttributeError):
+    pass
+class DisabledOptionError(AttributeError):
+    pass 
+class NotFoundError(Exception):
+    pass
+class MethodCallError(Exception):
+    pass 
+class RequiresError(Exception):
+    pass    
+class MandatoryError(Exception):
+    pass 
+class SpecialOwnersError(Exception):
+    pass 
+class ModeOptionError(Exception):
+    pass
+    
diff --git a/option.py b/option.py
new file mode 100644 (file)
index 0000000..3af1c45
--- /dev/null
+++ b/option.py
@@ -0,0 +1,482 @@
+# -*- coding: utf-8 -*-
+"pretty small and local configuration management tool"
+from error import (ConfigError, ConflictConfigError, NotFoundError, 
+                                                                  RequiresError)
+available_actions = ['hide', 'show', 'enable', 'disable'] 
+reverse_actions = {'hide': 'show', 'show': 'hide', 
+                   'disable':'enable', 'enable': 'disable'}
+# ____________________________________________________________
+# OptionDescription authorized group_type values
+group_types = ['default', 'family', 'group', 'master']
+# Option and OptionDescription modes
+modes = ['normal', 'expert']
+# ____________________________________________________________
+# interfaces
+class HiddenBaseType(object):
+    hidden = False
+    def hide(self):
+        self.hidden = True
+    def show(self):
+        self.hidden = False
+    def _is_hidden(self):
+        # dangerous method: how an Option can determine its status by itself ? 
+        return self.hidden
+
+class DisabledBaseType(object):
+    disabled = False   
+    def disable(self):
+        self.disabled = True
+    def enable(self):
+        self.disabled = False
+    def _is_disabled(self):
+        return self.disabled
+
+class ModeBaseType(object):
+    mode = 'normal'
+    def get_mode(self):
+        return self.mode
+    def set_mode(self, mode):
+        if mode not in modes:
+            raise TypeError("Unknown mode: {0}".format(mode))
+        self.mode = mode    
+# ____________________________________________________________
+class Option(HiddenBaseType, DisabledBaseType, ModeBaseType):
+    #reminder: an Option object is **not** a container for the value
+    _frozen = False
+    def __init__(self, name, doc, default=None, requires=None, 
+                    mandatory=False, multi=False, callback=None, mode='normal'):
+        self._name = name
+        self.doc = doc
+        self._requires = requires
+        self._mandatory = mandatory
+        self.multi = multi
+        self.callback = callback
+        if mode not in modes:
+            raise ConfigError("mode {0} not available".format(mode))
+        self.mode = mode
+        if default != None:
+            if not self.validate(default):
+                raise ConfigError("invalid default value {0} " 
+                                         "for option {1}".format(default, name))
+        self.default = default
+        
+    def validate(self, value):
+        raise NotImplementedError('abstract base class')
+
+    def getdefault(self):
+        return self.default
+
+    def getdoc(self):
+        return self.doc
+
+    def getcallback(self):
+        return self.callback
+
+    def setowner(self, config, who):
+        name = self._name
+        if self._frozen:
+            raise TypeError("trying to change a frozen option's owner: %s" % name)
+        if who in ['auto', 'fill']: # XXX special_owners to be imported from config
+            if self.callback == None:
+                raise SpecialOwnersError("no callback specified for" 
+                                                      "option {0}".format(name))
+        config._cfgimpl_value_owners[name] = who
+
+    def setoption(self, config, value, who):
+        name = self._name
+        if self._frozen:
+            raise TypeError('trying to change a frozen option object: %s' % name)
+        # we want the possibility to reset everything
+        if who == "default" and value is None:
+            self.default = None
+            return
+        if not self.validate(value):
+            raise ConfigError('invalid value %s for option %s' % (value, name))
+        if who == "default":
+            # changes the default value (and therefore resets the previous value)
+            self.default = value
+        apply_requires(self, config)
+        # FIXME put the validation for the multi somewhere else
+#            # it is a multi **and** it has requires
+#            if self.multi == True:
+#                if type(value) != list:
+#                    raise TypeError("value {0} must be a list".format(value))
+#                if self._requires is not None:
+#                    for reqname in self._requires:
+#                        # FIXME : verify that the slaves are all multi
+#                        #option = getattr(config._cfgimpl_descr, reqname)
+#    #                    if not option.multi == True:
+#    #                        raise ConflictConfigError("an option with requires "
+#    #                         "has to be a list type : {0}".format(name)) 
+#                        if len(config._cfgimpl_values[reqname]) != len(value):
+#                            raise ConflictConfigError("an option with requires "
+#                             "has not the same length of the others " 
+#                             "in the group : {0}".format(reqname)) 
+        config._cfgimpl_previous_values[name] = config._cfgimpl_values[name] 
+        config._cfgimpl_values[name] = value
+
+    def getkey(self, value):
+        return value
+
+    def freeze(self):
+        self._frozen = True
+        return True
+
+    def unfreeze(self):
+        self._frozen = False
+    # ____________________________________________________________
+    def is_multi(self):
+        return self.multi
+
+    def is_mandatory(self):
+        return self._mandatory
+        
+class ChoiceOption(Option):
+    opt_type = 'string'
+    
+    def __init__(self, name, doc, values, default=None, requires=None,
+                                                 multi=False, mandatory=False):
+        self.values = values
+        super(ChoiceOption, self).__init__(name, doc, default=default, 
+                           requires=requires, multi=multi, mandatory=mandatory)
+
+    def setoption(self, config, value, who):
+        name = self._name
+        super(ChoiceOption, self).setoption(config, value, who)
+
+    def validate(self, value):
+        if self.multi == False:
+            return value is None or value in self.values
+        else:
+            for val in value:
+                if not (val is None or val in self.values):
+                    return False
+            return True
+
+class BoolOption(Option):
+    opt_type = 'bool'
+    
+    def __init__(self, *args, **kwargs):
+        super(BoolOption, self).__init__(*args, **kwargs)
+#    def __init__(self, name, doc, default=None, requires=None,
+#                                  validator=None, multi=False, mandatory=False):
+#        super(BoolOption, self).__init__(name, doc, default=default, 
+#                            requires=requires, multi=multi, mandatory=mandatory)
+        #self._validator = validator
+
+    def validate(self, value):
+        if self.multi == False:
+            return isinstance(value, bool)
+        else:
+            try:
+                for val in value:
+                    if not isinstance(val, bool):
+                        return False
+            except Exception:
+                return False
+            return True
+# FIXME config level validator             
+#    def setoption(self, config, value, who):
+#        name = self._name
+#        if value and self._validator is not None:
+#            toplevel = config._cfgimpl_get_toplevel()
+#            self._validator(toplevel)
+#        super(BoolOption, self).setoption(config, value, who)
+
+class IntOption(Option):
+    opt_type = 'int'
+    
+    def __init__(self, *args, **kwargs):
+        super(IntOption, self).__init__(*args, **kwargs)
+
+    def validate(self, value):
+        if self.multi == False:
+            try:
+                int(value)
+            except TypeError:
+                return False
+            return True
+        else:
+            for val in value:
+                try:
+                    int(val)
+                except TypeError:
+                    return False
+            return True
+                            
+    def setoption(self, config, value, who):
+        try:
+            super(IntOption, self).setoption(config, value, who)
+        except TypeError, e:
+            raise ConfigError(*e.args)
+
+class FloatOption(Option):
+    opt_type = 'float'
+
+    def __init__(self, *args, **kwargs):
+        super(FloatOption, self).__init__(*args, **kwargs)
+
+    def validate(self, value):
+        if self.multi == False:
+            try:
+                float(value)
+            except TypeError:
+                return False
+            return True
+        else:
+            for val in value:
+               try:
+                   float(val)
+               except TypeError:
+                   return False
+            return True
+
+    def setoption(self, config, value, who):
+        try:
+            super(FloatOption, self).setoption(config, float(value), who)
+        except TypeError, e:
+            raise ConfigError(*e.args)
+
+class StrOption(Option):
+    opt_type = 'string'
+    
+    def __init__(self, *args, **kwargs):
+        super(StrOption, self).__init__(*args, **kwargs)
+
+    def validate(self, value):
+        if self.multi == False:
+            return isinstance(value, str)
+        else:
+            for val in value:
+                if not isinstance(val, str):
+                    return False
+                else: 
+                    return True
+                                     
+    def setoption(self, config, value, who):
+        try:
+            super(StrOption, self).setoption(config, value, who)
+        except TypeError, e:
+            raise ConfigError(*e.args)
+
+class SymLinkOption(object): #(HiddenBaseType, DisabledBaseType):
+    opt_type = 'symlink'
+    
+    def __init__(self, name, path):
+        self._name = name
+        self.path = path 
+    
+    def setoption(self, config, value, who):
+        try:
+            setattr(config, self.path, value) # .setoption(self.path, value, who)
+        except TypeError, e:
+            raise ConfigError(*e.args)
+
+class IPOption(Option):
+    opt_type = 'ip'
+    
+    def __init__(self, *args, **kwargs):
+        super(IPOption, self).__init__(*args, **kwargs)
+
+    def validate(self, value):
+        # by now the validation is nothing but a string, use IPy instead
+        if self.multi == False:
+            return isinstance(value, str)
+        else:
+            for val in value:
+                if not isinstance(val, str):
+                    return False
+                else: 
+                    return True
+                                     
+    def setoption(self, config, value, who):
+        try:
+            super(IPOption, self).setoption(config, value, who)
+        except TypeError, e:
+            raise ConfigError(*e.args)
+
+class NetmaskOption(Option):
+    opt_type = 'netmask'
+    
+    def __init__(self, *args, **kwargs):
+        super(NetmaskOption, self).__init__(*args, **kwargs)
+
+    def validate(self, value):
+        # by now the validation is nothing but a string, use IPy instead
+        if self.multi == False:
+            return isinstance(value, str)
+        else:
+            for val in value:
+                if not isinstance(val, str):
+                    return False
+                else: 
+                    return True
+                                     
+    def setoption(self, config, value, who):
+        try:
+            super(NetmaskOption, self).setoption(config, value, who)
+        except TypeError, e:
+            raise ConfigError(*e.args)
+
+class ArbitraryOption(Option):
+    def __init__(self, name, doc, default=None, defaultfactory=None, 
+                                   requires=None, multi=False, mandatory=False):
+        super(ArbitraryOption, self).__init__(name, doc, requires=requires,
+                                               multi=multi, mandatory=mandatory)
+        self.defaultfactory = defaultfactory
+        if defaultfactory is not None:
+            assert default is None
+
+    def validate(self, value):
+        return True
+
+    def getdefault(self):
+        if self.defaultfactory is not None:
+            return self.defaultfactory()
+        return self.default
+
+class OptionDescription(HiddenBaseType, DisabledBaseType, ModeBaseType):
+    group_type = 'default'
+    
+    def __init__(self, name, doc, children, requires=None):
+        self._name = name
+        self.doc = doc
+        self._children = children
+        self._requires = requires
+        self._build()
+    
+    def getdoc(self):
+        return self.doc
+
+    def _build(self):
+        for child in self._children:
+            setattr(self, child._name, child)
+
+    def add_child(self, child):
+        "dynamically adds a configuration option"
+        #Nothing is static. Even the Mona Lisa is falling apart.
+        for ch in self._children:
+            if isinstance(ch, Option):
+                if child._name == ch._name:
+                    raise ConflictConfigError("existing option : {0}".format(
+                                                                   child._name))
+        self._children.append(child)
+        setattr(self, child._name, child)
+    
+    def update_child(self, child):
+        "modification of an existing option"
+        # XXX : corresponds to the `redefine`, is it usefull 
+        pass
+        
+    def getkey(self, config):
+        return tuple([child.getkey(getattr(config, child._name))
+                      for child in self._children])
+
+    def getpaths(self, include_groups=False, currpath=None):
+        """returns a list of all paths in self, recursively
+           currpath should not be provided (helps with recursion)
+        """
+        if currpath is None:
+            currpath = []
+        paths = []
+        for option in self._children:
+            attr = option._name
+            if attr.startswith('_cfgimpl'):
+                continue
+            value = getattr(self, attr)
+            if isinstance(value, OptionDescription):
+                if include_groups:
+                    paths.append('.'.join(currpath + [attr]))
+                currpath.append(attr)
+                paths += value.getpaths(include_groups=include_groups,
+                                        currpath=currpath)
+                currpath.pop()
+            else:
+                paths.append('.'.join(currpath + [attr]))
+        return paths
+    # ____________________________________________________________
+
+    def set_group_type(self, group_type):
+        if group_type in group_types:
+            self.group_type = group_type
+        else:
+            raise ConfigError('not allowed value for group_type : {0}'.format(
+                              group_type))
+    
+    def get_group_type(self):
+        return self.group_type
+    # ____________________________________________________________
+    def hide(self):
+        super(OptionDescription, self).hide()
+        # FIXME : AND THE SUBCHILDREN ? 
+        for child in self._children:
+            if isinstance(child, OptionDescription):
+                child.hide()
+    
+    def show(self):
+        # FIXME : AND THE SUBCHILDREN ?? 
+        super(OptionDescription, self).show()
+        for child in self._children:
+            if isinstance(child, OptionDescription):
+                child.show()
+    # ____________________________________________________________
+    def disable(self):
+        super(OptionDescription, self).disable()
+        # FIXME : AND THE SUBCHILDREN ? 
+        for child in self._children:
+            if isinstance(child, OptionDescription):
+                child.disable()
+    
+    def enable(self):
+        # FIXME : AND THE SUBCHILDREN ? 
+        super(OptionDescription, self).enable()
+        for child in self._children:
+            if isinstance(child, OptionDescription):
+                child.enable()
+# ____________________________________________________________
+def apply_requires(opt, config):
+    if hasattr(opt, '_requires'):
+        if opt._requires is not None:
+            # malformed requirements
+            rootconfig = config._cfgimpl_get_toplevel()
+            for req in opt._requires:
+                if not type(req) == tuple and len(req) in (3, 4):
+                    raise RequiresError("malformed requirements for option:"
+                                                   " {0}".format(opt._name))
+            # all actions **must** be identical
+            actions = [req[2] for req in opt._requires]
+            action = actions[0]
+            for act in actions:
+                if act != action:
+                    raise RequiresError("malformed requirements for option:"
+                                                   " {0}".format(opt._name))
+            # filters the callbacks
+            matches = False
+            for req in opt._requires:
+                if len(req) == 3:
+                    name, expected, action = req
+                    inverted = False
+                if len(req) == 4:
+                    name, expected, action, inverted = req
+                    if inverted == 'inverted':
+                        inverted = True
+                homeconfig, shortname = \
+                                      rootconfig._cfgimpl_get_home_by_path(name)
+                # FIXME: doesn't work with 'auto' or 'fill' yet 
+                # (copy the code from the __getattr__
+                if shortname in homeconfig._cfgimpl_values:
+                    value = homeconfig._cfgimpl_values[shortname]
+                    if (not inverted and value == expected) or \
+                            (inverted and value != expected):
+                        if action not in available_actions:
+                            raise RequiresError("malformed requirements"
+                                           " for option: {0}".format(opt._name))
+                        getattr(opt, action)() #.hide() or show() or...
+                        matches = True
+                else: # option doesn't exist ! should not happen...
+                    raise NotFoundError("required option not found: "
+                                                             "{0}".format(name))
+            # no callback has been triggered, then just reverse the action
+            if not matches:
+                getattr(opt, reverse_actions[action])()
+                
diff --git a/report/Makefile b/report/Makefile
new file mode 100644 (file)
index 0000000..760f9bb
--- /dev/null
@@ -0,0 +1,15 @@
+.SUFFIXES:
+
+.PHONY: all clean
+
+all: html
+
+generate:
+       python ./generate.py
+                       
+html: generate
+       make -C ./build all
+
+clean: 
+       make -C ./build clean
+       
diff --git a/report/__init__.py b/report/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/report/build/Makefile b/report/build/Makefile
new file mode 100644 (file)
index 0000000..e6b01ad
--- /dev/null
@@ -0,0 +1,18 @@
+SRC=$(wildcard *.txt)
+HTMLFRAGMENT=$(addsuffix .html, $(basename $(SRC)))
+
+.SUFFIXES:
+
+.PHONY: all clean
+
+all: html
+
+html: $(HTMLFRAGMENT)
+
+%.html: %.txt    
+       ./rst2html.py --stylesheet ./style.css $< > $@
+
+clean:
+       rm -f *.html
+       rm -f *.txt
+       
diff --git a/report/build/basic.css b/report/build/basic.css
new file mode 100644 (file)
index 0000000..f0379f3
--- /dev/null
@@ -0,0 +1,540 @@
+/*
+ * basic.css
+ * ~~~~~~~~~
+ *
+ * Sphinx stylesheet -- basic theme.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+/* -- main layout ----------------------------------------------------------- */
+
+div.clearer {
+    clear: both;
+}
+
+/* -- relbar ---------------------------------------------------------------- */
+
+div.related {
+    width: 100%;
+    font-size: 90%;
+}
+
+div.related h3 {
+    display: none;
+}
+
+div.related ul {
+    margin: 0;
+    padding: 0 0 0 10px;
+    list-style: none;
+}
+
+div.related li {
+    display: inline;
+}
+
+div.related li.right {
+    float: right;
+    margin-right: 5px;
+}
+
+/* -- sidebar --------------------------------------------------------------- */
+
+div.sphinxsidebarwrapper {
+    padding: 10px 5px 0 10px;
+}
+
+div.sphinxsidebar {
+    float: left;
+    width: 230px;
+    margin-left: -100%;
+    font-size: 90%;
+}
+
+div.sphinxsidebar ul {
+    list-style: none;
+}
+
+div.sphinxsidebar ul ul,
+div.sphinxsidebar ul.want-points {
+    margin-left: 20px;
+    list-style: square;
+}
+
+div.sphinxsidebar ul ul {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+div.sphinxsidebar form {
+    margin-top: 10px;
+}
+
+div.sphinxsidebar input {
+    border: 1px solid #98dbcc;
+    font-family: sans-serif;
+    font-size: 1em;
+}
+
+div.sphinxsidebar input[type="text"] {
+    width: 170px;
+}
+
+div.sphinxsidebar input[type="submit"] {
+    width: 30px;
+}
+
+img {
+    border: 0;
+}
+
+/* -- search page ----------------------------------------------------------- */
+
+ul.search {
+    margin: 10px 0 0 20px;
+    padding: 0;
+}
+
+ul.search li {
+    padding: 5px 0 5px 20px;
+    background-image: url(file.png);
+    background-repeat: no-repeat;
+    background-position: 0 7px;
+}
+
+ul.search li a {
+    font-weight: bold;
+}
+
+ul.search li div.context {
+    color: #888;
+    margin: 2px 0 0 30px;
+    text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+    font-weight: bold;
+}
+
+/* -- index page ------------------------------------------------------------ */
+
+table.contentstable {
+    width: 90%;
+}
+
+table.contentstable p.biglink {
+    line-height: 150%;
+}
+
+a.biglink {
+    font-size: 1.3em;
+}
+
+span.linkdescr {
+    font-style: italic;
+    padding-top: 5px;
+    font-size: 90%;
+}
+
+/* -- general index --------------------------------------------------------- */
+
+table.indextable {
+    width: 100%;
+}
+
+table.indextable td {
+    text-align: left;
+    vertical-align: top;
+}
+
+table.indextable dl, table.indextable dd {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+table.indextable tr.pcap {
+    height: 10px;
+}
+
+table.indextable tr.cap {
+    margin-top: 10px;
+    background-color: #f2f2f2;
+}
+
+img.toggler {
+    margin-right: 3px;
+    margin-top: 3px;
+    cursor: pointer;
+}
+
+div.modindex-jumpbox {
+    border-top: 1px solid #ddd;
+    border-bottom: 1px solid #ddd;
+    margin: 1em 0 1em 0;
+    padding: 0.4em;
+}
+
+div.genindex-jumpbox {
+    border-top: 1px solid #ddd;
+    border-bottom: 1px solid #ddd;
+    margin: 1em 0 1em 0;
+    padding: 0.4em;
+}
+
+/* -- general body styles --------------------------------------------------- */
+
+a.headerlink {
+    visibility: hidden;
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink {
+    visibility: visible;
+}
+
+div.body p.caption {
+    text-align: inherit;
+}
+
+div.body td {
+    text-align: left;
+}
+
+.field-list ul {
+    padding-left: 1em;
+}
+
+.first {
+    margin-top: 0 !important;
+}
+
+p.rubric {
+    margin-top: 30px;
+    font-weight: bold;
+}
+
+img.align-left, .figure.align-left, object.align-left {
+    clear: left;
+    float: left;
+    margin-right: 1em;
+}
+
+img.align-right, .figure.align-right, object.align-right {
+    clear: right;
+    float: right;
+    margin-left: 1em;
+}
+
+img.align-center, .figure.align-center, object.align-center {
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.align-left {
+    text-align: left;
+}
+
+.align-center {
+    text-align: center;
+}
+
+.align-right {
+    text-align: right;
+}
+
+/* -- sidebars -------------------------------------------------------------- */
+
+div.sidebar {
+    margin: 0 0 0.5em 1em;
+    border: 1px solid #ddb;
+    padding: 7px 7px 0 7px;
+    background-color: #ffe;
+    width: 40%;
+    float: right;
+}
+
+p.sidebar-title {
+    font-weight: bold;
+}
+
+/* -- topics ---------------------------------------------------------------- */
+
+div.topic {
+    border: 1px solid #ccc;
+    padding: 7px 7px 0 7px;
+    margin: 10px 0 10px 0;
+}
+
+p.topic-title {
+    font-size: 1.1em;
+    font-weight: bold;
+    margin-top: 10px;
+}
+
+/* -- admonitions ----------------------------------------------------------- */
+
+div.admonition {
+    margin-top: 10px;
+    margin-bottom: 10px;
+    padding: 7px;
+}
+
+div.admonition dt {
+    font-weight: bold;
+}
+
+div.admonition dl {
+    margin-bottom: 0;
+}
+
+p.admonition-title {
+    margin: 0px 10px 5px 0px;
+    font-weight: bold;
+}
+
+div.body p.centered {
+    text-align: center;
+    margin-top: 25px;
+}
+
+/* -- tables ---------------------------------------------------------------- */
+
+table.docutils {
+    border: 0;
+    border-collapse: collapse;
+}
+
+table.docutils td, table.docutils th {
+    padding: 1px 8px 1px 5px;
+    border-top: 0;
+    border-left: 0;
+    border-right: 0;
+    border-bottom: 1px solid #aaa;
+}
+
+table.field-list td, table.field-list th {
+    border: 0 !important;
+}
+
+table.footnote td, table.footnote th {
+    border: 0 !important;
+}
+
+th {
+    text-align: left;
+    padding-right: 5px;
+}
+
+table.citation {
+    border-left: solid 1px gray;
+    margin-left: 1px;
+}
+
+table.citation td {
+    border-bottom: none;
+}
+
+/* -- other body styles ----------------------------------------------------- */
+
+ol.arabic {
+    list-style: decimal;
+}
+
+ol.loweralpha {
+    list-style: lower-alpha;
+}
+
+ol.upperalpha {
+    list-style: upper-alpha;
+}
+
+ol.lowerroman {
+    list-style: lower-roman;
+}
+
+ol.upperroman {
+    list-style: upper-roman;
+}
+
+dl {
+    margin-bottom: 15px;
+}
+
+dd p {
+    margin-top: 0px;
+}
+
+dd ul, dd table {
+    margin-bottom: 10px;
+}
+
+dd {
+    margin-top: 3px;
+    margin-bottom: 10px;
+    margin-left: 30px;
+}
+
+dt:target, .highlighted {
+    background-color: #fbe54e;
+}
+
+dl.glossary dt {
+    font-weight: bold;
+    font-size: 1.1em;
+}
+
+.field-list ul {
+    margin: 0;
+    padding-left: 1em;
+}
+
+.field-list p {
+    margin: 0;
+}
+
+.refcount {
+    color: #060;
+}
+
+.optional {
+    font-size: 1.3em;
+}
+
+.versionmodified {
+    font-style: italic;
+}
+
+.system-message {
+    background-color: #fda;
+    padding: 5px;
+    border: 3px solid red;
+}
+
+.footnote:target  {
+    background-color: #ffa;
+}
+
+.line-block {
+    display: block;
+    margin-top: 1em;
+    margin-bottom: 1em;
+}
+
+.line-block .line-block {
+    margin-top: 0;
+    margin-bottom: 0;
+    margin-left: 1.5em;
+}
+
+.guilabel, .menuselection {
+    font-family: sans-serif;
+}
+
+.accelerator {
+    text-decoration: underline;
+}
+
+.classifier {
+    font-style: oblique;
+}
+
+abbr, acronym {
+    border-bottom: dotted 1px;
+    cursor: help;
+}
+
+/* -- code displays --------------------------------------------------------- */
+
+pre {
+    overflow: auto;
+    overflow-y: hidden;  /* fixes display issues on Chrome browsers */
+}
+
+td.linenos pre {
+    padding: 5px 0px;
+    border: 0;
+    background-color: transparent;
+    color: #aaa;
+}
+
+table.highlighttable {
+    margin-left: 0.5em;
+}
+
+table.highlighttable td {
+    padding: 0 0.5em 0 0.5em;
+}
+
+tt.descname {
+    background-color: transparent;
+    font-weight: bold;
+    font-size: 1.2em;
+}
+
+tt.descclassname {
+    background-color: transparent;
+}
+
+tt.xref, a tt {
+    background-color: transparent;
+    font-weight: bold;
+}
+
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+    background-color: transparent;
+}
+
+.viewcode-link {
+    float: right;
+}
+
+.viewcode-back {
+    float: right;
+    font-family: sans-serif;
+}
+
+div.viewcode-block:target {
+    margin: -1px -10px;
+    padding: 0 10px;
+}
+
+/* -- math display ---------------------------------------------------------- */
+
+img.math {
+    vertical-align: middle;
+}
+
+div.body div.math p {
+    text-align: center;
+}
+
+span.eqno {
+    float: right;
+}
+
+/* -- printout stylesheet --------------------------------------------------- */
+
+@media print {
+    div.document,
+    div.documentwrapper,
+    div.bodywrapper {
+        margin: 0 !important;
+        width: 100%;
+    }
+
+    div.sphinxsidebar,
+    div.related,
+    div.footer,
+    #top-link {
+        display: none;
+    }
+}
\ No newline at end of file
diff --git a/report/build/rst2html.py b/report/build/rst2html.py
new file mode 100755 (executable)
index 0000000..cfa6769
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+# unproudly borrowed from David Goodger's rst2html.py
+
+""" A minimal front end to the Docutils Publisher, producing HTML with a 
+`config` role 
+"""
+
+try:
+    import locale
+    locale.setlocale(locale.LC_ALL, '')
+except:
+    pass
+
+from docutils.core import publish_cmdline, default_description
+# ____________________________________________________________
+from docutils import nodes, utils
+from docutils.parsers.rst import roles
+
+# ____________________________________________________________
+#register a :config: ReST link role for use in documentation    
+def config_reference_role(role, rawtext, text, lineno, inliner,
+                    options={}, content=[]):
+    basename = text
+    refuri = "report/build" + basename + '.html'
+    roles.set_classes(options)
+    node = nodes.reference(rawtext, utils.unescape(text), refuri=refuri,
+                        **options)
+    return [node], []
+
+roles.register_local_role('config', config_reference_role)
+# ____________________________________________________________
+
+
+description = ('Generates (X)HTML documents from standalone reStructuredText '
+               'sources.  ' + default_description)
+
+publish_cmdline(writer_name='html', description=description)
+
diff --git a/report/build/style.css b/report/build/style.css
new file mode 100644 (file)
index 0000000..1126f9d
--- /dev/null
@@ -0,0 +1,795 @@
+/*
+ * rtd.css
+ * ~~~~~~~~~~~~~~~
+ *
+ * Sphinx stylesheet -- sphinxdoc theme.  Originally created by
+ * Armin Ronacher for Werkzeug.
+ *
+ * Customized for ReadTheDocs by Eric Pierce & Eric Holscher
+ *
+ * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+/* RTD colors
+ * light blue: #e8ecef
+ * medium blue: #8ca1af
+ * dark blue: #465158
+ * dark grey: #444444
+ *
+ * white hover: #d1d9df;
+ * medium blue hover: #697983;
+ * green highlight: #8ecc4c
+ * light blue (project bar): #e8ecef
+ */
+
+@import url("basic.css");
+
+/* PAGE LAYOUT -------------------------------------------------------------- */
+
+body {
+    font: 100%/1.5 "ff-meta-web-pro-1","ff-meta-web-pro-2",Arial,"Helvetica Neue",sans-serif; 
+    text-align: center;
+    color: black;
+    background-color: #465158;
+    padding: 0;
+    margin: 0;
+}
+
+div.document {
+    text-align: left;
+    background-color: #e8ecef;
+}
+
+div.bodywrapper {
+    background-color: #ffffff;
+    border-left: 1px solid #ccc;
+    border-bottom: 1px solid #ccc;
+    margin: 0 0 0 16em;
+}
+
+div.body {
+    margin: 0;
+    padding: 0.5em 1.3em;
+    min-width: 20em;
+}
+
+div.related {
+    font-size: 1em;
+    background-color: #465158;
+}
+
+div.documentwrapper {
+    float: left;
+    width: 100%;
+    background-color: #e8ecef;
+}
+
+
+/* HEADINGS --------------------------------------------------------------- */
+
+h1 {
+    margin: 0;
+    padding: 0.7em 0 0.3em 0;
+    font-size: 1.5em;
+    line-height: 1.15;
+    color: #111;
+    clear: both;
+}
+
+h2 {
+    margin: 2em 0 0.2em 0;
+    font-size: 1.35em;
+    padding: 0;
+    color: #465158;
+}
+
+h3 {
+    margin: 1em 0 -0.3em 0;
+    font-size: 1.2em;
+    color: #6c818f;
+}
+
+div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a {
+    color: black;
+}
+
+h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor {
+    display: none;
+    margin: 0 0 0 0.3em;
+    padding: 0 0.2em 0 0.2em;
+    color: #aaa !important;
+}
+
+h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor,
+h5:hover a.anchor, h6:hover a.anchor {
+    display: inline;
+}
+
+h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover,
+h5 a.anchor:hover, h6 a.anchor:hover {
+    color: #777;
+    background-color: #eee;
+}
+
+
+/* LINKS ------------------------------------------------------------------ */
+
+/* Normal links get a pseudo-underline */
+a {
+    color: #444;
+    text-decoration: none;
+    border-bottom: 1px solid #ccc;
+}
+
+/* Links in sidebar, TOC, index trees and tables have no underline */
+.sphinxsidebar a,
+.toctree-wrapper a,
+.indextable a,
+#indices-and-tables a {
+    color: #444;
+    text-decoration: none;
+    border-bottom: none;
+}
+
+/* Most links get an underline-effect when hovered */
+a:hover,
+div.toctree-wrapper a:hover,
+.indextable a:hover,
+#indices-and-tables a:hover {
+    color: #111;
+    text-decoration: none;
+    border-bottom: 1px solid #111;
+}
+
+/* Footer links */
+div.footer a {
+    color: #86989B;
+    text-decoration: none;
+    border: none;
+}
+div.footer a:hover {
+    color: #a6b8bb;
+    text-decoration: underline;
+    border: none;
+}
+
+/* Permalink anchor (subtle grey with a red hover) */
+div.body a.headerlink {
+    color: #ccc;
+    font-size: 1em;
+    margin-left: 6px;
+    padding: 0 4px 0 4px;
+    text-decoration: none;
+    border: none;
+}
+div.body a.headerlink:hover {
+    color: #c60f0f;
+    border: none;
+}
+
+
+/* NAVIGATION BAR --------------------------------------------------------- */
+
+div.related ul {
+    height: 2.5em;
+}
+
+div.related ul li {
+    margin: 0;
+    padding: 0.65em 0;
+    float: left;
+    display: block;
+    color: white; /* For the >> separators */
+    font-size: 0.8em;
+}
+
+div.related ul li.right {
+    float: right;
+    margin-right: 5px;
+    color: transparent; /* Hide the | separators */
+}
+
+/* "Breadcrumb" links in nav bar */
+div.related ul li a {
+    order: none;
+    background-color: inherit;
+    font-weight: bold;
+    margin: 6px 0 6px 4px;
+    line-height: 1.75em;
+    color: #ffffff;
+    padding: 0.4em 0.8em;
+    border: none;
+    border-radius: 3px;
+}
+/* previous / next / modules / index links look more like buttons */
+div.related ul li.right a {
+    margin: 0.375em 0;
+    background-color: #697983;
+    text-shadow: 0 1px rgba(0, 0, 0, 0.5);
+    border-radius: 3px;
+    -webkit-border-radius: 3px;
+    -moz-border-radius: 3px;
+}
+/* All navbar links light up as buttons when hovered */
+div.related ul li a:hover {
+    background-color: #8ca1af;
+    color: #ffffff;
+    text-decoration: none;
+    border-radius: 3px;
+    -webkit-border-radius: 3px;
+    -moz-border-radius: 3px;
+}
+/* Take extra precautions for tt within links */
+a tt,
+div.related ul li a tt {
+    background: inherit !important;
+    color: inherit !important;
+}
+
+
+/* SIDEBAR ---------------------------------------------------------------- */
+
+div.sphinxsidebarwrapper {
+    padding: 0;
+}
+
+div.sphinxsidebar {
+    margin: 0;
+    margin-left: -100%;
+    float: left;
+    top: 3em;
+    left: 0;
+    padding: 0 1em;
+    width: 14em;
+    font-size: 1em;
+    text-align: left;
+    background-color: #e8ecef;
+}
+
+div.sphinxsidebar img {
+    max-width: 12em;
+}
+
+div.sphinxsidebar h3,
+div.sphinxsidebar h4,
+div.sphinxsidebar p.logo {
+    margin: 1.2em 0 0.3em 0;
+    font-size: 1em;
+    padding: 0;
+    color: #222222;
+    font-family: "ff-meta-web-pro-1", "ff-meta-web-pro-2", "Arial", "Helvetica Neue", sans-serif;
+}
+
+div.sphinxsidebar h3 a {
+    color: #444444;
+}
+
+div.sphinxsidebar ul,
+div.sphinxsidebar p {
+    margin-top: 0;
+    padding-left: 0;
+    line-height: 130%;
+    background-color: #e8ecef;
+}
+
+/* No bullets for nested lists, but a little extra indentation */
+div.sphinxsidebar ul ul {
+    list-style-type: none;
+    margin-left: 1.5em;
+    padding: 0;
+}
+
+/* A little top/bottom padding to prevent adjacent links' borders
+ * from overlapping each other */
+div.sphinxsidebar ul li {
+    padding: 1px 0;
+}
+
+/* A little left-padding to make these align with the ULs */
+div.sphinxsidebar p.topless {
+    padding-left: 0 0 0 1em;
+}
+
+/* Make these into hidden one-liners */
+div.sphinxsidebar ul li,
+div.sphinxsidebar p.topless {
+    white-space: nowrap;
+    overflow: hidden;
+}
+/* ...which become visible when hovered */
+div.sphinxsidebar ul li:hover,
+div.sphinxsidebar p.topless:hover {
+    overflow: visible;
+}
+
+/* Search text box and "Go" button */
+#searchbox {
+    margin-top: 2em;
+    margin-bottom: 1em;
+    background: #ddd;
+    padding: 0.5em;
+    border-radius: 6px;
+    -moz-border-radius: 6px;
+    -webkit-border-radius: 6px;
+}
+#searchbox h3 {
+    margin-top: 0;
+}
+
+/* Make search box and button abut and have a border */
+input,
+div.sphinxsidebar input {
+    border: 1px solid #999;
+    float: left;
+}
+
+/* Search textbox */
+input[type="text"] {
+    margin: 0;
+    padding: 0 3px;
+    height: 20px;
+    width: 144px;
+    border-top-left-radius: 3px;
+    border-bottom-left-radius: 3px;
+    -moz-border-radius-topleft: 3px;
+    -moz-border-radius-bottomleft: 3px;
+    -webkit-border-top-left-radius: 3px;
+    -webkit-border-bottom-left-radius: 3px;
+}
+/* Search button */
+input[type="submit"] {
+    margin: 0 0 0 -1px; /* -1px prevents a double-border with textbox */
+    height: 22px;
+    color: #444;
+    background-color: #e8ecef;
+    padding: 1px 4px;
+    font-weight: bold;
+    border-top-right-radius: 3px;
+    border-bottom-right-radius: 3px;
+    -moz-border-radius-topright: 3px;
+    -moz-border-radius-bottomright: 3px;
+    -webkit-border-top-right-radius: 3px;
+    -webkit-border-bottom-right-radius: 3px;
+}
+input[type="submit"]:hover {
+    color: #ffffff;
+    background-color: #8ecc4c;
+}
+
+div.sphinxsidebar p.searchtip {
+    clear: both;
+    padding: 0.5em 0 0 0;
+    background: #ddd;
+    color: #666;
+    font-size: 0.9em;
+}
+
+/* Sidebar links are unusual */
+div.sphinxsidebar li a,
+div.sphinxsidebar p a {
+    background: #e8ecef; /* In case links overlap main content */
+    border-radius: 3px;
+    -moz-border-radius: 3px;
+    -webkit-border-radius: 3px;
+    border: 1px solid transparent; /* To prevent things jumping around on hover */
+    padding: 0 5px 0 5px;
+}
+div.sphinxsidebar li a:hover,
+div.sphinxsidebar p a:hover {
+    color: #111;
+    text-decoration: none;
+    border: 1px solid #888;
+}
+div.sphinxsidebar p.logo a {
+    border: 0;
+}
+
+/* Tweak any link appearing in a heading */
+div.sphinxsidebar h3 a {
+}
+
+
+
+
+/* OTHER STUFF ------------------------------------------------------------ */
+
+cite, code, tt {
+    font-family: 'Consolas', 'Deja Vu Sans Mono',
+                 'Bitstream Vera Sans Mono', monospace;
+    font-size: 0.95em;
+    letter-spacing: 0.01em;
+}
+
+tt {
+    background-color: #f2f2f2;
+    color: #444;
+}
+
+tt.descname, tt.descclassname, tt.xref {
+    border: 0;
+}
+
+hr {
+    border: 1px solid #abc;
+    margin: 2em;
+}
+
+
+pre, #_fontwidthtest {
+    font-family: 'Consolas', 'Deja Vu Sans Mono',
+                 'Bitstream Vera Sans Mono', monospace;
+    margin: 1em 2em;
+    font-size: 0.95em;
+    letter-spacing: 0.015em;
+    line-height: 120%;
+    padding: 0.5em;
+    border: 1px solid #ccc;
+    background-color: #eee;
+    border-radius: 6px;
+    -moz-border-radius: 6px;
+    -webkit-border-radius: 6px;
+}
+
+pre a {
+    color: inherit;
+    text-decoration: underline;
+}
+
+td.linenos pre {
+    margin: 1em 0em;
+}
+
+td.code pre {
+    margin: 1em 0em;
+}
+
+div.quotebar {
+    background-color: #f8f8f8;
+    max-width: 250px;
+    float: right;
+    padding: 2px 7px;
+    border: 1px solid #ccc;
+}
+
+div.topic {
+    background-color: #f8f8f8;
+}
+
+table {
+    border-collapse: collapse;
+    margin: 0 -0.5em 0 -0.5em;
+}
+
+table td, table th {
+    padding: 0.2em 0.5em 0.2em 0.5em;
+}
+
+
+/* ADMONITIONS AND WARNINGS ------------------------------------------------- */
+
+/* Shared by admonitions, warnings and sidebars */
+div.admonition,
+div.warning,
+div.sidebar {
+    font-size: 0.9em;
+    margin: 2em;
+    padding: 0;
+    /*
+    border-radius: 6px;
+    -moz-border-radius: 6px;
+    -webkit-border-radius: 6px;
+    */
+}
+div.admonition p,
+div.warning p,
+div.sidebar p {
+    margin: 0.5em 1em 0.5em 1em;
+    padding: 0;
+}
+div.admonition pre,
+div.warning pre,
+div.sidebar pre {
+    margin: 0.4em 1em 0.4em 1em;
+}
+div.admonition p.admonition-title,
+div.warning p.admonition-title,
+div.sidebar p.sidebar-title {
+    margin: 0;
+    padding: 0.1em 0 0.1em 0.5em;
+    color: white;
+    font-weight: bold;
+    font-size: 1.1em;
+    text-shadow: 0 1px rgba(0, 0, 0, 0.5);
+}
+div.admonition ul, div.admonition ol,
+div.warning ul, div.warning ol,
+div.sidebar ul, div.sidebar ol {
+    margin: 0.1em 0.5em 0.5em 3em;
+    padding: 0;
+}
+
+
+/* Admonitions and sidebars only */
+div.admonition, div.sidebar {
+    border: 1px solid #609060;
+    background-color: #e9ffe9;
+}
+div.admonition p.admonition-title,
+div.sidebar p.sidebar-title {
+    background-color: #70A070;
+    border-bottom: 1px solid #609060;
+}
+
+
+/* Warnings only */
+div.warning {
+    border: 1px solid #900000;
+    background-color: #ffe9e9;
+}
+div.warning p.admonition-title {
+    background-color: #b04040;
+    border-bottom: 1px solid #900000;
+}
+
+
+/* Sidebars only */
+div.sidebar {
+  max-width: 30%;
+}
+
+
+
+div.versioninfo {
+    margin: 1em 0 0 0;
+    border: 1px solid #ccc;
+    background-color: #DDEAF0;
+    padding: 8px;
+    line-height: 1.3em;
+    font-size: 0.9em;
+}
+
+.viewcode-back {
+    font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
+                 'Verdana', sans-serif;
+}
+
+div.viewcode-block:target {
+    background-color: #f4debf;
+    border-top: 1px solid #ac9;
+    border-bottom: 1px solid #ac9;
+}
+
+dl {
+    margin: 1em 0 2.5em 0;
+}
+
+/* Highlight target when you click an internal link */
+dt:target {
+    background: #ffe080;
+}
+/* Don't highlight whole divs */
+div.highlight {
+    background: transparent;
+}
+/* But do highlight spans (so search results can be highlighted) */
+span.highlight {
+    background: #ffe080;
+}
+
+div.footer {
+    background-color: #465158;
+    color: #eeeeee;
+    padding: 0 2em 2em 2em;
+    clear: both;
+    font-size: 0.8em;
+    text-align: center;
+}
+
+p {
+    margin: 0.8em 0 0.5em 0;
+}
+
+.section p img.math {
+    margin: 0;
+}
+
+
+.section p img {
+    margin: 1em 2em;
+}
+
+
+/* MOBILE LAYOUT -------------------------------------------------------------- */
+
+@media screen and (max-width: 600px) {
+    
+    h1, h2, h3, h4, h5 {
+        position: relative;
+    }
+
+    ul {
+        padding-left: 1.25em;
+    }
+
+    div.bodywrapper a.headerlink, #indices-and-tables h1 a {
+        color: #e6e6e6;
+        font-size: 80%;
+        float: right;
+        line-height: 1.8;
+        position: absolute;
+        right: -0.7em;
+        visibility: inherit;
+    }
+
+    div.bodywrapper h1 a.headerlink, #indices-and-tables h1 a {
+        line-height: 1.5;
+    }
+
+    pre {
+        font-size: 0.7em;
+        overflow: auto;
+        word-wrap: break-word;
+        white-space: pre-wrap;
+    }
+
+    div.related ul {
+        height: 2.5em;
+        padding: 0;
+        text-align: left;
+    }
+
+    div.related ul li {
+        clear: both;
+        color: #465158;
+        padding: 0.2em 0;
+    }
+
+    div.related ul li:last-child {
+        border-bottom: 1px dotted #8ca1af;
+        padding-bottom: 0.4em;
+        margin-bottom: 1em;
+        width: 100%;
+    }
+
+    div.related ul li a {
+        color: #465158;
+        padding-right: 0;
+    }
+
+    div.related ul li a:hover {
+        background: inherit;
+        color: inherit;
+    }
+
+    div.related ul li.right {
+        clear: none;
+        padding: 0.65em 0;
+        margin-bottom: 0.5em;
+    }
+
+    div.related ul li.right a {
+        color: #fff;
+        padding-right: 0.8em;
+    }
+
+    div.related ul li.right a:hover {
+        background-color: #8ca1af;
+    }
+
+    div.body {
+        clear: both;
+        min-width: 0;
+        word-wrap: break-word;
+    }
+
+    div.bodywrapper {
+        margin: 0 0 0 0;
+    }
+
+    div.sphinxsidebar {
+        float: none;
+        margin: 0;
+        width: auto;
+    }
+
+    div.sphinxsidebar input[type="text"] {
+        height: 2em;
+        line-height: 2em;
+        width: 70%;
+    }
+
+    div.sphinxsidebar input[type="submit"] {
+        height: 2em;
+        margin-left: 0.5em;
+        width: 20%;
+    }
+
+    div.sphinxsidebar p.searchtip {
+        background: inherit;
+        margin-bottom: 1em;
+    }
+
+    div.sphinxsidebar ul li, div.sphinxsidebar p.topless {
+        white-space: normal;
+    }
+
+    .bodywrapper img {
+        display: block;
+        margin-left: auto;
+        margin-right: auto;
+        max-width: 100%;
+    }
+
+    div.documentwrapper {
+        float: none;
+    }
+
+    div.admonition, div.warning, pre, blockquote {
+        margin-left: 0em;
+        margin-right: 0em;
+    }
+
+    .body p img {
+        margin: 0;
+    }
+
+    #searchbox {
+        background: transparent;
+    }
+
+    .related:not(:first-child) li {
+        display: none;
+    }
+
+    .related:not(:first-child) li.right {
+        display: block;
+    }
+
+    div.footer {
+        padding: 1em;
+    }
+
+    .rtd_doc_footer .badge {
+        float: none;
+        margin: 1em auto;
+        position: static;
+    }
+
+    .rtd_doc_footer .badge.revsys-inline {
+        margin-right: auto;
+        margin-bottom: 2em;
+    }
+
+    table.indextable {
+        display: block;
+        width: auto; 
+    }
+
+    .indextable tr {
+        display: block;
+    }
+
+    .indextable td {
+        display: block;
+        padding: 0;
+        width: auto !important;
+    }
+
+    .indextable td dt {
+        margin: 1em 0;
+    }
+
+    ul.search {
+        margin-left: 0.25em;
+    }
+
+    ul.search li div.context {
+        font-size: 90%;
+        line-height: 1.1;
+        margin-bottom: 1;
+        margin-left: 0;
+    }
+
+}
diff --git a/report/generate.py b/report/generate.py
new file mode 100644 (file)
index 0000000..b4112a1
--- /dev/null
@@ -0,0 +1,99 @@
+from os.path import dirname, join
+from rst import Rest, Paragraph, Strong, OrderedListItem, ListItem, Title, Link, Transition
+from rst import Directive, Em, Quote, Text
+from tiramisu.option import *
+from tiramisu.config import *
+#from makerestdoc import *
+
+docdir = join(dirname(__file__), 'build')
+
+def make_rst_file(filename, rstcontent):
+    fh = file(filename, 'w')
+    fh.write(rstcontent.text())
+    fh.close()
+
+def descr_content(path, prefix, descr, root=False):
+    content = Rest()
+    title = Title(abovechar="", belowchar="=")
+    if root:
+        title.join(Text("Configuration's overview for: "), Quote(descr._name))
+    else:
+        title.join(Text("Group's overview for: "), Quote(descr._name))
+    content.add(title)
+    content.add(ListItem().join(Strong("name:"), Text(descr._name)))
+    if not root:
+        content.add(ListItem().join(Strong("path:"), Text(path)))
+    content.add(ListItem().join(Strong("description:"), Text(descr.doc)))
+    if not root:
+        content.add(ListItem().join(Strong("container:"), Text(prefix)))
+    if not root:
+        content.add(ListItem().join(Strong("type:"), Text(descr.group_type)))
+    if not root:
+        content.add(ListItem().join(Strong("requirements:"), Text(str(descr._requires))))
+    content.add(ListItem().join(Strong("is hidden:"), Text(str(descr._is_hidden()))))
+    content.add(ListItem().join(Strong("is disabled:"), Text(str(descr._is_disabled()))))
+    content.add(Transition())
+    content.add(Title(abovechar="", belowchar="-").join(Text("Ordered list of childrens for:"), Text(path)))
+    for opt in descr._children:
+        name = opt._name
+        link = Link(name + ":", join(path + '.' + name + ".html"))
+        # because of SympLink opt
+        if hasattr(opt, 'doc'):
+            doc = opt.doc
+        else:
+            doc = name
+        content.add(OrderedListItem(link, Text(opt.doc)))
+    content.add(Transition())
+    content.add(Paragraph(Link("back to index", "index.html")))
+    make_rst_file(join(docdir, path + '.txt'), content)
+    if root:
+        make_rst_file(join(docdir, 'index.txt'), content)
+    
+def opt_rst_content(path, prefix, descr, value):
+    content = Rest()
+    title = Title(abovechar="", belowchar="=")
+    title.join(Text("Configuration's option overview for: "), Quote(descr._name))
+    content.add(title)
+    content.add(ListItem().join(Strong("name:"), Text(descr._name)))
+    content.add(ListItem().join(Strong("value:"), Text(str(value))))
+    content.add(ListItem().join(Strong("path:"), Text(path)))
+    content.add(ListItem().join(Strong("container:"), Text(prefix)))
+    if isinstance(descr, ChoiceOption):
+        content.add(ListItem().join(Strong("possible values:"), Text(str(descr.values))))
+    if not isinstance(descr, SymLinkOption):
+        content.add(ListItem().join(Strong("type:"), Text(str(descr.opt_type))))
+        content.add(ListItem().join(Strong("default:"), Text(str(descr.getdefault()))))
+        content.add(ListItem().join(Strong("description:"), Text(str(descr.getdoc()))))
+        content.add(ListItem().join(Strong("requirements:"), Text(str(descr._requires))))
+        content.add(ListItem().join(Strong("is hidden:"), Text(str(descr._is_hidden()))))
+        content.add(ListItem().join(Strong("is disabled:"), Text(str(descr._is_disabled()))))
+        content.add(ListItem().join(Strong("is frozen:"), Text(str(descr._frozen))))
+        content.add(ListItem().join(Strong("is multi:"), Text(str(descr.multi))))
+        content.add(ListItem().join(Strong("is mandatory:"), Text(str(descr.is_mandatory()))))
+    else:
+        content.add(ListItem().join(Strong("links to:"), Text(str(descr.path))))
+    content.add(Transition())
+    content.add(Paragraph(Link("back to container", join(prefix + ".html"))))
+    make_rst_file(join(docdir, path + '.txt'), content)
+        
+def make_rest_overview(cfg, title=True):
+    rootname = cfg._cfgimpl_descr._name
+    descr_content(rootname, rootname, cfg._cfgimpl_descr, root=True)
+    #cfg.cfgimpl_read_write()
+    cfg._cfgimpl_disabled = False
+    cfg._cfgimpl_hidden = False
+    for path in cfg.getpaths(include_groups=True, allpaths=True):
+        child = cfg.unwrap_from_path(path)
+        fullpath = rootname + '.' + path 
+        prefix = fullpath.rsplit(".", 1)[0]
+        if isinstance(child, OptionDescription):
+            descr_content(fullpath, prefix, child)
+        else: 
+            value = getattr(cfg, path)
+            opt_rst_content(fullpath, prefix, child, value)
+
+if __name__ == '__main__':
+    from test_config_big_example import get_example_config
+    make_rest_overview(get_example_config())
+# ____________________________________________________________
+
diff --git a/report/makerestdoc.py b/report/makerestdoc.py
new file mode 100644 (file)
index 0000000..3ca042d
--- /dev/null
@@ -0,0 +1,115 @@
+from tiramisu.config import Config
+from tiramisu import option
+# we shall keep extendable types out of the reach of unexceptional guys like us 
+# horror __metaclass__ = extendabletype
+
+def get_fullpath(opt, path):
+    if path:
+        return "%s.%s" % (path, opt._name)
+    else:
+        return opt._name
+
+class Option:
+    def make_rest_doc(self, path=""):
+        fullpath = get_fullpath(self, path)
+        result = Rest(
+            Title(fullpath, abovechar="=", belowchar="="),
+            ListItem(Strong("name:"), self._name),
+            ListItem(Strong("description:"), self.doc))
+        return result
+
+class ChoiceOption(Option, option.ChoiceOption):
+    def make_rest_doc(self, path=""):
+        content = super(ChoiceOption, self).make_rest_doc(path)
+        content.add(ListItem(Strong("option type:"), "choice option"))
+        content.add(ListItem(Strong("possible values:"),
+                             *[ListItem(str(val)) for val in self.values]))
+        if self.default is not None:
+            content.add(ListItem(Strong("default:"), str(self.default)))
+
+#        requirements = []
+#        
+#        for val in self.values:
+#            if val not in self._requires:
+#                continue
+#            req = self._requires[val]
+#            requirements.append(ListItem("value '%s' requires:" % (val, ),
+#                *[ListItem(Link(opt, opt + ".html"),
+#                           "to be set to '%s'" % (rval, ))
+#                      for (opt, rval) in req]))
+#        if requirements:
+#            content.add(ListItem(Strong("requirements:"), *requirements))
+        return content
+
+class BoolOption(Option, option.BoolOption):
+    def make_rest_doc(self, path=""):
+        content = super(BoolOption, self).make_rest_doc(path)
+        fullpath = get_fullpath(self, path)
+        content.add(ListItem(Strong("option type:"), "boolean option"))
+        if self.default is not None:
+            content.add(ListItem(Strong("default:"), str(self.default)))
+#        if self._requires is not None:
+#            requirements = [ListItem(Link(opt, opt + ".html"),
+#                               "must be set to '%s'" % (rval, ))
+#                                for (opt, rval) in self._requires]
+#            if requirements:
+#                content.add(ListItem(Strong("requirements:"), *requirements))
+        return content
+
+class IntOption(Option, option.IntOption):
+    def make_rest_doc(self, path=""):
+        content = super(IntOption, self).make_rest_doc(path)
+        content.add(ListItem(Strong("option type:"), "integer option"))
+        if self.default is not None:
+            content.add(ListItem(Strong("default:"), str(self.default)))
+        return content
+
+class FloatOption(Option, option.FloatOption):
+    def make_rest_doc(self, path=""):
+        content = super(FloatOption, self).make_rest_doc(path)
+        content.add(ListItem(Strong("option type:"), "float option"))
+        if self.default is not None:
+            content.add(ListItem(Strong("default:"), str(self.default)))
+        return content
+
+class StrOption(Option, option.StrOption):
+    def make_rest_doc(self, path=""):
+        content = super(StrOption, self).make_rest_doc(path)
+        content.add(ListItem(Strong("option type:"), "string option"))
+        if self.default is not None:
+            content.add(ListItem(Strong("default:"), str(self.default)))
+        return content
+
+#class ArbitraryOption:
+#    def make_rest_doc(self, path=""):
+#        content = super(ArbitraryOption, self).make_rest_doc(path)
+#        content.add(ListItem(Strong("option type:"),
+#                             "arbitrary option (mostly internal)"))
+#        if self.default is not None:
+#            content.add(ListItem(Strong("default:"), str(self.default)))
+#        elif self.defaultfactory is not None:
+#            content.add(ListItem(Strong("factory for the default value:"),
+#                                 str(self.defaultfactory)))
+#        return content
+
+class OptionDescription(option.OptionDescription):
+    def make_rest_doc(self, path=""):
+        fullpath = get_fullpath(self, path)
+        content = Rest(
+            Title(fullpath, abovechar="=", belowchar="="))
+        toctree = []
+        for child in self._children:
+            subpath = fullpath + "." + child._name
+            toctree.append(subpath)
+        content.add(Directive("toctree", *toctree, **{'maxdepth': 4}))
+        content.join(
+            ListItem(Strong("name:"), self._name),
+            ListItem(Strong("description:"), self.doc))
+        stack = []
+        curr = content
+#        config = Config(self)
+        return content
+
+# ____________________________________________________________
+
+
diff --git a/report/rst.py b/report/rst.py
new file mode 100644 (file)
index 0000000..7548cdd
--- /dev/null
@@ -0,0 +1,410 @@
+# unproudly borrowed from pypy : 
+# http://codespeak.net/svn/pypy/trunk/pypy/tool/rest/rst.py
+""" reStructuredText generation tools
+
+    provides an api to build a tree from nodes, which can be converted to
+    ReStructuredText on demand
+
+    note that not all of ReST is supported, a usable subset is offered, but
+    certain features aren't supported, and also certain details (like how links
+    are generated, or how escaping is done) can not be controlled
+"""
+
+import re
+
+def escape(txt):
+    """escape ReST markup"""
+    if not isinstance(txt, str) and not isinstance(txt, unicode):
+        txt = str(txt)
+    # XXX this takes a very naive approach to escaping, but it seems to be
+    # sufficient...
+    for c in '\\*`|:_':
+        txt = txt.replace(c, '\\%s' % (c,))
+    return txt
+
+class RestError(Exception):
+    """ raised on containment errors (wrong parent) """
+
+class AbstractMetaclass(type):
+    def __new__(cls, *args):
+        obj = super(AbstractMetaclass, cls).__new__(cls, *args)
+        parent_cls = obj.parentclass
+        if parent_cls is None:
+            return obj
+        if not isinstance(parent_cls, list):
+            class_list = [parent_cls]
+        else:
+            class_list = parent_cls
+        if obj.allow_nesting:
+            class_list.append(obj)
+        
+        for _class in class_list:
+            if not _class.allowed_child:
+                _class.allowed_child = {obj:True}
+            else:
+                _class.allowed_child[obj] = True
+        return obj
+
+class AbstractNode(object):
+    """ Base class implementing rest generation
+    """
+    sep = ''
+    __metaclass__ = AbstractMetaclass
+    parentclass = None # this exists to allow parent to know what
+        # children can exist
+    allow_nesting = False
+    allowed_child = {}
+    defaults = {}
+    
+    _reg_whitespace = re.compile('\s+')
+
+    def __init__(self, *args, **kwargs):
+        self.parent = None
+        self.children = []
+        for child in args:
+            self._add(child)
+        for arg in kwargs:
+            setattr(self, arg, kwargs[arg])
+    
+    def join(self, *children):
+        """ add child nodes
+        
+            returns a reference to self
+        """
+        for child in children:
+            self._add(child)
+        return self
+    
+    def add(self, child):
+        """ adds a child node
+            
+            returns a reference to the child
+        """
+        self._add(child)
+        return child
+        
+    def _add(self, child):
+        if child.__class__ not in self.allowed_child:
+            raise RestError("%r cannot be child of %r" % \
+                (child.__class__, self.__class__))
+        self.children.append(child)
+        child.parent = self
+    
+    def __getitem__(self, item):
+        return self.children[item]
+    
+    def __setitem__(self, item, value):
+        self.children[item] = value
+
+    def text(self):
+        """ return a ReST string representation of the node """
+        return self.sep.join([child.text() for child in self.children])
+    
+    def wordlist(self):
+        """ return a list of ReST strings for this node and its children """ 
+        return [self.text()]
+
+class Rest(AbstractNode):
+    """ Root node of a document """
+    
+    sep = "\n\n"
+    def __init__(self, *args, **kwargs):
+        AbstractNode.__init__(self, *args, **kwargs)
+        self.links = {}
+    
+    def render_links(self, check=False):
+        """render the link attachments of the document"""
+        assert not check, "Link checking not implemented"
+        if not self.links:
+            return ""
+        link_texts = []
+        # XXX this could check for duplicates and remove them...
+        for link, target in self.links.iteritems():
+            link_texts.append(".. _`%s`: %s" % (escape(link), target))
+        return "\n" + "\n".join(link_texts) + "\n\n"
+
+    def text(self):
+        outcome = []
+        if (isinstance(self.children[0], Transition) or
+                isinstance(self.children[-1], Transition)):
+            raise ValueError, ('document must not begin or end with a '
+                               'transition')
+        for child in self.children:
+            outcome.append(child.text())
+        
+        # always a trailing newline
+        text = self.sep.join([i for i in outcome if i]) + "\n"
+        return text + self.render_links()
+
+class Transition(AbstractNode):
+    """ a horizontal line """
+    parentclass = Rest
+
+    def __init__(self, char='-', width=80, *args, **kwargs):
+        self.char = char
+        self.width = width
+        super(Transition, self).__init__(*args, **kwargs)
+        
+    def text(self):
+        return (self.width - 1) * self.char
+
+class Paragraph(AbstractNode):
+    """ simple paragraph """
+
+    parentclass = Rest
+    sep = " "
+    indent = ""
+    # FIXME
+    width = 880
+    
+    def __init__(self, *args, **kwargs):
+        # make shortcut
+        args = list(args)
+        for num, arg in enumerate(args):
+            if isinstance(arg, str):
+                args[num] = Text(arg)
+        super(Paragraph, self).__init__(*args, **kwargs)
+    
+    def text(self):
+        texts = []
+        for child in self.children:
+            texts += child.wordlist()
+        
+        buf = []
+        outcome = []
+        lgt = len(self.indent)
+        
+        def grab(buf):
+            outcome.append(self.indent + self.sep.join(buf))
+        
+        texts.reverse()
+        while texts:
+            next = texts[-1]
+            if not next:
+                texts.pop()
+                continue
+            if lgt + len(self.sep) + len(next) <= self.width or not buf:
+                buf.append(next)
+                lgt += len(next) + len(self.sep)
+                texts.pop()
+            else:
+                grab(buf)
+                lgt = len(self.indent)
+                buf = []
+        grab(buf)
+        return "\n".join(outcome)
+    
+class SubParagraph(Paragraph):
+    """ indented sub paragraph """
+
+    indent = " "
+    
+class Title(Paragraph):
+    """ title element """
+
+    parentclass = Rest
+    belowchar = "="
+    abovechar = ""
+    
+    def text(self):
+        txt = self._get_text()
+        lines = []
+        if self.abovechar:
+            lines.append(self.abovechar * len(txt))
+        lines.append(txt)
+        if self.belowchar:
+            lines.append(self.belowchar * len(txt))
+        return "\n".join(lines)
+
+    def _get_text(self):
+        txt = []
+        for node in self.children:
+            txt += node.wordlist()
+        return ' '.join(txt)
+
+class AbstractText(AbstractNode):
+    parentclass = [Paragraph, Title]
+    start = ""
+    end = ""
+    def __init__(self, _text):
+        self._text = _text
+    
+    def text(self):
+        text = self.escape(self._text)
+        return self.start + text + self.end
+
+    def escape(self, text):
+        if not isinstance(text, str) and not isinstance(text, unicode):
+            text = str(text)
+        if self.start:
+            text = text.replace(self.start, '\\%s' % (self.start,))
+        if self.end and self.end != self.start:
+            text = text.replace(self.end, '\\%s' % (self.end,))
+        return text
+    
+class Text(AbstractText):
+    def wordlist(self):
+        text = escape(self._text)
+        return self._reg_whitespace.split(text)
+
+class LiteralBlock(AbstractText):
+    parentclass = Rest
+    start = '::\n\n'
+
+    def text(self):
+        if not self._text.strip():
+            return ''
+        text = self.escape(self._text).split('\n')
+        for i, line in enumerate(text):
+            if line.strip():
+                text[i] = '  %s' % (line,)
+        return self.start + '\n'.join(text)
+
+class Em(AbstractText):
+    start = "*"
+    end = "*"
+
+class Strong(AbstractText):
+    start = "**"
+    end = "**"
+
+class Quote(AbstractText):
+    start = '``'
+    end = '``'
+
+class Anchor(AbstractText):
+    start = '_`'
+    end = '`'
+
+class Footnote(AbstractText):
+    def __init__(self, note, symbol=False):
+        raise NotImplemented('XXX')
+
+class Citation(AbstractText):
+    def __init__(self, text, cite):
+        raise NotImplemented('XXX')
+
+class ListItem(Paragraph):
+    allow_nesting = True
+    item_chars = '*+-'
+    
+    def text(self):
+        idepth = self.get_indent_depth()
+        indent = self.indent + (idepth + 1) * '  '
+        txt = '\n\n'.join(self.render_children(indent))
+        ret = []
+        item_char = self.item_chars[idepth]
+        ret += [indent[len(item_char)+1:], item_char, ' ', txt[len(indent):]]
+        return ''.join(ret)
+    
+    def render_children(self, indent):
+        txt = []
+        buffer = []
+        def render_buffer(fro, to):
+            if not fro:
+                return
+            p = Paragraph(indent=indent, *fro)
+            p.parent = self.parent
+            to.append(p.text())
+        for child in self.children:
+            if isinstance(child, AbstractText):
+                buffer.append(child)
+            else:
+                if buffer:
+                    render_buffer(buffer, txt)
+                    buffer = []
+                txt.append(child.text())
+
+        render_buffer(buffer, txt)
+        return txt
+
+    def get_indent_depth(self):
+        depth = 0
+        current = self
+        while (current.parent is not None and
+                isinstance(current.parent, ListItem)):
+            depth += 1
+            current = current.parent
+        return depth
+
+class OrderedListItem(ListItem):
+    item_chars = ["#."] * 5
+
+class DListItem(ListItem):
+    item_chars = None
+    def __init__(self, term, definition, *args, **kwargs):
+        self.term = term
+        super(DListItem, self).__init__(definition, *args, **kwargs)
+
+    def text(self):
+        idepth = self.get_indent_depth()
+        indent = self.indent + (idepth + 1) * '  '
+        txt = '\n\n'.join(self.render_children(indent))
+        ret = []
+        ret += [indent[2:], self.term, '\n', txt]
+        return ''.join(ret)
+
+class Link(AbstractText):
+    start = '`'
+    end = '`_'
+
+    def __init__(self, _text, target):
+        self._text = _text
+        self.target = target
+        self.rest = None
+    
+    def text(self):
+        if self.rest is None:
+            self.rest = self.find_rest()
+        if self.rest.links.get(self._text, self.target) != self.target:
+            raise ValueError('link name %r already in use for a different '
+                             'target' % (self.target,))
+        self.rest.links[self._text] = self.target
+        return AbstractText.text(self)
+
+    def find_rest(self):
+        # XXX little overkill, but who cares...
+        next = self
+        while next.parent is not None:
+            next = next.parent
+        return next
+
+class InternalLink(AbstractText):
+    start = '`'
+    end = '`_'
+    
+class LinkTarget(Paragraph):
+    def __init__(self, name, target):
+        self.name = name
+        self.target = target
+    
+    def text(self):
+        return ".. _`%s`:%s\n" % (self.name, self.target)
+
+class Substitution(AbstractText):
+    def __init__(self, text, **kwargs):
+        raise NotImplemented('XXX')
+
+class Directive(Paragraph):
+    indent = '   '
+    def __init__(self, name, *args, **options):
+        self.name = name
+        self.content = args
+        super(Directive, self).__init__()
+        self.options = options
+        
+    def text(self):
+        # XXX not very pretty...
+        txt = '.. %s::' % (self.name,)
+        options = '\n'.join(['    :%s: %s' % (k, v) for (k, v) in
+                             self.options.iteritems()])
+        if options:
+            txt += '\n%s' % (options,)
+
+        if self.content:
+            txt += '\n'
+            for item in self.content:
+                txt += '\n    ' + item
+        
+        return txt
+
diff --git a/report/test_config_big_example.py b/report/test_config_big_example.py
new file mode 100644 (file)
index 0000000..3a93273
--- /dev/null
@@ -0,0 +1,27 @@
+# coding: utf-8
+from tiramisu.config import *
+from tiramisu.option import *
+
+all_modules = ['amon', 'sphynx', 'zephir']
+
+gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+gcdummy = BoolOption('dummy', 'dummy', default=False)
+objspaceoption = ChoiceOption('objspace', 'Object space',
+                            ['std', 'thunk'], 'std')
+booloption = BoolOption('bool', 'Test boolean option', default=True)
+intoption = IntOption('int', 'Test int option', default=0)
+floatoption = FloatOption('float', 'Test float option', default=2.3)
+stroption = StrOption('str', 'Test string option', default="abc")
+boolop = BoolOption('boolop', 'Test boolean option op', default=True)
+wantref_option = BoolOption('wantref', 'Test requires', default=False)
+wantframework_option = BoolOption('wantframework', 'Test requires',
+                                  default=False)
+
+gcgroup = OptionDescription('gc', 'doc pour gc', [gcoption, gcdummy, floatoption])
+descr = OptionDescription('essai', 'une éééééé doc pour essai', [gcgroup, booloption, objspaceoption,
+                                       wantref_option, stroption,
+                                       wantframework_option,
+                                       intoption, boolop])
+
+def get_example_config():
+    return Config(descr)
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/autopath.py b/test/autopath.py
new file mode 100644 (file)
index 0000000..bc2ddff
--- /dev/null
@@ -0,0 +1,13 @@
+"""automatically sets the PYTHONPATH before running the unit tests
+
+This is supposed to be used in development mode (i.e. testing from a fresh
+checkout)
+"""
+
+from os.path import dirname, abspath, join, normpath
+import sys
+
+HERE = dirname(abspath(__file__))
+PATH = normpath(join(HERE, '..'))
+if PATH not in sys.path:
+    sys.path.insert(1, PATH)
diff --git a/test/test_config.py b/test/test_config.py
new file mode 100644 (file)
index 0000000..a18cf60
--- /dev/null
@@ -0,0 +1,96 @@
+#this test is much more to test that **it's there** and answers attribute access
+import autopath
+from py.test import raises
+
+from config import *
+from option import *
+
+def make_description():
+    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                ['std', 'thunk'], 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
+    wantref_option = BoolOption('wantref', 'Test requires', default=False)
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False)
+    
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
+    descr = OptionDescription('tiram', '', [gcgroup, booloption, objspaceoption,
+                                           wantref_option, stroption,
+                                           wantframework_option,
+                                           intoption, boolop])
+    return descr
+
+def test_base_config():
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+    descr = OptionDescription('tiramisu', '', [gcdummy])
+    cfg = Config(descr)
+    assert cfg.dummy == False
+    dm = cfg.unwrap_from_path('dummy')
+    assert dm._name == 'dummy'
+    
+def test_base_config_and_groups():
+    descr = make_description()
+    # overrides the booloption default value
+    config = Config(descr, bool=False)
+    assert config.gc.name == 'ref'
+    assert config.bool == False
+    nm = config.unwrap_from_path('gc.name')
+    assert nm._name == 'name'
+    gc = config.unwrap_from_path('gc')
+    assert gc._name == 'gc' 
+    nm = config.unwrap_from_name('name')
+    assert nm._name == 'name'
+    
+def test_base_config_in_a_tree():
+    "how options are organized into a tree"
+    descr = make_description()
+    config = Config(descr, bool=False)
+    
+    assert config.gc.name == 'ref'
+    config.gc.name = 'framework'
+    assert config.gc.name == 'framework'
+    assert getattr(config, "gc.name") == 'framework'
+
+    assert config.objspace == 'std'
+    config.objspace = 'thunk'
+    assert config.objspace == 'thunk'
+    
+    assert config.gc.float == 2.3
+    assert config.int == 0
+    config.gc.float = 3.4
+    config.int = 123
+    assert config.gc.float == 3.4
+    assert config.int == 123
+
+    assert not config.wantref
+
+    assert config.str == "abc"
+    config.str = "def"
+    assert config.str == "def"
+
+    raises(AttributeError, 'config.gc.foo = "bar"')
+
+    config = Config(descr, bool=False)
+    assert config.gc.name == 'ref'
+    config.wantframework = True
+
+def test_config_values():
+    "_cfgimpl_values appears to be a simple dict"
+    descr = make_description()
+    config = Config(descr, bool=False)
+    config.set(dummy=False)
+    assert config.gc._cfgimpl_values == {'dummy': False, 'float': 2.3, 'name': 'ref'}
+
+def test_cfgimpl_get_home_by_path():
+    descr = make_description()
+    config = Config(descr, bool=False)
+    assert config._cfgimpl_get_home_by_path('gc.dummy')[1] == 'dummy'
+    assert config._cfgimpl_get_home_by_path('dummy')[1] == 'dummy'
+    assert config.getpaths(include_groups=False) == ['gc.name', 'gc.dummy', 'gc.float', 'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
+    assert config.getpaths(include_groups=True) == ['gc', 'gc.name', 'gc.dummy', 'gc.float', 'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']   
diff --git a/test/test_config_api.py b/test/test_config_api.py
new file mode 100644 (file)
index 0000000..9c6578c
--- /dev/null
@@ -0,0 +1,167 @@
+"configuration objects global API"
+import autopath
+from py.test import raises
+
+from config import *
+from option import *
+
+def make_description():
+    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                ['std', 'thunk'], 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
+    wantref_option = BoolOption('wantref', 'Tests', default=False)
+    wantframework_option = BoolOption('wantframework', 'Test', default=False)
+    
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
+    descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption,
+                                           wantref_option, stroption,
+                                           wantframework_option,
+                                           intoption, boolop])
+    return descr
+
+
+def test_compare_configs():
+    "config object comparison"
+    descr = make_description()
+    conf1 = Config(descr)
+    conf2 = Config(descr, wantref=True)
+    assert conf1 != conf2
+    assert hash(conf1) != hash(conf2)
+    assert conf1.getkey() != conf2.getkey()
+    conf1.wantref = True
+    assert conf1 == conf2
+    assert hash(conf1) == hash(conf2)
+    assert conf1.getkey() == conf2.getkey()
+# ____________________________________________________________
+
+def test_iter_config():
+    "iteration on config object"
+    s = StrOption("string", "", default="string")
+    s2 = StrOption("string2", "", default="string2")
+    descr = OptionDescription("options", "", [s,s2])
+    config = Config(descr)
+    assert [(name, value) for name, value in config] == \
+    [('string', 'string'), ('string2', 'string2')]
+
+def test_iter_subconfig():
+    "iteration on config sub object"
+    descr = make_description()
+    conf = Config(descr)
+    for (name, value), (gname, gvalue) in \
+        zip(conf.gc, [("name", "ref"), ("dummy", False)]):
+        assert name == gname
+        assert value == gvalue
+#____________________________________________________________
+def test_getpaths():
+    descr = make_description()
+    config = Config(descr)
+    
+    assert config.getpaths() == ['gc.name', 'gc.dummy', 'gc.float', 'bool',
+                                 'objspace', 'wantref', 'str', 'wantframework',
+                                 'int', 'boolop']
+    assert config.getpaths() == descr.getpaths()
+    assert config.gc.getpaths() == ['name', 'dummy', 'float']
+    assert config.gc.getpaths() == descr.gc.getpaths()
+    assert config.getpaths(include_groups=True) == [
+        'gc', 'gc.name', 'gc.dummy', 'gc.float',
+        'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
+    
+    assert config.getpaths(True) == descr.getpaths(True)
+
+def test_getpaths_with_hidden():
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                ['std', 'thunk'], 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    booloption.hide()
+    intoption = IntOption('int', 'Test int option', default=0)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
+    wantref_option = BoolOption('wantref', 'Test requires', default=False)
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False)
+    
+    descr = OptionDescription('tiramisu', '', [booloption, objspaceoption,
+                                           wantref_option, stroption,
+                                           wantframework_option,
+                                           intoption, boolop])
+    
+    config = Config(descr)
+    result = ['objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
+    assert config.getpaths() == result 
+    r2 = ['bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
+    assert config.getpaths(allpaths=True) == r2
+    
+def test_str():
+    descr = make_description()
+    c = Config(descr)
+    print c # does not crash
+
+def test_dir():
+    descr = make_description()
+    c = Config(descr)
+    print dir(c)
+
+def test_make_dict():
+    "serialization of the whole config to a dict"
+    descr = OptionDescription("opt", "", [
+        OptionDescription("s1", "", [
+            BoolOption("a", "", default=False)]),
+        IntOption("int", "", default=42)])
+    config = Config(descr)
+    d = make_dict(config)
+    assert d == {"s1.a": False, "int": 42}
+    config.int = 43
+    config.s1.a = True
+    d = make_dict(config)
+    assert d == {"s1.a": True, "int": 43}
+    d2 = make_dict(config, flatten=True)
+    assert d2 == {'a': True, 'int': 43}
+
+def test_delattr():
+    "delattr, means suppression of an option in a config"
+    descr = OptionDescription("opt", "", [
+    OptionDescription("s1", "", [
+        BoolOption("a", "", default=False)]),
+    IntOption("int", "", default=42)])
+    c = Config(descr)
+    c.int = 45
+    assert c.int == 45
+    del c.int
+    assert c.int == 42
+    c.int = 45
+    assert c.int == 45
+
+#def test_validator():
+#    "validates the integrity of an option towards a whole configuration"
+#    def my_validator_1(config):
+#        assert config is c
+
+#    def my_validator_2(config):
+#        assert config is c
+#        raise ConflictConfigError
+
+#    descr = OptionDescription("opt", "", [
+#        BoolOption('booloption1', 'option test1', default=False,
+#                   validator=my_validator_1),
+#        BoolOption('booloption2', 'option test2', default=False,
+#                   validator=my_validator_2),
+#        BoolOption('booloption4', 'option test4', default=False,
+#                   ),
+#        ])
+#    c = Config(descr)
+#    c.booloption1 = True
+##    raises(ConfigError, "c.booloption2 = True")
+##    assert c.booloption2 is False
+##    raises(ConfigError, "c.booloption3 = True")
+#    assert c.booloption2 is False
+#    c.booloption4 = True
+#    assert c.booloption2 is False
+#    c.booloption2 = False
+#    assert c.booloption2 is False
+#    
diff --git a/test/test_config_big_example.py b/test/test_config_big_example.py
new file mode 100644 (file)
index 0000000..3ffbd80
--- /dev/null
@@ -0,0 +1,258 @@
+#just a proof of concept with a lot of options and option groups
+import autopath
+from config import *
+from option import *
+
+all_modules = ['amon', 'sphynx', 'zephir']
+
+example__optiondescription = OptionDescription("objspace", "Object Space Options", [
+    ChoiceOption("name", "Object Space name",
+                 ["std", "flow", "thunk", "dump", "taint"],
+                 "std"),
+
+    OptionDescription("opcodes", "opcodes to enable in the interpreter", [
+        BoolOption("CALL_LIKELY_BUILTIN", "emit a special bytecode for likely calls to builtin functions",
+                   default=False,
+                   requires=[("translation.stackless", False)]),
+        BoolOption("CALL_METHOD", "emit a special bytecode for expr.name()",
+                   default=False),
+        ]),
+
+    BoolOption("nofaking", "disallow faking in the object space",
+               default=False,
+               requires=[
+                   ("objspace.usemodules.posix", True),
+                   ("objspace.usemodules.time", True),
+                   ("objspace.usemodules.errno", True)],
+               ),
+
+    OptionDescription("usemodules", "Which Modules should be used", [
+        BoolOption(modname, "use module %s" % (modname, ),
+                   default=True,
+                   requires= ['amon'],
+                   )
+        for modname in all_modules]),
+
+    BoolOption("allworkingmodules", "use as many working modules as possible",
+               default=True,
+               ),
+
+    BoolOption("translationmodules",
+          "use only those modules that are needed to run translate.py on pypy",
+               default=False,
+               ),
+
+    BoolOption("geninterp", "specify whether geninterp should be used",
+               default=True),
+
+    BoolOption("logbytecodes",
+               "keep track of bytecode usage",
+               default=False),
+
+    BoolOption("usepycfiles", "Write and read pyc files when importing",
+               default=True),
+
+    BoolOption("lonepycfiles", "Import pyc files with no matching py file",
+               default=False,
+               requires=[("objspace.usepycfiles", True)]),
+
+    StrOption("soabi",
+              "Tag to differentiate extension modules built for different Python interpreters",
+              default=None),
+
+    BoolOption("honor__builtins__",
+               "Honor the __builtins__ key of a module dictionary",
+               default=False),
+
+    BoolOption("disable_call_speedhacks",
+               "make sure that all calls go through space.call_args",
+               default=False),
+
+    BoolOption("timing",
+               "timing of various parts of the interpreter (simple profiling)",
+               default=False),
+
+    OptionDescription("std", "Standard Object Space Options", [
+        BoolOption("withtproxy", "support transparent proxies",
+                   default=True),
+
+        BoolOption("withsmallint", "use tagged integers",
+                   default=False,
+                   requires=[("objspace.std.withprebuiltint", False),
+                             ("translation.taggedpointers", True)]),
+
+        BoolOption("withprebuiltint", "prebuild commonly used int objects",
+                   default=False),
+
+        IntOption("prebuiltintfrom", "lowest integer which is prebuilt",
+                  default=-5),
+
+        IntOption("prebuiltintto", "highest integer which is prebuilt",
+                  default=100),
+
+        BoolOption("withstrjoin", "use strings optimized for addition",
+                   default=False),
+
+        BoolOption("withstrslice", "use strings optimized for slicing",
+                   default=False),
+
+        BoolOption("withstrbuf", "use strings optimized for addition (ver 2)",
+                   default=False),
+
+        BoolOption("withprebuiltchar",
+                   "use prebuilt single-character string objects",
+                   default=False),
+
+        BoolOption("sharesmallstr",
+                   "always reuse the prebuilt string objects "
+                   "(the empty string and potentially single-char strings)",
+                   default=False),
+
+        BoolOption("withrope", "use ropes as the string implementation",
+                   default=False,
+                   requires=[("objspace.std.withstrslice", False),
+                             ("objspace.std.withstrjoin", False),
+                             ("objspace.std.withstrbuf", False)],
+                   ),
+
+        BoolOption("withropeunicode", "use ropes for the unicode implementation",
+                   default=False,
+                   requires=[("objspace.std.withrope", True)]),
+
+        BoolOption("withcelldict",
+                   "use dictionaries that are optimized for being used as module dicts",
+                   default=False,
+                   requires=[("objspace.opcodes.CALL_LIKELY_BUILTIN", False),
+                             ("objspace.honor__builtins__", False)]),
+
+        BoolOption("withdictmeasurement",
+                   "create huge files with masses of information "
+                   "about dictionaries",
+                   default=False),
+
+        BoolOption("withmapdict",
+                   "make instances really small but slow without the JIT",
+                   default=False,
+                   requires=[("objspace.std.getattributeshortcut", True),
+                             ("objspace.std.withtypeversion", True),
+                       ]),
+
+        BoolOption("withrangelist",
+                   "enable special range list implementation that does not "
+                   "actually create the full list until the resulting "
+                   "list is mutated",
+                   default=False),
+
+        BoolOption("withtypeversion",
+                   "version type objects when changing them",
+                   default=False,
+                   # weakrefs needed, because of get_subclasses()
+                   requires=[("translation.rweakref", True)]),
+
+        BoolOption("withmethodcache",
+                   "try to cache method lookups",
+                   default=False,
+                   requires=[("objspace.std.withtypeversion", True),
+                             ("translation.rweakref", True)]),
+        BoolOption("withmethodcachecounter",
+                   "try to cache methods and provide a counter in __pypy__. "
+                   "for testing purposes only.",
+                   default=False,
+                   requires=[("objspace.std.withmethodcache", True)]),
+        IntOption("methodcachesizeexp",
+                  " 2 ** methodcachesizeexp is the size of the of the method cache ",
+                  default=11),
+        BoolOption("optimized_int_add",
+                   "special case the addition of two integers in BINARY_ADD",
+                   default=False),
+        BoolOption("optimized_comparison_op",
+                   "special case the comparison of integers",
+                   default=False),
+        BoolOption("optimized_list_getitem",
+                   "special case the 'list[integer]' expressions",
+                   default=False),
+        BoolOption("builtinshortcut",
+                   "a shortcut for operations between built-in types",
+                   default=False),
+        BoolOption("getattributeshortcut",
+                   "track types that override __getattribute__",
+                   default=False),
+        BoolOption("newshortcut",
+                   "cache and shortcut calling __new__ from builtin types",
+                   default=False),        
+
+        BoolOption("logspaceoptypes",
+                   "a instrumentation option: before exit, print the types seen by "
+                   "certain simpler bytecodes",
+                   default=False),
+        ChoiceOption("multimethods", "the multimethod implementation to use",
+                     ["doubledispatch", "mrd"],
+                     default="mrd"),
+        BoolOption("immutable_builtintypes",
+                   "Forbid the changing of builtin types", default=True),
+     ]),
+])
+
+# ____________________________________________________________
+
+def get_combined_translation_config(other_optdescr=None,
+                                    existing_config=None,
+                                    overrides=None,
+                                    translating=False):
+    if overrides is None:
+        overrides = {}
+    d = BoolOption("translating",
+                   "indicates whether we are translating currently",
+                   default=False)
+    if other_optdescr is None:
+        children = []
+        newname = ""
+    else:
+        children = [other_optdescr]
+        newname = other_optdescr._name
+    descr = OptionDescription("eole", "all options", children)
+    config = Config(descr, **overrides)
+    if translating:
+        config.translating = True
+    if existing_config is not None:
+        for child in existing_config._cfgimpl_descr._children:
+            if child._name == newname:
+                continue
+            value = getattr(existing_config, child._name)
+            config._cfgimpl_values[child._name] = value
+    return config
+
+def get_example_config(overrides=None, translating=False):
+    return get_combined_translation_config(
+            example__optiondescription, overrides=overrides,
+            translating=translating)
+
+# ____________________________________________________________
+
+def test_example_option():
+    config = get_example_config()
+    result = ['objspace.name', 'objspace.opcodes.CALL_LIKELY_BUILTIN',
+    'objspace.opcodes.CALL_METHOD', 'objspace.nofaking',
+    'objspace.usemodules.amon', 'objspace.usemodules.sphynx',
+    'objspace.usemodules.zephir', 'objspace.allworkingmodules',
+    'objspace.translationmodules', 'objspace.geninterp',
+    'objspace.logbytecodes', 'objspace.usepycfiles', 'objspace.lonepycfiles',
+    'objspace.soabi', 'objspace.honor__builtins__',
+    'objspace.disable_call_speedhacks', 'objspace.timing',
+    'objspace.std.withtproxy', 'objspace.std.withsmallint',
+    'objspace.std.withprebuiltint', 'objspace.std.prebuiltintfrom',
+    'objspace.std.prebuiltintto', 'objspace.std.withstrjoin',
+    'objspace.std.withstrslice', 'objspace.std.withstrbuf',
+    'objspace.std.withprebuiltchar', 'objspace.std.sharesmallstr',
+    'objspace.std.withrope', 'objspace.std.withropeunicode',
+    'objspace.std.withcelldict', 'objspace.std.withdictmeasurement',
+    'objspace.std.withmapdict', 'objspace.std.withrangelist',
+    'objspace.std.withtypeversion', 'objspace.std.withmethodcache',
+    'objspace.std.withmethodcachecounter', 'objspace.std.methodcachesizeexp',
+    'objspace.std.optimized_int_add', 'objspace.std.optimized_comparison_op',
+    'objspace.std.optimized_list_getitem', 'objspace.std.builtinshortcut',
+    'objspace.std.getattributeshortcut', 'objspace.std.newshortcut',
+    'objspace.std.logspaceoptypes', 'objspace.std.multimethods',
+    'objspace.std.immutable_builtintypes']
+
+    assert config.getpaths(allpaths=True) == result 
diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py
new file mode 100644 (file)
index 0000000..d59e378
--- /dev/null
@@ -0,0 +1,207 @@
+import autopath
+from py.test import raises
+
+from config import *
+from option import *
+
+def make_description():
+    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                ['std', 'thunk'], 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
+    wantref_option = BoolOption('wantref', 'Test requires', default=False,
+                                    requires=['boolop'])
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False,
+                                      requires=['boolop'])
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
+    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
+                                           wantref_option, stroption,
+                                           wantframework_option,
+                                           intoption, boolop])
+    return descr
+
+def make_description_duplicates():
+    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    ## dummy 1
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                ['std', 'thunk'], 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
+    wantref_option = BoolOption('wantref', 'Test requires', default=False,
+                                    requires=['boolop'])
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False,
+                                      requires=['boolop'])
+    # dummy2 (same name)
+    gcdummy2  = BoolOption('dummy', 'dummy2', default=True)
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, gcdummy2, floatoption])
+    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
+                                           wantref_option, stroption,
+                                           wantframework_option,
+                                           intoption, boolop])
+    return descr
+
+def test_identical_names():
+    """If in the schema (the option description) there is something that 
+    have the same name, an exection is raised
+    """    
+    descr = make_description_duplicates()
+    raises(ConflictConfigError, "cfg = Config(descr)")
+
+def make_description2():
+    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                ['std', 'thunk'], 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    # first multi
+    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
+    boolop.enable_multi()
+    wantref_option = BoolOption('wantref', 'Test requires', default=False,
+                                    requires=['boolop'])
+    # second multi
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False,
+                                      requires=['boolop'])
+    wantframework_option.enable_multi()
+    
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
+    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
+                                           wantref_option, stroption,
+                                           wantframework_option,
+                                           intoption, boolop])
+    return descr
+
+# FIXME: XXX would you mind putting the multi validations anywhere else 
+# than in the requires !!! 
+#def test_multi_constraints():
+#    "a multi in a constraint has to have the same length"
+#    descr = make_description2()
+#    cfg = Config(descr)
+#    cfg.boolop = [True, True, False]
+#    cfg.wantframework = [False, False, True]
+#    
+#def test_multi_raise():
+#    "a multi in a constraint has to have the same length"
+#    # FIXME fusionner les deux tests, MAIS PROBLEME :
+#    # il ne devrait pas etre necessaire de refaire une config
+#    # si la valeur est modifiee une deuxieme fois -> 
+#    #raises(ConflictConfigError, "cfg.wantframework = [False, False, True]")
+#    #   ExceptionFailure: 'DID NOT RAISE'
+#    descr = make_description2()
+#    cfg = Config(descr)
+#    cfg.boolop = [True]
+#    raises(ConflictConfigError, "cfg.wantframework = [False, False, True]")
+# ____________________________________________________________
+# adding dynamically new options description schema
+def test_newoption_add_in_descr():
+    descr = make_description()
+    newoption = BoolOption('newoption', 'dummy twoo', default=False)
+    descr.add_child(newoption)
+    config = Config(descr)
+    assert config.newoption == False
+
+def test_newoption_add_in_subdescr():
+    descr = make_description()
+    newoption = BoolOption('newoption', 'dummy twoo', default=False)
+    descr.gc.add_child(newoption)
+    config = Config(descr, bool=False)
+    assert config.gc.newoption == False
+
+def test_newoption_add_in_config():
+    descr = make_description()
+    config = Config(descr, bool=False)
+    newoption = BoolOption('newoption', 'dummy twoo', default=False)
+    descr.add_child(newoption)
+    config.cfgimpl_update()
+    assert config.newoption == False
+# ____________________________________________________________
+def make_description_requires():
+    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                ['std', 'thunk'], 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    stroption = StrOption('str', 'Test string option', default="abc", 
+                          requires=[('int', 1, 'hide')])
+    
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
+    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
+                                           stroption, intoption])
+    return descr
+
+def test_hidden_if_in():
+    descr = make_description_requires()
+    cfg = Config(descr)
+    intoption = cfg.unwrap_from_path('int')
+    stroption = cfg.unwrap_from_path('str')
+    assert not stroption._is_hidden()
+    cfg.int = 1
+    raises(HiddenOptionError, "cfg.str")
+    raises(HiddenOptionError, 'cfg.str= "uvw"')
+    assert stroption._is_hidden()
+
+def test_hidden_if_in_with_group():
+    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                ['std', 'thunk'], 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption], 
+                                                  requires=[('int', 1, 'hide')])
+    descr = OptionDescription('constraints', '', [gcgroup, booloption, 
+                                          objspaceoption, stroption, intoption])
+    cfg = Config(descr)
+    assert not gcgroup._is_hidden()
+    cfg.int = 1
+    raises(HiddenOptionError, "cfg.gc.name")
+#    raises(HiddenOptionError, 'cfg.gc= "uvw"')
+    assert gcgroup._is_hidden()
+
+def test_disabled_with_group():
+    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                ['std', 'thunk'], 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption], 
+                                                  requires=[('int', 1, 'disable')])
+    descr = OptionDescription('constraints', '', [gcgroup, booloption, 
+                                          objspaceoption, stroption, intoption])
+    cfg = Config(descr)
+    assert not gcgroup._is_disabled()
+    cfg.int = 1
+    raises(DisabledOptionError, "cfg.gc.name")
+#    raises(HiddenOptionError, 'cfg.gc= "uvw"')
+    assert gcgroup._is_disabled()
+
+    
diff --git a/test/test_option_default.py b/test/test_option_default.py
new file mode 100644 (file)
index 0000000..3d7baf7
--- /dev/null
@@ -0,0 +1,121 @@
+"test all types of option default values for options, add new option in a descr"
+import autopath
+
+from py.test import raises
+from config import *
+from option import *
+from error import MandatoryError
+
+def make_description():
+    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                ['std', 'thunk'], 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
+    wantref_option = BoolOption('wantref', 'Test requires', default=False,
+                                    requires=['boolop'])
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False,
+                                      requires=['boolop'])
+    
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
+    descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption,
+                                           wantref_option, stroption,
+                                           wantframework_option,
+                                           intoption, boolop])
+    return descr
+
+#____________________________________________________________
+# default values
+def test_default_is_none():
+    """
+    Most constructors take a ``default`` argument that specifies the default
+    value of the option. If this argument is not supplied the default value is
+    assumed to be ``None``.
+    """
+    dummy1 = BoolOption('dummy1', 'doc dummy')
+    dummy2 = BoolOption('dummy2', 'doc dummy')
+    group = OptionDescription('group', '', [dummy1, dummy2])
+    config = Config(group)
+    # so when the default value is not set, there is actually a default value
+    assert config.dummy1 == None
+    assert config.dummy2 == None
+
+def test_set_defaut_value_from_option_object():
+    """Options have an available default setting and can give it back"""
+    b = BoolOption("boolean", "", default=False)
+    assert b.getdefault() == False
+
+def test_mandatory():
+    dummy1 = BoolOption('dummy1', 'doc dummy', mandatory=True)
+    dummy2 = BoolOption('dummy2', 'doc dummy', mandatory=True)
+    group = OptionDescription('group', '', [dummy1, dummy2])
+    config = Config(group)
+#    config.setoption('dummy1', True)
+    raises(MandatoryError, 'config.dummy1')
+    config.dummy1 = True
+    assert config.dummy1 == True
+    raises(MandatoryError, 'config.dummy2 == None')
+    raises(MandatoryError, "config.override({'dummy2':None})")
+    config.set(dummy2=True)    
+    config.dummy2 = False
+    assert config.dummy2 == False
+
+def test_override_are_defaults():
+    descr = make_description()
+    config = Config(descr, bool=False)
+
+    config.gc.dummy = True
+    assert config._cfgimpl_values['gc']._cfgimpl_value_owners['dummy'] == 'user'
+    #Options have an available default setting and can give it back
+    assert config._cfgimpl_descr._children[0]._children[1].getdefault() == False
+    config.override({'gc.dummy':True})
+    #assert config.gc.dummy == True
+    #assert config._cfgimpl_descr._children[0]._children[1].getdefault() == True
+    #assert config._cfgimpl_values['gc']._cfgimpl_value_owners['dummy'] == 'default'
+
+def test_overrides_changes_option_value():
+    "with config.override(), the default is changed and the value is changed"
+    descr = OptionDescription("test", "", [
+        BoolOption("b", "", default=False)])
+    config = Config(descr)
+    config.b = True
+    config.override({'b': False})
+    assert config.b == False
+#____________________________________________________________
+# test various option types
+def test_choice_with_no_default():
+    descr = OptionDescription("test", "", [
+        ChoiceOption("backend", "", ["c", "cli"])])
+    config = Config(descr)
+    assert config.backend is None
+    config.backend = "c"
+
+def test_choice_with_default():
+    descr = OptionDescription("test", "", [
+        ChoiceOption("backend", "", ["c", "cli"], default="cli")])
+    config = Config(descr)
+    assert config.backend == "cli"
+        
+def test_arbitrary_option():
+    descr = OptionDescription("top", "", [
+        ArbitraryOption("a", "no help", default=None)
+    ])
+    config = Config(descr)
+    config.a = []
+    config.a.append(1)
+    assert config.a == [1]
+
+    descr = OptionDescription("top", "", [
+        ArbitraryOption("a", "no help", defaultfactory=list)
+    ])
+    c1 = Config(descr)
+    c2 = Config(descr)
+    c1.a.append(1)
+    assert c2.a == []
+    assert c1.a == [1]
+
diff --git a/test/test_option_owner.py b/test/test_option_owner.py
new file mode 100644 (file)
index 0000000..3043353
--- /dev/null
@@ -0,0 +1,109 @@
+import autopath
+
+from py.test import raises
+from config import *
+from option import *
+from error import SpecialOwnersError
+
+def make_description():
+    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    ## dummy 1
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                ['std', 'thunk'], 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
+    wantref_option = BoolOption('wantref', 'Test requires', default=False,
+                                    requires=['boolop'])
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False,
+                                      requires=['boolop'])
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
+    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
+                                           wantref_option, stroption,
+                                           wantframework_option,
+                                           intoption, boolop])
+    return descr
+
+def test_override_are_default_owner():
+    "config.override() implies that the owner is 'default' again"
+    descr = make_description()
+    config = Config(descr, bool=False)
+    # defaut
+    assert config.gc._cfgimpl_value_owners['dummy'] == 'default'
+    # user
+    config.gc.dummy = True
+    assert config.gc._cfgimpl_value_owners['dummy'] == 'user'
+    assert config._cfgimpl_values['gc']._cfgimpl_value_owners['dummy'] == 'user'
+    #Options have an available default setting and can give it back
+    assert config._cfgimpl_descr._children[0]._children[1].getdefault() == False
+    config.override({'gc.dummy':True})
+    assert config.gc._cfgimpl_value_owners['dummy'] == 'default'
+    # user again
+    config.gc.dummy = False
+    assert config.gc._cfgimpl_value_owners['dummy'] == 'user'
+
+def test_change_owner():
+    descr = make_description()
+    # here the owner is 'default'
+    config = Config(descr, bool=False)
+    # the default owner is 'user' (which is not 'default')
+    # Still not getting it ? read the docs
+    config.gc.dummy = True
+    assert config.gc._cfgimpl_value_owners['dummy'] == 'user'
+    config.cfgimpl_set_owner('eggs')
+    config.set(dummy=False)
+    assert config.gc._cfgimpl_value_owners['dummy'] == 'eggs'
+    config.cfgimpl_set_owner('spam')
+    gcdummy = config.unwrap_from_path('gc.dummy')
+    gcdummy.setowner(config.gc, 'blabla')
+    assert config.gc._cfgimpl_value_owners['dummy'] == 'blabla'
+    config.gc.dummy = True
+    assert config.gc._cfgimpl_value_owners['dummy'] == 'spam'
+
+#____________________________________________________________
+# special owners
+def test_auto_owner():
+    descr = make_description()
+    config = Config(descr, bool=False)
+    config.gc.setoption('dummy', True, 'auto')
+    raises(HiddenOptionError, "config.gc.dummy")
+    raises(ConflictConfigError, "config.gc.setoption('dummy', False, 'auto')")
+    # shall return an auto value...
+    #assert config.gc.dummy == 'auto_dummy_value'
+
+def test_cannot_override_special_owners():
+    descr = make_description()
+    config = Config(descr, bool=False)
+    config.gc.setoption('dummy', True, 'auto')
+    raises(SpecialOwnersError, "config.override({'gc.dummy': True})")
+
+def test_fill_owner():    
+    "fill option"
+    descr = make_description()
+    config = Config(descr, bool=False)
+    assert config.bool == False
+    assert config.gc.dummy == False
+    # 'fill' special values
+    config.gc.setoption('dummy', True, 'fill')
+    assert config.gc.dummy == False
+
+def test_auto_fill_and_override():    
+    descr = make_description()
+    config = Config(descr, bool=False)
+    booloption = config.unwrap_from_path('bool')
+    booloption.callback = 'identical'
+    booloption.setowner(config, 'auto')
+    assert config.bool == 'identicalbool'
+    gcdummy = config.unwrap_from_path('gc.dummy')
+    gcdummy.callback = 'identical'
+    gcdummy.setowner(config.gc, 'fill')
+    raises(SpecialOwnersError, "config.override({'gc.dummy':True})")
+    config.gc.setoption('dummy', False, 'fill')
+    # value is returned
+    assert config.gc.dummy == False
+
+
diff --git a/test/test_option_setting.py b/test/test_option_setting.py
new file mode 100644 (file)
index 0000000..dad5e20
--- /dev/null
@@ -0,0 +1,356 @@
+"config.set() or config.setoption() or option.setoption()"
+import autopath
+from py.test import raises
+
+from config import *
+from option import *
+from error import *
+
+def make_description():
+    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                ['std', 'thunk'], 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
+    wantref_option = BoolOption('wantref', 'Test requires', default=False)
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False)    
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
+    descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption,
+                                           wantref_option, stroption,
+                                           wantframework_option,
+                                           intoption, boolop])
+    return descr
+
+#____________________________________________________________
+# change with __setattr__
+def test_attribute_access():
+    "Once set, option values can't be changed again by attribute access"
+    s = StrOption("string", "", default="string")
+    descr = OptionDescription("options", "", [s])
+    config = Config(descr)
+    # let's try to change it again
+    config.string = "foo"
+    assert config.string == "foo"
+#    raises(ConflictConfigError, 'config.string = "bar"')
+
+def test_idontexist():
+    descr = make_description()
+    cfg = Config(descr)
+    raises(AttributeError, "cfg.idontexist")
+# ____________________________________________________________
+def test_attribute_access_with_multi():
+    s = StrOption("string", "", default="string", multi=True)
+    descr = OptionDescription("options", "", [s])
+    config = Config(descr)
+    config.string = ["foo", "bar"]
+    assert config.string == ["foo", "bar"]
+
+def test_attribute_access_with_multi():
+    s = StrOption("string", "", default="string", multi=True)
+    descr = OptionDescription("options", "", [s])
+    config = Config(descr)
+    config.string = ["foo", "bar"]
+    assert config.string == ["foo", "bar"]
+
+def test_multi_with_requires():
+    s = StrOption("string", "", default="string", multi=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    stroption = StrOption('str', 'Test string option', default="abc", 
+                          requires=[('int', 1, 'hide')], multi=True)
+    descr = OptionDescription("options", "", [s, intoption, stroption])
+    config = Config(descr)
+    assert stroption._is_hidden() == False
+    config.int = 1
+    raises(HiddenOptionError, "config.str = ['a', 'b']")
+    assert stroption._is_hidden()
+
+def test__requires_with_inverted():
+    s = StrOption("string", "", default="string", multi=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    stroption = StrOption('str', 'Test string option', default="abc", 
+                          requires=[('int', 1, 'hide', 'inverted')], multi=True)
+    descr = OptionDescription("options", "", [s, intoption, stroption])
+    config = Config(descr)
+    assert stroption._is_hidden() == False
+    config.int = 1
+    assert stroption._is_hidden() == False
+
+def test_multi_with_requires_in_another_group():
+    s = StrOption("string", "", default="string", multi=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    descr = OptionDescription("options", "", [intoption])
+    stroption = StrOption('str', 'Test string option', default="abc", 
+                          requires=[('int', 1, 'hide')], multi=True)
+    descr = OptionDescription("opt", "", [stroption])
+    descr2 = OptionDescription("opt2", "", [intoption, s, descr])
+    config = Config(descr2)
+    assert stroption._is_hidden() == False
+    config.int = 1
+    raises(HiddenOptionError,  "config.opt.str = ['a', 'b']")
+    assert stroption._is_hidden()
+
+def test_apply_requires_from_config():
+    s = StrOption("string", "", default="string", multi=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    descr = OptionDescription("options", "", [intoption])
+    stroption = StrOption('str', 'Test string option', default="abc", 
+                          requires=[('int', 1, 'hide')], multi=True)
+    descr = OptionDescription("opt", "", [stroption])
+    descr2 = OptionDescription("opt2", "", [intoption, s, descr])
+    config = Config(descr2)
+    assert stroption._is_hidden() == False
+    config.int = 1
+    try:
+        config.opt.str
+    except:
+        pass 
+    assert stroption._is_hidden()
+    
+
+def test_apply_requires_with_disabled():
+    s = StrOption("string", "", default="string", multi=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    descr = OptionDescription("options", "", [intoption])
+    stroption = StrOption('str', 'Test string option', default="abc", 
+                          requires=[('int', 1, 'disable')], multi=True)
+    descr = OptionDescription("opt", "", [stroption])
+    descr2 = OptionDescription("opt2", "", [intoption, s, descr])
+    config = Config(descr2)
+    assert stroption._is_disabled() == False
+    config.int = 1
+    try:
+        config.opt.str
+    except:
+        pass 
+    assert stroption._is_disabled()
+
+def test_multi_with_requires_with_disabled_in_another_group():
+    s = StrOption("string", "", default="string", multi=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    descr = OptionDescription("options", "", [intoption])
+    stroption = StrOption('str', 'Test string option', default="abc", 
+                          requires=[('int', 1, 'disable')], multi=True)
+    descr = OptionDescription("opt", "", [stroption])
+    descr2 = OptionDescription("opt2", "", [intoption, s, descr])
+    config = Config(descr2)
+    assert stroption._is_disabled() == False
+    config.int = 1
+    raises(DisabledOptionError,  "config.opt.str = ['a', 'b']")
+    assert stroption._is_disabled()
+
+def test_multi_with_requires_that_is_multi():
+    s = StrOption("string", "", default="string", multi=True)
+    intoption = IntOption('int', 'Test int option', default=[0, 0], multi=True)
+    stroption = StrOption('str', 'Test string option', default="abc", 
+                          requires=[('int', [1, 1], 'hide')], multi=True)
+    descr = OptionDescription("options", "", [s, intoption, stroption])
+    config = Config(descr)
+    assert stroption._is_hidden() == False
+    config.int = [1, 1]
+    raises(HiddenOptionError, "config.str = ['a', 'b']")
+    assert stroption._is_hidden()
+
+def test_multi_with_bool():
+    s = BoolOption("bool", "", default=[False], multi=True)
+    descr = OptionDescription("options", "", [s])
+    config = Config(descr)
+    assert descr.bool.multi == True
+    config.bool = [True, False]
+    assert config._cfgimpl_values['bool'] == [True, False]
+    assert config.bool == [True, False]
+
+def test_multi_with_bool_two():
+    s = BoolOption("bool", "", default=[False], multi=True)
+    descr = OptionDescription("options", "", [s])
+    config = Config(descr)
+    assert descr.bool.multi == True
+    raises(ConfigError, "config.bool = True")
+
+def test_choice_access_with_multi():
+    ch = ChoiceOption("t1", "", ["a", "b"], default=["a", "a", "a"], multi=True)
+    descr = OptionDescription("options", "", [ch])
+    config = Config(descr)
+    config.t1 = ["a", "b", "a", "b"]
+    assert config.t1 == ["a", "b", "a", "b"]
+# ____________________________________________________________
+
+def test_setoption_from_option():
+    "a setoption directly from the option is **not** a good practice"
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    descr = OptionDescription('descr', '', [booloption])
+    cfg = Config(descr)
+    booloption.setoption(cfg, False, 'owner')
+    assert cfg.bool == False
+# ____________________________________________________________
+def test_set_mode_in_config():
+    booloption = BoolOption('bool', 'Test boolean option', default=True, 
+                                                                  mode='expert')
+    descr = OptionDescription('descr', '', [booloption])
+    cfg = Config(descr)
+    cfg.cfgimpl_set_mode('expert')
+    raises(ModeOptionError, "cfg.bool")
+    cfg.cfgimpl_set_mode('normal')
+    assert cfg.bool == True
+#____________________________________________________________
+def test_dwim_set():
+    descr = OptionDescription("opt", "", [
+        OptionDescription("sub", "", [
+            BoolOption("b1", ""),
+            ChoiceOption("c1", "", ['a', 'b', 'c'], 'a'),
+            BoolOption("d1", ""),
+        ]),
+        BoolOption("b2", ""),
+        BoolOption("d1", ""),
+    ])
+    c = Config(descr)
+    c.set(b1=False, c1='b')
+    assert not c.sub.b1
+    assert c.sub.c1 == 'b'
+    # new config, because you cannot change values once they are set
+    c = Config(descr)
+    c.set(b2=False, **{'sub.c1': 'c'})
+    assert not c.b2
+    assert c.sub.c1 == 'c'
+    raises(AmbigousOptionError, "c.set(d1=True)")
+    raises(NoMatchingOptionFound, "c.set(unknown='foo')")
+
+def test_more_set():
+    descr = OptionDescription("opt", "", [
+        OptionDescription("s1", "", [
+            BoolOption("a", "", default=False)]),
+        IntOption("int", "", default=42)])
+    d = {'s1.a': True, 'int': 23}
+    config = Config(descr)
+    config.set(**d)
+    assert config.s1.a
+    assert config.int == 23
+
+def test_set_with_hidden_option():
+    boolopt = BoolOption("a", "", default=False)
+    boolopt.hide()
+    descr = OptionDescription("opt", "", [
+        OptionDescription("s1", "", [boolopt]),
+        IntOption("int", "", default=42)])
+    d = {'s1.a': True, 'int': 23}
+    config = Config(descr)
+    raises(HiddenOptionError, "config.set(**d)")
+
+def test_set_with_unknown_option():
+    boolopt = BoolOption("b", "", default=False)
+    descr = OptionDescription("opt", "", [
+        OptionDescription("s1", "", [boolopt]),
+        IntOption("int", "", default=42)])
+    d = {'s1.a': True, 'int': 23}
+    config = Config(descr)
+    raises(NoMatchingOptionFound, "config.set(**d)")
+
+
+def test_set_symlink_option():
+    boolopt = BoolOption("b", "", default=False)
+    linkopt = SymLinkOption("c", "s1.b")
+    descr = OptionDescription("opt", "", 
+                              [linkopt, OptionDescription("s1", "", [boolopt])])
+    config = Config(descr)
+    setattr(config, "s1.b", True)
+    setattr(config, "s1.b", False)
+    assert config.s1.b == False
+    assert config.c == False
+    config.c = True
+    assert config.s1.b == True
+    assert config.c == True
+    config.c = False
+    assert config.s1.b == False
+    assert config.c == False
+    
+#____________________________________________________________
+def test_config_impl_values():
+    descr = make_description()
+    config = Config(descr, bool=False)
+#    gcdummy.setoption(config, True, "user")
+#    config.setoption("gc.dummy", True, "user")
+    #config.gc.dummy = True
+#    config.setoption("bool", False, "user")
+    config.set(dummy=False)
+    assert config.gc._cfgimpl_values == {'dummy': False, 'float': 2.3, 'name': 'ref'}
+    ## acces to the option object 
+#    config.gc._cfgimpl_descr.dummy.setoption(config, True, "user")
+    assert config.gc.dummy == False
+#    config.set(dummy=True)
+#    assert config.gc.dummy == True
+
+#____________________________________________________________
+def test_accepts_multiple_changes_from_option():
+    s = StrOption("string", "", default="string")
+    descr = OptionDescription("options", "", [s])
+    config = Config(descr)
+    config.string = "egg"
+    assert s.getdefault() == "string"
+    assert config.string == "egg"
+    s.setoption(config, 'blah', "default")
+    assert s.getdefault() == "blah"
+    assert config.string == "blah"
+    s.setoption(config, 'bol', "user")
+    assert config.string == 'bol'
+    config.override({'string': "blurp"})
+    assert config.string == 'blurp'
+    assert s.getdefault() == 'blurp'
+
+def test_allow_multiple_changes_from_config():
+    """
+    a `setoption` from the config object is much like the attribute access, 
+    except the fact that value owner can bet set 
+    """
+    s = StrOption("string", "", default="string")
+    s2 = StrOption("string2", "", default="string")
+    suboption = OptionDescription("bip", "", [s2])
+    descr = OptionDescription("options", "", [s, suboption])
+    config = Config(descr)
+    config.setoption("string", 'blah', "user")
+    config.setoption("string", "oh", "user")    
+    assert config.string == "oh"
+    config.set(string2= 'blah')
+    assert config.bip.string2 == 'blah'
+# ____________________________________________________________
+
+def test_overrides_are_defaults():
+    descr = OptionDescription("test", "", [
+        BoolOption("b1", "", default=False),
+        BoolOption("b2", "", default=False),
+        ])
+    # overrides here
+    config = Config(descr, b2=True)
+    assert config.b2
+    # test with a require
+    config.b1 = True
+    assert config.b2
+ # ____________________________________________________________
+ # accessing a value by the get method
+def test_access_by_get():
+    descr = make_description()
+    cfg = Config(descr)
+    raises(NotFoundError, "cfg.get('idontexist')" )
+    assert cfg.get('wantref') == False
+    assert cfg.gc.dummy == False
+    assert cfg.get('dummy') == False
+
+def test_access_by_get_whith_hide():
+    b1 = BoolOption("b1", "")
+    b1.hide()
+    descr = OptionDescription("opt", "", [
+    OptionDescription("sub", "", [
+        b1,
+        ChoiceOption("c1", "", ['a', 'b', 'c'], 'a'),
+        BoolOption("d1", ""),
+    ]),
+    BoolOption("b2", ""),
+    BoolOption("d1", ""),
+    ])
+    c = Config(descr)
+    raises(HiddenOptionError, "c.get('b1')")
+    
diff --git a/test/test_option_type.py b/test/test_option_type.py
new file mode 100644 (file)
index 0000000..5421ca4
--- /dev/null
@@ -0,0 +1,141 @@
+# coding: utf-8
+"frozen and hidden values" 
+import autopath
+from py.test import raises
+
+from config import *
+from option import *
+
+def make_description():
+    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcoption.set_mode("expert")
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+    # hidding dummy here
+    gcdummy.hide()
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                ['std', 'thunk'], 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    stroption = StrOption('str', 'Test string option', default="abc")
+
+    wantref_option = BoolOption('wantref', 'Test requires', default=False,
+                                    requires=[('gc.name', 'ref')])
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False,
+                                      requires=[('gc.name', 'framework')])
+    
+    # ____________________________________________________________
+    booloptiontwo = BoolOption('booltwo', 'Test boolean option two', default=False)
+    subgroup = OptionDescription('subgroup', '', [booloptiontwo])
+    # ____________________________________________________________
+    
+    gcgroup = OptionDescription('gc', '', [subgroup, gcoption, gcdummy, floatoption])
+    gcgroup.set_mode("expert")
+    descr = OptionDescription('trs', '', [gcgroup, booloption, objspaceoption,
+                                           wantref_option, stroption,
+                                           wantframework_option,
+                                           intoption])
+    return descr
+#____________________________________________________________
+#freeze
+def make_description_freeze():
+    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                ['std', 'thunk'], 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
+    wantref_option = BoolOption('wantref', 'Test requires', default=False,
+                                    requires=['boolop'])
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False,
+                                      requires=['boolop'])
+    
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
+    descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption,
+                                           wantref_option, stroption,
+                                           wantframework_option,
+                                           intoption, boolop])
+    return descr
+
+def test_freeze_one_option():
+    "freeze an option "
+    descr = make_description_freeze()
+    conf = Config(descr)
+    #freeze only one option
+    conf.gc._cfgimpl_descr.dummy.freeze()
+    assert conf.gc.dummy == False
+    raises(TypeError, "conf.gc.dummy = True")
+
+def test_frozen_value():
+    "setattr a frozen value at the config level"
+    s = StrOption("string", "", default="string")
+    descr = OptionDescription("options", "", [s])
+    config = Config(descr)
+    s.freeze()
+    raises(ConfigError, 'config.string = "egg"')
+
+def test_freeze():
+    "freeze a whole configuration object"
+    descr = make_description()
+    conf = Config(descr)
+    conf.cfgimpl_freeze()
+    raises(ConfigError, "conf.gc.name = 'try to modify'")
+# ____________________________________________________________
+def test_is_hidden():
+    descr = make_description()
+    config = Config(descr)
+    assert config.gc._cfgimpl_descr.dummy._is_hidden() == True
+    # setattr
+    raises(HiddenOptionError, "config.gc.dummy == False")
+    # getattr
+    raises(HiddenOptionError, "config.gc.dummy")
+    # I want to access to this option anyway
+    path = 'gc.dummy'
+    homeconfig, name = config._cfgimpl_get_home_by_path(path)
+    assert homeconfig._cfgimpl_values[name] == False
+
+def test_group_is_hidden():
+    descr = make_description()
+    config = Config(descr)
+    gc = config.unwrap_from_path('gc')
+    gc.hide()
+    dummy = config.unwrap_from_path('gc.dummy')
+    raises(HiddenOptionError, "config.gc.dummy")
+    assert gc._is_hidden()
+    raises(HiddenOptionError, "config.gc.float")